commit
						18476d9624
					
				@ -17,7 +17,15 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "plugins": ["@typescript-eslint"],
 | 
					  "plugins": ["@typescript-eslint"],
 | 
				
			||||||
  "rules": {
 | 
					  "rules": {
 | 
				
			||||||
    "@typescript-eslint/ban-ts-comment": ["warn", "allow-with-description"],
 | 
					    "@typescript-eslint/ban-ts-comment": [
 | 
				
			||||||
 | 
					      "warn",
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "ts-expect-error": "allow-with-description",
 | 
				
			||||||
 | 
					        "ts-ignore": "allow-with-description",
 | 
				
			||||||
 | 
					        "ts-nocheck": "allow-with-description",
 | 
				
			||||||
 | 
					        "ts-check": "allow-with-description"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
    "@typescript-eslint/no-unused-vars": "off",
 | 
					    "@typescript-eslint/no-unused-vars": "off",
 | 
				
			||||||
    "@typescript-eslint/no-explicit-any": ["off", { "ignoreRestArgs": true }],
 | 
					    "@typescript-eslint/no-explicit-any": ["off", { "ignoreRestArgs": true }],
 | 
				
			||||||
    "@typescript-eslint/no-non-null-assertion": "off"
 | 
					    "@typescript-eslint/no-non-null-assertion": "off"
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@
 | 
				
			|||||||
    "assets": ["build/*.xpi"]
 | 
					    "assets": ["build/*.xpi"]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "hooks": {
 | 
					  "hooks": {
 | 
				
			||||||
 | 
					    "before:init": "npm run lint",
 | 
				
			||||||
    "after:bump": "npm run build",
 | 
					    "after:bump": "npm run build",
 | 
				
			||||||
    "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
 | 
					    "after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -143,7 +143,7 @@ This is also how your plugin will be released and used by others.
 | 
				
			|||||||
<details >
 | 
					<details >
 | 
				
			||||||
<summary>💡 Start with GitHub Codespace</summary>
 | 
					<summary>💡 Start with GitHub Codespace</summary>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*GitHub CodeSpace* enables you getting started without the need to download code/IDE/dependencies locally.
 | 
					_GitHub CodeSpace_ enables you getting started without the need to download code/IDE/dependencies locally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Replace the steps above and build you first plugin in 30 seconds!
 | 
					Replace the steps above and build you first plugin in 30 seconds!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								addon/bootstrap.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								addon/bootstrap.js
									
									
									
									
										vendored
									
									
								
							@ -51,7 +51,7 @@ async function waitForZotero() {
 | 
				
			|||||||
                resolve();
 | 
					                resolve();
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            false
 | 
					            false,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
@ -92,7 +92,7 @@ async function startup({ id, version, resourceURI, rootURI }, reason) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Services.scriptloader.loadSubScript(
 | 
					  Services.scriptloader.loadSubScript(
 | 
				
			||||||
    `${rootURI}/chrome/content/scripts/index.js`,
 | 
					    `${rootURI}/chrome/content/scripts/index.js`,
 | 
				
			||||||
    ctx
 | 
					    ctx,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -102,7 +102,7 @@ function shutdown({ id, version, resourceURI, rootURI }, reason) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  if (typeof Zotero === "undefined") {
 | 
					  if (typeof Zotero === "undefined") {
 | 
				
			||||||
    Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
 | 
					    Zotero = Components.classes["@zotero.org/Zotero;1"].getService(
 | 
				
			||||||
      Components.interfaces.nsISupports
 | 
					      Components.interfaces.nsISupports,
 | 
				
			||||||
    ).wrappedJSObject;
 | 
					    ).wrappedJSObject;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  Zotero.__addonInstance__.hooks.onShutdown();
 | 
					  Zotero.__addonInstance__.hooks.onShutdown();
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								package.json
									
									
									
									
									
								
							@ -41,23 +41,23 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "homepage": "https://github.com/windingwind/zotero-addon-template#readme",
 | 
					  "homepage": "https://github.com/windingwind/zotero-addon-template#readme",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "zotero-plugin-toolkit": "^2.1.3"
 | 
					    "zotero-plugin-toolkit": "^2.1.5"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@types/node": "^20.1.1",
 | 
					    "@types/node": "^20.4.2",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^6.0.0",
 | 
					    "@typescript-eslint/eslint-plugin": "^6.0.0",
 | 
				
			||||||
    "@typescript-eslint/parser": "^6.0.0",
 | 
					    "@typescript-eslint/parser": "^6.0.0",
 | 
				
			||||||
    "chokidar-cli": "^3.0.0",
 | 
					    "chokidar-cli": "^3.0.0",
 | 
				
			||||||
    "compressing": "^1.9.0",
 | 
					    "compressing": "^1.9.0",
 | 
				
			||||||
    "concurrently": "^8.0.1",
 | 
					    "concurrently": "^8.2.0",
 | 
				
			||||||
    "cross-env": "^7.0.3",
 | 
					    "cross-env": "^7.0.3",
 | 
				
			||||||
    "esbuild": "^0.18.1",
 | 
					    "esbuild": "^0.18.12",
 | 
				
			||||||
    "eslint": "^8.40.0",
 | 
					    "eslint": "^8.44.0",
 | 
				
			||||||
    "eslint-config-prettier": "^8.8.0",
 | 
					    "eslint-config-prettier": "^8.8.0",
 | 
				
			||||||
    "prettier": "3.0.0",
 | 
					    "prettier": "^3.0.0",
 | 
				
			||||||
    "release-it": "^16.1.0",
 | 
					    "release-it": "^16.1.0",
 | 
				
			||||||
    "replace-in-file": "^7.0.1",
 | 
					    "replace-in-file": "^7.0.1",
 | 
				
			||||||
    "typescript": "^5.0.4",
 | 
					    "typescript": "^5.1.6",
 | 
				
			||||||
    "zotero-types": "^1.0.14"
 | 
					    "zotero-types": "^1.0.16"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -81,7 +81,7 @@ function dateFormat(fmt, date) {
 | 
				
			|||||||
    if (ret) {
 | 
					    if (ret) {
 | 
				
			||||||
      fmt = fmt.replace(
 | 
					      fmt = fmt.replace(
 | 
				
			||||||
        ret[1],
 | 
					        ret[1],
 | 
				
			||||||
        ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
 | 
					        ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0"),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -106,7 +106,7 @@ function renameLocaleFiles() {
 | 
				
			|||||||
      if (localeSubFile.endsWith(".ftl")) {
 | 
					      if (localeSubFile.endsWith(".ftl")) {
 | 
				
			||||||
        renameSync(
 | 
					        renameSync(
 | 
				
			||||||
          path.join(localeSubDir, localeSubFile),
 | 
					          path.join(localeSubDir, localeSubFile),
 | 
				
			||||||
          path.join(localeSubDir, `${config.addonRef}-${localeSubFile}`)
 | 
					          path.join(localeSubDir, `${config.addonRef}-${localeSubFile}`),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -124,7 +124,7 @@ function replaceString() {
 | 
				
			|||||||
  const replaceTo = [author, description, homepage, version, buildTime];
 | 
					  const replaceTo = [author, description, homepage, version, buildTime];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  replaceFrom.push(
 | 
					  replaceFrom.push(
 | 
				
			||||||
    ...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g"))
 | 
					    ...Object.keys(config).map((k) => new RegExp(`__${k}__`, "g")),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  replaceTo.push(...Object.values(config));
 | 
					  replaceTo.push(...Object.values(config));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -154,7 +154,7 @@ function replaceString() {
 | 
				
			|||||||
      const prefixedLines = lines.map((line) => {
 | 
					      const prefixedLines = lines.map((line) => {
 | 
				
			||||||
        // https://regex101.com/r/lQ9x5p/1
 | 
					        // https://regex101.com/r/lQ9x5p/1
 | 
				
			||||||
        const match = line.match(
 | 
					        const match = line.match(
 | 
				
			||||||
          /^(?<message>[a-zA-Z]\S*)([ ]*=[ ]*)(?<pattern>.*)$/m
 | 
					          /^(?<message>[a-zA-Z]\S*)([ ]*=[ ]*)(?<pattern>.*)$/m,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        if (match) {
 | 
					        if (match) {
 | 
				
			||||||
          localeMessage.add(match.groups.message);
 | 
					          localeMessage.add(match.groups.message);
 | 
				
			||||||
@ -175,7 +175,7 @@ function replaceString() {
 | 
				
			|||||||
        if (localeMessage.has(match[2])) {
 | 
					        if (localeMessage.has(match[2])) {
 | 
				
			||||||
          input = input.replace(
 | 
					          input = input.replace(
 | 
				
			||||||
            match[0],
 | 
					            match[0],
 | 
				
			||||||
            `${match[1]}="${config.addonRef}-${match[2]}"`
 | 
					            `${match[1]}="${config.addonRef}-${match[2]}"`,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          localeMessageMiss.add(match[2]);
 | 
					          localeMessageMiss.add(match[2]);
 | 
				
			||||||
@ -191,14 +191,14 @@ function replaceString() {
 | 
				
			|||||||
      .filter((f) => f.hasChanged)
 | 
					      .filter((f) => f.hasChanged)
 | 
				
			||||||
      .map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`),
 | 
					      .map((f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`),
 | 
				
			||||||
    replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`),
 | 
					    replaceResultFlt.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`),
 | 
				
			||||||
    replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`)
 | 
					    replaceResultXhtml.filter((f) => f.hasChanged).map((f) => `${f.file} : OK`),
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (localeMessageMiss.size !== 0) {
 | 
					  if (localeMessageMiss.size !== 0) {
 | 
				
			||||||
    console.warn(
 | 
					    console.warn(
 | 
				
			||||||
      `[Build] [Warn] Fluent message [${new Array(
 | 
					      `[Build] [Warn] Fluent message [${new Array(
 | 
				
			||||||
        ...localeMessageMiss
 | 
					        ...localeMessageMiss,
 | 
				
			||||||
      )}] do not exsit in addon's locale files.`
 | 
					      )}] do not exsit in addon's locale files.`,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -221,7 +221,7 @@ async function main() {
 | 
				
			|||||||
  console.log(
 | 
					  console.log(
 | 
				
			||||||
    `[Build] BUILD_DIR=${buildDir}, VERSION=${version}, BUILD_TIME=${buildTime}, ENV=${[
 | 
					    `[Build] BUILD_DIR=${buildDir}, VERSION=${version}, BUILD_TIME=${buildTime}, ENV=${[
 | 
				
			||||||
      env.NODE_ENV,
 | 
					      env.NODE_ENV,
 | 
				
			||||||
    ]}`
 | 
					    ]}`,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearFolder(buildDir);
 | 
					  clearFolder(buildDir);
 | 
				
			||||||
@ -248,12 +248,12 @@ async function main() {
 | 
				
			|||||||
    path.join(buildDir, `${name}.xpi`),
 | 
					    path.join(buildDir, `${name}.xpi`),
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      ignoreBase: true,
 | 
					      ignoreBase: true,
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  console.log("[Build] Addon pack OK");
 | 
					  console.log("[Build] Addon pack OK");
 | 
				
			||||||
  console.log(
 | 
					  console.log(
 | 
				
			||||||
    `[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`
 | 
					    `[Build] Finished in ${(new Date().getTime() - t.getTime()) / 1000} s.`,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { execSync } from "child_process";
 | 
					import { execSync } from "child_process";
 | 
				
			||||||
import { exit } from "process";
 | 
					import { exit } from "process";
 | 
				
			||||||
import { existsSync, writeFileSync, readFileSync } from "fs";
 | 
					import { existsSync, writeFileSync, readFileSync } from "fs";
 | 
				
			||||||
import path from "path"
 | 
					import path from "path";
 | 
				
			||||||
import details from "../package.json" assert { type: "json" };
 | 
					import details from "../package.json" assert { type: "json" };
 | 
				
			||||||
import cmd from "./zotero-cmd.json" assert { type: "json" };
 | 
					import cmd from "./zotero-cmd.json" assert { type: "json" };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,7 +21,7 @@ if (existsSync(profilePath)) {
 | 
				
			|||||||
    console.log(
 | 
					    console.log(
 | 
				
			||||||
      `[info] Addon proxy file has been updated. \n
 | 
					      `[info] Addon proxy file has been updated. \n
 | 
				
			||||||
      File path: ${addonProxyFilePath} \n
 | 
					      File path: ${addonProxyFilePath} \n
 | 
				
			||||||
      Addon path: ${buildPath} \n`
 | 
					      Addon path: ${buildPath} \n`,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ async function onStartup() {
 | 
				
			|||||||
  initLocale();
 | 
					  initLocale();
 | 
				
			||||||
  ztoolkit.ProgressWindow.setIconURI(
 | 
					  ztoolkit.ProgressWindow.setIconURI(
 | 
				
			||||||
    "default",
 | 
					    "default",
 | 
				
			||||||
    `chrome://${config.addonRef}/content/icons/favicon.png`
 | 
					    `chrome://${config.addonRef}/content/icons/favicon.png`,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const popupWin = new ztoolkit.ProgressWindow(config.addonName, {
 | 
					  const popupWin = new ztoolkit.ProgressWindow(config.addonName, {
 | 
				
			||||||
@ -97,7 +97,7 @@ async function onNotify(
 | 
				
			|||||||
  event: string,
 | 
					  event: string,
 | 
				
			||||||
  type: string,
 | 
					  type: string,
 | 
				
			||||||
  ids: Array<string | number>,
 | 
					  ids: Array<string | number>,
 | 
				
			||||||
  extraData: { [key: string]: any }
 | 
					  extraData: { [key: string]: any },
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  // You can add your code to the corresponding notify type
 | 
					  // You can add your code to the corresponding notify type
 | 
				
			||||||
  ztoolkit.log("notify", event, type, ids, extraData);
 | 
					  ztoolkit.log("notify", event, type, ids, extraData);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import { getString } from "../utils/locale";
 | 
				
			|||||||
function example(
 | 
					function example(
 | 
				
			||||||
  target: any,
 | 
					  target: any,
 | 
				
			||||||
  propertyKey: string | symbol,
 | 
					  propertyKey: string | symbol,
 | 
				
			||||||
  descriptor: PropertyDescriptor
 | 
					  descriptor: PropertyDescriptor,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  const original = descriptor.value;
 | 
					  const original = descriptor.value;
 | 
				
			||||||
  descriptor.value = function (...args: any) {
 | 
					  descriptor.value = function (...args: any) {
 | 
				
			||||||
@ -27,7 +27,7 @@ export class BasicExampleFactory {
 | 
				
			|||||||
        event: string,
 | 
					        event: string,
 | 
				
			||||||
        type: string,
 | 
					        type: string,
 | 
				
			||||||
        ids: number[] | string[],
 | 
					        ids: number[] | string[],
 | 
				
			||||||
        extraData: { [key: string]: any }
 | 
					        extraData: { [key: string]: any },
 | 
				
			||||||
      ) => {
 | 
					      ) => {
 | 
				
			||||||
        if (!addon?.data.alive) {
 | 
					        if (!addon?.data.alive) {
 | 
				
			||||||
          this.unregisterNotifier(notifierID);
 | 
					          this.unregisterNotifier(notifierID);
 | 
				
			||||||
@ -50,7 +50,7 @@ export class BasicExampleFactory {
 | 
				
			|||||||
      (e: Event) => {
 | 
					      (e: Event) => {
 | 
				
			||||||
        this.unregisterNotifier(notifierID);
 | 
					        this.unregisterNotifier(notifierID);
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      false
 | 
					      false,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -175,7 +175,7 @@ export class KeyExampleFactory {
 | 
				
			|||||||
      "Conflicting:",
 | 
					      "Conflicting:",
 | 
				
			||||||
      conflictingGroups,
 | 
					      conflictingGroups,
 | 
				
			||||||
      "All keys:",
 | 
					      "All keys:",
 | 
				
			||||||
      ztoolkit.Shortcut.getAll()
 | 
					      ztoolkit.Shortcut.getAll(),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -226,8 +226,8 @@ export class UIExampleFactory {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      "before",
 | 
					      "before",
 | 
				
			||||||
      document.querySelector(
 | 
					      document.querySelector(
 | 
				
			||||||
        "#zotero-itemmenu-addontemplate-test"
 | 
					        "#zotero-itemmenu-addontemplate-test",
 | 
				
			||||||
      ) as XUL.MenuItem
 | 
					      ) as XUL.MenuItem,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -253,13 +253,13 @@ export class UIExampleFactory {
 | 
				
			|||||||
        field: string,
 | 
					        field: string,
 | 
				
			||||||
        unformatted: boolean,
 | 
					        unformatted: boolean,
 | 
				
			||||||
        includeBaseMapped: boolean,
 | 
					        includeBaseMapped: boolean,
 | 
				
			||||||
        item: Zotero.Item
 | 
					        item: Zotero.Item,
 | 
				
			||||||
      ) => {
 | 
					      ) => {
 | 
				
			||||||
        return field + String(item.id);
 | 
					        return field + String(item.id);
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        iconPath: "chrome://zotero/skin/cross.png",
 | 
					        iconPath: "chrome://zotero/skin/cross.png",
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -272,7 +272,7 @@ export class UIExampleFactory {
 | 
				
			|||||||
        field: string,
 | 
					        field: string,
 | 
				
			||||||
        unformatted: boolean,
 | 
					        unformatted: boolean,
 | 
				
			||||||
        includeBaseMapped: boolean,
 | 
					        includeBaseMapped: boolean,
 | 
				
			||||||
        item: Zotero.Item
 | 
					        item: Zotero.Item,
 | 
				
			||||||
      ) => {
 | 
					      ) => {
 | 
				
			||||||
        return String(item.id);
 | 
					        return String(item.id);
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -280,13 +280,13 @@ export class UIExampleFactory {
 | 
				
			|||||||
        renderCellHook(index, data, column) {
 | 
					        renderCellHook(index, data, column) {
 | 
				
			||||||
          const span = document.createElementNS(
 | 
					          const span = document.createElementNS(
 | 
				
			||||||
            "http://www.w3.org/1999/xhtml",
 | 
					            "http://www.w3.org/1999/xhtml",
 | 
				
			||||||
            "span"
 | 
					            "span",
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          span.style.background = "#0dd068";
 | 
					          span.style.background = "#0dd068";
 | 
				
			||||||
          span.innerText = "⭐" + data;
 | 
					          span.innerText = "⭐" + data;
 | 
				
			||||||
          return span;
 | 
					          return span;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -300,7 +300,7 @@ export class UIExampleFactory {
 | 
				
			|||||||
        span.style.background = "rgb(30, 30, 30)";
 | 
					        span.style.background = "rgb(30, 30, 30)";
 | 
				
			||||||
        span.style.color = "rgb(156, 220, 240)";
 | 
					        span.style.color = "rgb(156, 220, 240)";
 | 
				
			||||||
        return span;
 | 
					        return span;
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await ztoolkit.ItemTree.refresh();
 | 
					    await ztoolkit.ItemTree.refresh();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -322,12 +322,12 @@ export class UIExampleFactory {
 | 
				
			|||||||
          ztoolkit.ExtraField.setExtraField(
 | 
					          ztoolkit.ExtraField.setExtraField(
 | 
				
			||||||
            item,
 | 
					            item,
 | 
				
			||||||
            "itemBoxFieldEditable",
 | 
					            "itemBoxFieldEditable",
 | 
				
			||||||
            value
 | 
					            value,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          return true;
 | 
					          return true;
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        index: 1,
 | 
					        index: 1,
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await ztoolkit.ItemBox.register(
 | 
					    await ztoolkit.ItemBox.register(
 | 
				
			||||||
@ -341,7 +341,7 @@ export class UIExampleFactory {
 | 
				
			|||||||
      {
 | 
					      {
 | 
				
			||||||
        editable: false,
 | 
					        editable: false,
 | 
				
			||||||
        index: 2,
 | 
					        index: 2,
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -385,7 +385,7 @@ export class UIExampleFactory {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        targetIndex: 1,
 | 
					        targetIndex: 1,
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -397,11 +397,11 @@ export class UIExampleFactory {
 | 
				
			|||||||
        panel: XUL.TabPanel | undefined,
 | 
					        panel: XUL.TabPanel | undefined,
 | 
				
			||||||
        deck: XUL.Deck,
 | 
					        deck: XUL.Deck,
 | 
				
			||||||
        win: Window,
 | 
					        win: Window,
 | 
				
			||||||
        reader: _ZoteroTypes.ReaderInstance
 | 
					        reader: _ZoteroTypes.ReaderInstance,
 | 
				
			||||||
      ) => {
 | 
					      ) => {
 | 
				
			||||||
        if (!panel) {
 | 
					        if (!panel) {
 | 
				
			||||||
          ztoolkit.log(
 | 
					          ztoolkit.log(
 | 
				
			||||||
            "This reader do not have right-side bar. Adding reader tab skipped."
 | 
					            "This reader do not have right-side bar. Adding reader tab skipped.",
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -457,7 +457,7 @@ export class UIExampleFactory {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        targetIndex: 1,
 | 
					        targetIndex: 1,
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -501,7 +501,7 @@ export class PromptExampleFactory {
 | 
				
			|||||||
            const publicationTitle = item.getField(
 | 
					            const publicationTitle = item.getField(
 | 
				
			||||||
              "publicationTitle",
 | 
					              "publicationTitle",
 | 
				
			||||||
              false,
 | 
					              false,
 | 
				
			||||||
              true
 | 
					              true,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            if (publicationTitle) {
 | 
					            if (publicationTitle) {
 | 
				
			||||||
              nodes.push(`<i>${publicationTitle}</i>`);
 | 
					              nodes.push(`<i>${publicationTitle}</i>`);
 | 
				
			||||||
@ -558,7 +558,7 @@ export class PromptExampleFactory {
 | 
				
			|||||||
          s.addCondition("itemType", "isNot", "attachment");
 | 
					          s.addCondition("itemType", "isNot", "attachment");
 | 
				
			||||||
          let ids = await s.search();
 | 
					          let ids = await s.search();
 | 
				
			||||||
          // prompt.exit will remove current container element.
 | 
					          // prompt.exit will remove current container element.
 | 
				
			||||||
          // @ts-ignore
 | 
					          // @ts-ignore ignore
 | 
				
			||||||
          prompt.exit();
 | 
					          prompt.exit();
 | 
				
			||||||
          const container = prompt.createCommandsContainer();
 | 
					          const container = prompt.createCommandsContainer();
 | 
				
			||||||
          container.classList.add("suggestions");
 | 
					          container.classList.add("suggestions");
 | 
				
			||||||
@ -593,12 +593,12 @@ export class PromptExampleFactory {
 | 
				
			|||||||
                s.addCondition(
 | 
					                s.addCondition(
 | 
				
			||||||
                  "joinMode",
 | 
					                  "joinMode",
 | 
				
			||||||
                  joinMode as Zotero.Search.Operator,
 | 
					                  joinMode as Zotero.Search.Operator,
 | 
				
			||||||
                  ""
 | 
					                  "",
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
                s.addCondition(
 | 
					                s.addCondition(
 | 
				
			||||||
                  conditions[0] as string,
 | 
					                  conditions[0] as string,
 | 
				
			||||||
                  conditions[1] as Zotero.Search.Operator,
 | 
					                  conditions[1] as Zotero.Search.Operator,
 | 
				
			||||||
                  conditions[2] as string
 | 
					                  conditions[2] as string,
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@ -619,7 +619,7 @@ export class PromptExampleFactory {
 | 
				
			|||||||
                  {
 | 
					                  {
 | 
				
			||||||
                    type: "mousemove",
 | 
					                    type: "mousemove",
 | 
				
			||||||
                    listener: function () {
 | 
					                    listener: function () {
 | 
				
			||||||
                      // @ts-ignore
 | 
					                      // @ts-ignore ignore
 | 
				
			||||||
                      prompt.selectItem(this);
 | 
					                      prompt.selectItem(this);
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                  },
 | 
					                  },
 | 
				
			||||||
@ -666,7 +666,7 @@ export class PromptExampleFactory {
 | 
				
			|||||||
              container.appendChild(ele);
 | 
					              container.appendChild(ele);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            // @ts-ignore
 | 
					            // @ts-ignore ignore
 | 
				
			||||||
            prompt.exit();
 | 
					            prompt.exit();
 | 
				
			||||||
            prompt.showTip("Not Found.");
 | 
					            prompt.showTip("Not Found.");
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
@ -693,9 +693,9 @@ export class PromptExampleFactory {
 | 
				
			|||||||
            `You select ${items.length} items!\n\n${items
 | 
					            `You select ${items.length} items!\n\n${items
 | 
				
			||||||
              .map(
 | 
					              .map(
 | 
				
			||||||
                (item, index) =>
 | 
					                (item, index) =>
 | 
				
			||||||
                  String(index + 1) + ". " + item.getDisplayTitle()
 | 
					                  String(index + 1) + ". " + item.getDisplayTitle(),
 | 
				
			||||||
              )
 | 
					              )
 | 
				
			||||||
              .join("\n")}`
 | 
					              .join("\n")}`,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -757,7 +757,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          properties: { label: "Cell 1,0" },
 | 
					          properties: { label: "Cell 1,0" },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        false
 | 
					        false,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addCell(4, 0, {
 | 
					      .addCell(4, 0, {
 | 
				
			||||||
        tag: "label",
 | 
					        tag: "label",
 | 
				
			||||||
@ -780,7 +780,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
            type: "text",
 | 
					            type: "text",
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        false
 | 
					        false,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addCell(5, 0, {
 | 
					      .addCell(5, 0, {
 | 
				
			||||||
        tag: "h2",
 | 
					        tag: "h2",
 | 
				
			||||||
@ -815,7 +815,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        false
 | 
					        false,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addCell(
 | 
					      .addCell(
 | 
				
			||||||
        7,
 | 
					        7,
 | 
				
			||||||
@ -846,7 +846,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        false
 | 
					        false,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addCell(
 | 
					      .addCell(
 | 
				
			||||||
        8,
 | 
					        8,
 | 
				
			||||||
@ -877,7 +877,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        false
 | 
					        false,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addCell(
 | 
					      .addCell(
 | 
				
			||||||
        9,
 | 
					        9,
 | 
				
			||||||
@ -908,7 +908,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        false
 | 
					        false,
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addButton("Confirm", "confirm")
 | 
					      .addButton("Confirm", "confirm")
 | 
				
			||||||
      .addButton("Cancel", "cancel")
 | 
					      .addButton("Cancel", "cancel")
 | 
				
			||||||
@ -916,7 +916,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
        noClose: true,
 | 
					        noClose: true,
 | 
				
			||||||
        callback: (e) => {
 | 
					        callback: (e) => {
 | 
				
			||||||
          dialogHelper.window?.alert(
 | 
					          dialogHelper.window?.alert(
 | 
				
			||||||
            "Help Clicked! Dialog will not be closed."
 | 
					            "Help Clicked! Dialog will not be closed.",
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
@ -927,7 +927,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
    addon.data.dialog = undefined;
 | 
					    addon.data.dialog = undefined;
 | 
				
			||||||
    addon.data.alive &&
 | 
					    addon.data.alive &&
 | 
				
			||||||
      ztoolkit.getGlobal("alert")(
 | 
					      ztoolkit.getGlobal("alert")(
 | 
				
			||||||
        `Close dialog with ${dialogData._lastButtonId}.\nCheckbox: ${dialogData.checkboxValue}\nInput: ${dialogData.inputValue}.`
 | 
					        `Close dialog with ${dialogData._lastButtonId}.\nCheckbox: ${dialogData.checkboxValue}\nInput: ${dialogData.inputValue}.`,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    ztoolkit.log(dialogData);
 | 
					    ztoolkit.log(dialogData);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -937,11 +937,11 @@ export class HelperExampleFactory {
 | 
				
			|||||||
    new ztoolkit.Clipboard()
 | 
					    new ztoolkit.Clipboard()
 | 
				
			||||||
      .addText(
 | 
					      .addText(
 | 
				
			||||||
        "",
 | 
					        "",
 | 
				
			||||||
        "text/unicode"
 | 
					        "text/unicode",
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .addText(
 | 
					      .addText(
 | 
				
			||||||
        '<a href="https://github.com/windingwind/zotero-plugin-template">Plugin Template</a>',
 | 
					        '<a href="https://github.com/windingwind/zotero-plugin-template">Plugin Template</a>',
 | 
				
			||||||
        "text/html"
 | 
					        "text/html",
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
      .copy();
 | 
					      .copy();
 | 
				
			||||||
    ztoolkit.getGlobal("alert")("Copied!");
 | 
					    ztoolkit.getGlobal("alert")("Copied!");
 | 
				
			||||||
@ -956,7 +956,7 @@ export class HelperExampleFactory {
 | 
				
			|||||||
        ["PNG File(*.png)", "*.png"],
 | 
					        ["PNG File(*.png)", "*.png"],
 | 
				
			||||||
        ["Any", "*.*"],
 | 
					        ["Any", "*.*"],
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      "image.png"
 | 
					      "image.png",
 | 
				
			||||||
    ).open();
 | 
					    ).open();
 | 
				
			||||||
    ztoolkit.getGlobal("alert")(`Selected ${path}`);
 | 
					    ztoolkit.getGlobal("alert")(`Selected ${path}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,7 @@ async function updatePrefsUI() {
 | 
				
			|||||||
        addon.data.prefs?.rows[index] || {
 | 
					        addon.data.prefs?.rows[index] || {
 | 
				
			||||||
          title: "no data",
 | 
					          title: "no data",
 | 
				
			||||||
          detail: "no data",
 | 
					          detail: "no data",
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    // Show a progress window when selection changes
 | 
					    // Show a progress window when selection changes
 | 
				
			||||||
    .setProp("onSelectionChange", (selection) => {
 | 
					    .setProp("onSelectionChange", (selection) => {
 | 
				
			||||||
@ -86,7 +86,7 @@ async function updatePrefsUI() {
 | 
				
			|||||||
      if (event.key == "Delete" || (Zotero.isMac && event.key == "Backspace")) {
 | 
					      if (event.key == "Delete" || (Zotero.isMac && event.key == "Backspace")) {
 | 
				
			||||||
        addon.data.prefs!.rows =
 | 
					        addon.data.prefs!.rows =
 | 
				
			||||||
          addon.data.prefs?.rows.filter(
 | 
					          addon.data.prefs?.rows.filter(
 | 
				
			||||||
            (v, i) => !tableHelper.treeInstance.selection.isSelected(i)
 | 
					            (v, i) => !tableHelper.treeInstance.selection.isSelected(i),
 | 
				
			||||||
          ) || [];
 | 
					          ) || [];
 | 
				
			||||||
        tableHelper.render();
 | 
					        tableHelper.render();
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@ -96,7 +96,7 @@ async function updatePrefsUI() {
 | 
				
			|||||||
    // For find-as-you-type
 | 
					    // For find-as-you-type
 | 
				
			||||||
    .setProp(
 | 
					    .setProp(
 | 
				
			||||||
      "getRowString",
 | 
					      "getRowString",
 | 
				
			||||||
      (index) => addon.data.prefs?.rows[index].title || ""
 | 
					      (index) => addon.data.prefs?.rows[index].title || "",
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    // Render the table.
 | 
					    // Render the table.
 | 
				
			||||||
    .render(-1, () => {
 | 
					    .render(-1, () => {
 | 
				
			||||||
@ -109,23 +109,23 @@ async function updatePrefsUI() {
 | 
				
			|||||||
function bindPrefEvents() {
 | 
					function bindPrefEvents() {
 | 
				
			||||||
  addon.data
 | 
					  addon.data
 | 
				
			||||||
    .prefs!.window.document.querySelector(
 | 
					    .prefs!.window.document.querySelector(
 | 
				
			||||||
      `#zotero-prefpane-${config.addonRef}-enable`
 | 
					      `#zotero-prefpane-${config.addonRef}-enable`,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    ?.addEventListener("command", (e) => {
 | 
					    ?.addEventListener("command", (e) => {
 | 
				
			||||||
      ztoolkit.log(e);
 | 
					      ztoolkit.log(e);
 | 
				
			||||||
      addon.data.prefs!.window.alert(
 | 
					      addon.data.prefs!.window.alert(
 | 
				
			||||||
        `Successfully changed to ${(e.target as XUL.Checkbox).checked}!`
 | 
					        `Successfully changed to ${(e.target as XUL.Checkbox).checked}!`,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  addon.data
 | 
					  addon.data
 | 
				
			||||||
    .prefs!.window.document.querySelector(
 | 
					    .prefs!.window.document.querySelector(
 | 
				
			||||||
      `#zotero-prefpane-${config.addonRef}-input`
 | 
					      `#zotero-prefpane-${config.addonRef}-input`,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    ?.addEventListener("change", (e) => {
 | 
					    ?.addEventListener("change", (e) => {
 | 
				
			||||||
      ztoolkit.log(e);
 | 
					      ztoolkit.log(e);
 | 
				
			||||||
      addon.data.prefs!.window.alert(
 | 
					      addon.data.prefs!.window.alert(
 | 
				
			||||||
        `Successfully changed to ${(e.target as HTMLInputElement).value}!`
 | 
					        `Successfully changed to ${(e.target as HTMLInputElement).value}!`,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,7 @@ function getString(localString: string): string;
 | 
				
			|||||||
function getString(localString: string, branch: string): string;
 | 
					function getString(localString: string, branch: string): string;
 | 
				
			||||||
function getString(
 | 
					function getString(
 | 
				
			||||||
  localString: string,
 | 
					  localString: string,
 | 
				
			||||||
  options: { branch?: string | undefined; args?: Record<string, unknown> }
 | 
					  options: { branch?: string | undefined; args?: Record<string, unknown> },
 | 
				
			||||||
): string;
 | 
					): string;
 | 
				
			||||||
function getString(...inputs: any[]) {
 | 
					function getString(...inputs: any[]) {
 | 
				
			||||||
  if (inputs.length === 1) {
 | 
					  if (inputs.length === 1) {
 | 
				
			||||||
@ -61,7 +61,7 @@ function getString(...inputs: any[]) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function _getString(
 | 
					function _getString(
 | 
				
			||||||
  localString: string,
 | 
					  localString: string,
 | 
				
			||||||
  options: { branch?: string | undefined; args?: Record<string, unknown> } = {}
 | 
					  options: { branch?: string | undefined; args?: Record<string, unknown> } = {},
 | 
				
			||||||
): string {
 | 
					): string {
 | 
				
			||||||
  const localStringWithPrefix = `${config.addonRef}-${localString}`;
 | 
					  const localStringWithPrefix = `${config.addonRef}-${localString}`;
 | 
				
			||||||
  const { branch, args } = options;
 | 
					  const { branch, args } = options;
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ export function waitUntil(
 | 
				
			|||||||
  condition: () => boolean,
 | 
					  condition: () => boolean,
 | 
				
			||||||
  callback: () => void,
 | 
					  callback: () => void,
 | 
				
			||||||
  interval = 100,
 | 
					  interval = 100,
 | 
				
			||||||
  timeout = 10000
 | 
					  timeout = 10000,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  const start = Date.now();
 | 
					  const start = Date.now();
 | 
				
			||||||
  const intervalId = ztoolkit.getGlobal("setInterval")(() => {
 | 
					  const intervalId = ztoolkit.getGlobal("setInterval")(() => {
 | 
				
			||||||
@ -32,7 +32,7 @@ export function waitUntil(
 | 
				
			|||||||
export function waitUtilAsync(
 | 
					export function waitUtilAsync(
 | 
				
			||||||
  condition: () => boolean,
 | 
					  condition: () => boolean,
 | 
				
			||||||
  interval = 100,
 | 
					  interval = 100,
 | 
				
			||||||
  timeout = 10000
 | 
					  timeout = 10000,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  return new Promise<void>((resolve, reject) => {
 | 
					  return new Promise<void>((resolve, reject) => {
 | 
				
			||||||
    const start = Date.now();
 | 
					    const start = Date.now();
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user