mirror of
				https://github.com/tidys/cc-inspector-chrome
				synced 2025-11-04 13:05:25 +00:00 
			
		
		
		
	完成节点数据的收集
This commit is contained in:
		
							
								
								
									
										30
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								README.md
									
									
									
									
									
								
							@@ -1,27 +1,7 @@
 | 
			
		||||
# Vue.js Chrome Extension Template ([wcer](https://github.com/YuraDev/wcer))
 | 
			
		||||
> Template for quick creation of Chrome extension on Vuejs c hot reloading when developing.
 | 
			
		||||
#说明
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## Installation:
 | 
			
		||||
This boilerplate was built as a template for [vue-cli](https://github.com/vuejs/vue-cli) and includes options to customize your final scaffolded app. 
 | 
			
		||||
``` bash
 | 
			
		||||
# install vue-cli
 | 
			
		||||
$ npm install -g vue-cli
 | 
			
		||||
# create a new project using the template
 | 
			
		||||
$ vue init YuraDev/vue-chrome-extension-template my-project
 | 
			
		||||
# install dependencies and go!
 | 
			
		||||
$ cd my-project
 | 
			
		||||
$ npm install # or yarn
 | 
			
		||||
$ npm run dev # or yarn dev
 | 
			
		||||
inject在development模式下无法正常使用,暂时的解决办法,注释掉`vue-cli-plugin-browser-extension`代码中的
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Structure
 | 
			
		||||
* [backend](https://developer.chrome.com/extensions/background_pages): Background work of your scripts
 | 
			
		||||
* [content](https://developer.chrome.com/extensions/content_scripts) Run in the context of web pages 
 | 
			
		||||
* [devtools](https://developer.chrome.com/extensions/devtools) - It can add new UI panels and sidebars, interact with the inspected page, get information about network requests, and more.
 | 
			
		||||
* [options](https://developer.chrome.com/extensions/options) - To allow users to customize the behavior of your extension, you may wish to provide an options page.
 | 
			
		||||
* popup - The page (window) that will be displayed when the icon is clicked
 | 
			
		||||
* tab - Your application will work in a separate tab
 | 
			
		||||
* ext - Shared scripts
 | 
			
		||||
* [manifest.js](https://developer.chrome.com/extensions/manifest) - Descriptions of the application, its rights and possibilities
 | 
			
		||||
webpackConfig.plugin('extension-reloader').use(ExtensionReloader, [{ entries, ...extensionReloaderOptions }])
 | 
			
		||||
```
 | 
			
		||||
详细原因参考:[issues](https://github.com/adambullmer/vue-cli-plugin-browser-extension/issues/120)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
  root: true,
 | 
			
		||||
  globals: {
 | 
			
		||||
    chrome: true,
 | 
			
		||||
  },
 | 
			
		||||
  env: {
 | 
			
		||||
    node: true,
 | 
			
		||||
    webextensions: true,
 | 
			
		||||
@@ -37,5 +40,7 @@ module.exports = {
 | 
			
		||||
    "@typescript-eslint/ban-types": "off",
 | 
			
		||||
    "@typescript-eslint/no-empty-function": "off",
 | 
			
		||||
    "@typescript-eslint/no-explicit-any": "off",
 | 
			
		||||
    "no-prototype-builtins": "off",
 | 
			
		||||
    "@typescript-eslint/ban-ts-comment": "off",
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -5,36 +5,22 @@ let DevtoolsPanel: chrome.runtime.Port | null = null;
 | 
			
		||||
let Content: chrome.runtime.Port | null = null;
 | 
			
		||||
console.log('on background')
 | 
			
		||||
 | 
			
		||||
function shortConnectionLink(request: any, sender: any, sendResponse: any) {
 | 
			
		||||
  // console.log(`%c[短连接|id:${sender.id}|url:${sender.url}]\n${JSON.stringify(request)}`, 'background:#aaa;color:#BD4E19')
 | 
			
		||||
  sendResponse && sendResponse(request);
 | 
			
		||||
  if (request.msg === PluginMsg.Msg.Support ||
 | 
			
		||||
    request.msg === PluginMsg.Msg.ListInfo ||
 | 
			
		||||
    request.msg === PluginMsg.Msg.NodeInfo) {
 | 
			
		||||
    // 将消息转发到devtools
 | 
			
		||||
    Devtools && Devtools.postMessage(request);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function longConnectionLink(data: any, sender: any) {
 | 
			
		||||
  console.log(`%c[长连接:${sender.name}]\n${JSON.stringify(data)}`, "background:#aaa;color:#bada55")
 | 
			
		||||
  sender.postMessage(data);
 | 
			
		||||
  if (data.msg === PluginMsg.Msg.UrlChange) {
 | 
			
		||||
    if (sender.name === PluginMsg.Page.DevToolsPanel) {
 | 
			
		||||
      Content && Content.postMessage({msg: PluginMsg.Msg.UrlChange, data: {}})
 | 
			
		||||
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
 | 
			
		||||
  console.log(`%c[Connect] ${port.name}`, "color:blue;");
 | 
			
		||||
  port.onMessage.addListener((data: any, sender: any) => {
 | 
			
		||||
    console.log(`%c[Connect-Message] ${sender.name}\n${JSON.stringify(data)}`, "color:green;")
 | 
			
		||||
    sender.postMessage(data);
 | 
			
		||||
    if (data.msg === PluginMsg.Msg.UrlChange) {
 | 
			
		||||
      if (sender.name === PluginMsg.Page.DevToolsPanel) {
 | 
			
		||||
        Content && Content.postMessage({msg: PluginMsg.Msg.UrlChange, data: {}})
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // chrome.tabs.executeScript(message.tabId, {code: message.content});
 | 
			
		||||
  // port.postMessage(message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 长连接
 | 
			
		||||
chrome.runtime.onConnect.addListener((port) => {
 | 
			
		||||
  console.log(`%c[长连接:${port.name}] 建立链接!`, "background:#aaa;color:#ff0000");
 | 
			
		||||
  port.onMessage.addListener(longConnectionLink);
 | 
			
		||||
  port.onDisconnect.addListener(function (port) {
 | 
			
		||||
    console.log(`%c[长连接:${port.name}] 断开链接!`, "background:#aaa;color:#00ff00");
 | 
			
		||||
    port.onMessage.removeListener(longConnectionLink);
 | 
			
		||||
    // chrome.tabs.executeScript(message.tabId, {code: message.content});
 | 
			
		||||
    // port.postMessage(message);
 | 
			
		||||
  });
 | 
			
		||||
  port.onDisconnect.addListener(function (port: chrome.runtime.Port) {
 | 
			
		||||
    console.log(`%c[Connect-Dis] ${port.name}`, "color:red");
 | 
			
		||||
    // port.onMessage.removeListener(longConnectionLink);
 | 
			
		||||
    if (port.name === PluginMsg.Page.Devtools) {
 | 
			
		||||
      Devtools = null;
 | 
			
		||||
    } else if (port.name === PluginMsg.Page.Content) {
 | 
			
		||||
@@ -55,8 +41,18 @@ chrome.runtime.onConnect.addListener((port) => {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// background.js 更像是一个主进程,负责整个插件的调度,生命周期和chrome保持一致
 | 
			
		||||
// [短连接] 监听来自content.js发来的事件
 | 
			
		||||
chrome.runtime.onMessage.addListener(shortConnectionLink);
 | 
			
		||||
//  监听来自content.js发来的事件
 | 
			
		||||
chrome.runtime.onMessage.addListener((request: any, sender: any, sendResponse: any) => {
 | 
			
		||||
    console.log(`%c[Message]url:${sender.url}]\n${JSON.stringify(request)}`, 'color:green')
 | 
			
		||||
    sendResponse && sendResponse(request);
 | 
			
		||||
    if (request.msg === PluginMsg.Msg.Support ||
 | 
			
		||||
      request.msg === PluginMsg.Msg.ListInfo ||
 | 
			
		||||
      request.msg === PluginMsg.Msg.NodeInfo) {
 | 
			
		||||
      // 将消息转发到devtools
 | 
			
		||||
      Devtools && Devtools.postMessage(request);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
 | 
			
		||||
  if (changeInfo.status === "complete") {
 | 
			
		||||
@@ -69,7 +65,8 @@ chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function createPluginMenus() {
 | 
			
		||||
  // 右键菜单
 | 
			
		||||
  const menus = [];
 | 
			
		||||
 | 
			
		||||
  let parent = chrome.contextMenus.create({id: "parent", title: "CC-Inspector"});
 | 
			
		||||
  chrome.contextMenus.create({
 | 
			
		||||
    id: "test",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +1,32 @@
 | 
			
		||||
console.log('content code')
 | 
			
		||||
// 具有操作dom的能力
 | 
			
		||||
// 加载其他脚本
 | 
			
		||||
// content.js 和原始界面共享DOM,但是不共享js,要想访问页面js,只能通过注入的方式
 | 
			
		||||
// content.js 和原始界面共享DOM,具有操作dom的能力
 | 
			
		||||
// 但是不共享js,要想访问页面js,只能通过注入的方式
 | 
			
		||||
import * as PluginMsg from './core/plugin-msg'
 | 
			
		||||
 | 
			
		||||
function injectScriptToPage(url) {
 | 
			
		||||
function injectScriptToPage(url: string) {
 | 
			
		||||
  let content = chrome.extension.getURL(url)
 | 
			
		||||
  console.log(`[cc-inspector]注入脚本:${content}`);
 | 
			
		||||
  let script = document.createElement('script')
 | 
			
		||||
  script.setAttribute('type', 'text/javascript')
 | 
			
		||||
  script.setAttribute('src', content)
 | 
			
		||||
  script.onload = function () {
 | 
			
		||||
    // 注入脚本执行完后移除掉
 | 
			
		||||
    this.parentNode.removeChild(this);
 | 
			
		||||
    document.body.removeChild(script);
 | 
			
		||||
  }
 | 
			
		||||
  document.body.appendChild(script)
 | 
			
		||||
}
 | 
			
		||||
debugger
 | 
			
		||||
 | 
			
		||||
injectScriptToPage("js/inject.js");
 | 
			
		||||
 | 
			
		||||
// 和background.js保持长连接通讯
 | 
			
		||||
let conn = chrome.runtime.connect({name: PluginMsg.Page.Content})
 | 
			
		||||
// conn.postMessage('test');
 | 
			
		||||
conn.onMessage.addListener(function (data) {
 | 
			
		||||
  // 将background.js的消息返回到injection.js
 | 
			
		||||
  console.log(`%c[Connect-Message] ${JSON.stringify(data)}`, "color:green;")
 | 
			
		||||
  window.postMessage(data, "*");
 | 
			
		||||
})
 | 
			
		||||
// 接受来自inject.js的消息数据,然后中转到background.js
 | 
			
		||||
window.addEventListener('message', function (event) {
 | 
			
		||||
  let data = event.data;
 | 
			
		||||
  if (data.data.log) {
 | 
			
		||||
  }
 | 
			
		||||
  console.log(`%c[content] ${JSON.stringify(data)}`, "color:#BD4E19");
 | 
			
		||||
  console.log(`%c[Window-Message] ${JSON.stringify(data)}`, "color:green;");
 | 
			
		||||
  chrome.runtime.sendMessage(data);
 | 
			
		||||
}, false);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
export const Page = {
 | 
			
		||||
  Inject: "inject.js",
 | 
			
		||||
  Devtools: "devtools.js",
 | 
			
		||||
  Inject: "Inject",
 | 
			
		||||
  Devtools: "Devtools",
 | 
			
		||||
  DevToolsPanel: "DevToolsPanel",
 | 
			
		||||
  Content: "content.js",
 | 
			
		||||
  Popup: "popup.js",
 | 
			
		||||
  Options: "options.js",
 | 
			
		||||
  Content: "Content",
 | 
			
		||||
  Popup: "Popup",
 | 
			
		||||
  Options: "Options",
 | 
			
		||||
}
 | 
			
		||||
export const Msg = {
 | 
			
		||||
  NodeInfo: "node_info",// 具体的节点信息
 | 
			
		||||
 
 | 
			
		||||
@@ -39,9 +39,6 @@ export default class NodeBaseProperty extends Vue {
 | 
			
		||||
  @Prop({default: "label"})
 | 
			
		||||
  private label?: string | undefined
 | 
			
		||||
 | 
			
		||||
  @Prop()
 | 
			
		||||
  private itemData: any;
 | 
			
		||||
 | 
			
		||||
  setup() {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ import {DataType} from '../data'
 | 
			
		||||
@Component({
 | 
			
		||||
  components: {}
 | 
			
		||||
})
 | 
			
		||||
// todo 支持array
 | 
			
		||||
export default class UiProp extends Vue {
 | 
			
		||||
  @Prop({default: ""})
 | 
			
		||||
  name: string | undefined;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ export enum DataType {
 | 
			
		||||
 | 
			
		||||
class Info {
 | 
			
		||||
  public type: DataType = DataType.Number;
 | 
			
		||||
  public data: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class TextData extends Info {
 | 
			
		||||
@@ -20,10 +21,19 @@ export class TextData extends Info {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ColorData extends Info {
 | 
			
		||||
  constructor(color: string) {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = DataType.Color;
 | 
			
		||||
    this.data = color;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class StringData extends Info {
 | 
			
		||||
  constructor() {
 | 
			
		||||
  constructor(data: string) {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = DataType.String;
 | 
			
		||||
    this.data = data;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -31,33 +41,44 @@ export class NumberData extends Info {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = DataType.Number;
 | 
			
		||||
    this.data = 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class BoolData extends Info {
 | 
			
		||||
  constructor(bol: boolean) {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = DataType.Bool;
 | 
			
		||||
    this.data = bol;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Vec2Data extends Info {
 | 
			
		||||
  public v1: number = 0;
 | 
			
		||||
  public v2: number = 0;
 | 
			
		||||
 | 
			
		||||
  constructor(v1: number, v2: number) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = DataType.Vec2
 | 
			
		||||
    this.v1 = v1;
 | 
			
		||||
    this.v2 = v2;
 | 
			
		||||
    this.data = [];
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  add(info: Property) {
 | 
			
		||||
    this.data.push(info);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Vec3Data extends Info {
 | 
			
		||||
  public v1: number = 0;
 | 
			
		||||
  public v2: number = 0;
 | 
			
		||||
  public v3: number = 0;
 | 
			
		||||
 | 
			
		||||
  constructor(v1: number, v2: number, v3: number) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super();
 | 
			
		||||
    this.type = DataType.Vec3
 | 
			
		||||
    this.v1 = v1;
 | 
			
		||||
    this.v2 = v2;
 | 
			
		||||
    this.v3 = v3;
 | 
			
		||||
    this.type = DataType.Vec3;
 | 
			
		||||
    this.data = [];
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  add(info: Property) {
 | 
			
		||||
    this.data.push(info);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +90,29 @@ export class EnumData extends Info {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Property {
 | 
			
		||||
  public name: string = 'property';
 | 
			
		||||
  public value: Info = new Info();
 | 
			
		||||
 | 
			
		||||
  constructor(name: string, info: Info) {
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.value = info;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Group {
 | 
			
		||||
  public name: string = 'group';
 | 
			
		||||
  public data: Array<Property> = [];
 | 
			
		||||
 | 
			
		||||
  constructor(name: string) {
 | 
			
		||||
    this.name = name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addProperty(property: Property) {
 | 
			
		||||
    this.data.push(property)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class NodeInfo {
 | 
			
		||||
  public type: string = ''; // 类型
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,6 @@
 | 
			
		||||
  <div id="devtools">
 | 
			
		||||
    <div v-show="isShowDebug" class="find">
 | 
			
		||||
      <div v-if="false">
 | 
			
		||||
        <el-button type="success" @click="onBtnClickTest1">Test1</el-button>
 | 
			
		||||
        <el-button type="success" @click="onBtnClickTest2">Test2</el-button>
 | 
			
		||||
        <el-button type="success" @click="onMemoryTest">内存测试</el-button>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-if="false">
 | 
			
		||||
@@ -28,7 +26,7 @@
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="right">
 | 
			
		||||
        <NodeBaseProperty :item-data="treeItemData"></NodeBaseProperty>
 | 
			
		||||
        <NodeBaseProperty :all-group="treeItemData"></NodeBaseProperty>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div v-show="!isShowDebug" class="no-find">
 | 
			
		||||
@@ -56,7 +54,7 @@ const PluginMsg = require("../core/plugin-msg");
 | 
			
		||||
})
 | 
			
		||||
export default class Index extends Vue {
 | 
			
		||||
  private isShowDebug: boolean = false;
 | 
			
		||||
  treeItemData: Record<string, any> = {};
 | 
			
		||||
  treeItemData: Array<Record<string, any>> = [];
 | 
			
		||||
  treeData: Array<Record<string, any>> = []
 | 
			
		||||
  bgConn: chrome.runtime.Port | null = null// 与background.js的链接
 | 
			
		||||
 | 
			
		||||
@@ -97,11 +95,17 @@ export default class Index extends Vue {
 | 
			
		||||
      let eventMsg = data.msg;
 | 
			
		||||
      if (eventMsg === PluginMsg.Msg.ListInfo) {
 | 
			
		||||
        this.isShowDebug = true;
 | 
			
		||||
        this._updateTreeView(eventData);
 | 
			
		||||
        if (!Array.isArray(eventData)) {
 | 
			
		||||
          eventData = [eventData]
 | 
			
		||||
        }
 | 
			
		||||
        this.treeData = eventData;
 | 
			
		||||
      } else if (eventMsg === PluginMsg.Msg.Support) {
 | 
			
		||||
        this.isShowDebug = eventData.support;
 | 
			
		||||
      } else if (eventMsg === PluginMsg.Msg.NodeInfo) {
 | 
			
		||||
        this.isShowDebug = true;
 | 
			
		||||
        if (!Array.isArray(eventData)) {
 | 
			
		||||
          eventData = [eventData]
 | 
			
		||||
        }
 | 
			
		||||
        this.treeItemData = eventData;
 | 
			
		||||
      } else if (eventMsg === PluginMsg.Msg.MemoryInfo) {
 | 
			
		||||
        this.memory = eventData;
 | 
			
		||||
@@ -114,27 +118,6 @@ export default class Index extends Vue {
 | 
			
		||||
    for (let i = 0; i < 40; i++) {
 | 
			
		||||
      this.treeData.push({name: `node${i}`, children: [{name: `children11111111111111111111111111111111111111${i}`}]})
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.treeItemData = {
 | 
			
		||||
      "uuid": "11",
 | 
			
		||||
      "name": "name",
 | 
			
		||||
      "type": "cc_Node",
 | 
			
		||||
      "height": 1080.986301369863,
 | 
			
		||||
      "color": "#fff85f",
 | 
			
		||||
      "opacity": 255,
 | 
			
		||||
      "components": [
 | 
			
		||||
        {
 | 
			
		||||
          "uuid": "Comp.931",
 | 
			
		||||
          "type": "cc_Canvas",
 | 
			
		||||
          "name": "Canvas<Canvas>"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "uuid": "Comp.932",
 | 
			
		||||
          "type": "HotUpdateScene",
 | 
			
		||||
          "name": "Canvas<HotUpdateScene>"
 | 
			
		||||
        }],
 | 
			
		||||
      "active": true
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleNodeClick(data: any) {
 | 
			
		||||
@@ -157,191 +140,44 @@ export default class Index extends Vue {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _updateTreeView(data: any) {
 | 
			
		||||
    this.treeData = [data.scene];
 | 
			
		||||
    return;
 | 
			
		||||
    // 构建树形数据
 | 
			
		||||
    if (this.treeData.length === 0) {// 第一次赋值
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
 | 
			
		||||
  evalInspectorFunction(func: string, para?: string = '') {
 | 
			
		||||
    if (!func || func.length < 0) {
 | 
			
		||||
      console.log("缺失执行函数名!");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    let treeData = [];
 | 
			
		||||
    debugger
 | 
			
		||||
    let sceneData = data.scene;
 | 
			
		||||
    if (sceneData) {
 | 
			
		||||
      // scene info
 | 
			
		||||
      let dataRoot = {
 | 
			
		||||
        type: sceneData.type, uuid: sceneData.uuid,
 | 
			
		||||
        label: sceneData.name, children: []
 | 
			
		||||
      };
 | 
			
		||||
      treeData.push(dataRoot);
 | 
			
		||||
      this.handleNodeClick(dataRoot);
 | 
			
		||||
      // scene children info
 | 
			
		||||
      for (let k in sceneData.children) {
 | 
			
		||||
        let itemSceneData = sceneData.children[k];
 | 
			
		||||
        // let sceneItem = {uuid: itemSceneData.uuid, label: itemSceneData.name, children: []};
 | 
			
		||||
        let sceneItem = {};
 | 
			
		||||
        dealChildrenNode(itemSceneData, sceneItem);
 | 
			
		||||
        treeData[0].children.push(sceneItem);
 | 
			
		||||
      }
 | 
			
		||||
    if (!chrome || !chrome.devtools) {
 | 
			
		||||
      console.log("环境异常,无法执行函数");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.treeData = treeData;
 | 
			
		||||
 | 
			
		||||
    function dealChildrenNode(rootData: any, obj: any) {
 | 
			
		||||
      obj["data"] = rootData;
 | 
			
		||||
      obj["uuid"] = rootData.uuid;
 | 
			
		||||
      obj["label"] = rootData.name;
 | 
			
		||||
      obj["type"] = rootData.type;
 | 
			
		||||
      obj["children"] = [];
 | 
			
		||||
      let rootChildren = rootData.children;
 | 
			
		||||
      for (let k in rootChildren) {
 | 
			
		||||
        let itemData = rootChildren[k];
 | 
			
		||||
        let item = {};
 | 
			
		||||
        dealChildrenNode(itemData, item);
 | 
			
		||||
        obj.children.push(item);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _getInjectScriptString() {
 | 
			
		||||
    let injectScript = "";
 | 
			
		||||
    let code = injectScript.toString();
 | 
			
		||||
    let array = code.split("\n");
 | 
			
		||||
    array.splice(0, 1);// 删除开头
 | 
			
		||||
    array.splice(-1, 1);// 删除结尾
 | 
			
		||||
    let evalCode = "";
 | 
			
		||||
    for (let i = 0; i < array.length; i++) {
 | 
			
		||||
      evalCode += array[i] + "\n";
 | 
			
		||||
    }
 | 
			
		||||
    // console.log(evalCode);
 | 
			
		||||
    return evalCode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  evalInspectorFunction(funcString: string, parm?: any) {
 | 
			
		||||
    if (funcString || funcString.length > 0) {
 | 
			
		||||
      let injectCode =
 | 
			
		||||
          `if(window.ccinspector){
 | 
			
		||||
              let func = window.ccinspector.${funcString};
 | 
			
		||||
    let injectCode =
 | 
			
		||||
        `if(window.ccinspector){
 | 
			
		||||
              let func = window.ccinspector.${func};
 | 
			
		||||
              if(func){
 | 
			
		||||
                console.log("执行${funcString}成功");
 | 
			
		||||
                func.apply(window.ccinspector,[${parm}]);
 | 
			
		||||
                console.log("执行${func}成功");
 | 
			
		||||
                func.apply(window.ccinspector,[${para}]);
 | 
			
		||||
              }else{
 | 
			
		||||
                console.log("未发现${funcString}函数");
 | 
			
		||||
                console.log("未发现${func}函数");
 | 
			
		||||
              }
 | 
			
		||||
            }else{
 | 
			
		||||
              console.log("可能脚本没有注入");
 | 
			
		||||
            }`;
 | 
			
		||||
      console.log(injectCode);
 | 
			
		||||
      if (chrome && chrome.devtools) {
 | 
			
		||||
        let ret = chrome.devtools.inspectedWindow.eval(injectCode, function (result, info) {
 | 
			
		||||
          if (info && info.isException) {
 | 
			
		||||
            console.log(info.value);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
        console.log(`ret:${ret}`);
 | 
			
		||||
         }else{
 | 
			
		||||
              console.log("脚本inject.js未注入");
 | 
			
		||||
         }`;
 | 
			
		||||
    chrome.devtools.inspectedWindow.eval(injectCode, (result, isException) => {
 | 
			
		||||
      if (isException) {
 | 
			
		||||
        console.error(isException);
 | 
			
		||||
      } else {
 | 
			
		||||
        console.log(`执行结果:${result}`)
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log("执行失败!");
 | 
			
		||||
    }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onBtnClickUpdateTree() {
 | 
			
		||||
    this.evalInspectorFunction("updateTreeInfo");
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onBtnClickUpdatePage() {
 | 
			
		||||
    this.evalInspectorFunction("checkIsGamePage", "true");
 | 
			
		||||
    // let code = this._getInjectScriptString();
 | 
			
		||||
    // chrome.devtools.inspectedWindow.eval(code, function () {
 | 
			
		||||
    //   console.log("刷新成功!");
 | 
			
		||||
    // });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onBtnClickTest1() {
 | 
			
		||||
    chrome.devtools.inspectedWindow.eval(`window.ccinspector.testMsg1()`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _getTime() {
 | 
			
		||||
    return new Date().getTime().toString();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onBtnClickTest2() {
 | 
			
		||||
    // chrome.devtools.inspectedWindow.eval(`window.ccinspector.testMsg2()`)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    let newData = [
 | 
			
		||||
      {
 | 
			
		||||
        name: this._getTime(),
 | 
			
		||||
        children: [
 | 
			
		||||
          {
 | 
			
		||||
            name: this._getTime(),
 | 
			
		||||
            children: [
 | 
			
		||||
              {
 | 
			
		||||
                name: this._getTime(),
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            name: this._getTime(),
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // this.treeData = newData;
 | 
			
		||||
    this._update37(this.treeData[0], newData[0]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _update37(oldTreeNode: any, newTreeNode: any) {
 | 
			
		||||
    debugger
 | 
			
		||||
    if (!newTreeNode) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (!oldTreeNode) {
 | 
			
		||||
      oldTreeNode = {name: "", children: []};
 | 
			
		||||
    }
 | 
			
		||||
    if (oldTreeNode.name !== newTreeNode.name) {
 | 
			
		||||
      oldTreeNode.name = newTreeNode.name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let oldChildren = oldTreeNode.children;
 | 
			
		||||
    let newChildren = newTreeNode.children;
 | 
			
		||||
 | 
			
		||||
    if (oldChildren.length === 0) {
 | 
			
		||||
      oldChildren = newChildren;
 | 
			
		||||
    } else {
 | 
			
		||||
      // 比较2个数据: treeData, newTreeData
 | 
			
		||||
      // 比较该层级的数据
 | 
			
		||||
      for (let i = 0; i < newChildren.length; i++) {
 | 
			
		||||
        let itemNew = newChildren[i];
 | 
			
		||||
        let itemOld = oldChildren[i];
 | 
			
		||||
        if (itemOld === undefined) {
 | 
			
		||||
          // 老节点中没有
 | 
			
		||||
          oldChildren.push(itemNew);
 | 
			
		||||
        } else if (itemNew.name !== itemOld.name) {
 | 
			
		||||
          // 替换
 | 
			
		||||
          oldChildren.splice(i, 1, itemNew);
 | 
			
		||||
        } else {
 | 
			
		||||
          this._update37(itemOld, itemNew);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      // 多余的删除了
 | 
			
		||||
      if (oldChildren.length > newChildren.length) {
 | 
			
		||||
        oldChildren.splice(newChildren.length, oldChildren.length - newChildren.length);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onBtnClickTest3() {
 | 
			
		||||
    // chrome.devtools.inspectedWindow.eval(`window.ccinspector.testMsg3()`)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onMemoryTest() {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,8 @@ if (chrome && chrome.devtools) {
 | 
			
		||||
  chrome.devtools.panels.create("Cocos", "icons/48.png", Manifest.devtools_page, (panel: chrome.devtools.panels.ExtensionPanel) => {
 | 
			
		||||
      console.log("[CC-Inspector] Dev Panel Created!");
 | 
			
		||||
      let conn = chrome.runtime.connect({name: PluginMsg.Page.DevToolsPanel});
 | 
			
		||||
      conn.onMessage.addListener(function (event, sender) {
 | 
			
		||||
        // debugger
 | 
			
		||||
      conn.onMessage.addListener((event, sender) => {
 | 
			
		||||
        console.log(`[Message] ${JSON.stringify(event)}`);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      panel.onShown.addListener((window) => {
 | 
			
		||||
@@ -27,7 +27,5 @@ if (chrome && chrome.devtools) {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
// eval 注入脚本的代码,变量尽量使用var,后来发现在import之后,let会自动变为var
 | 
			
		||||
const PluginMsg = require("./core/plugin-msg");
 | 
			
		||||
import {BoolData, ColorData, Group, NumberData, Property, StringData, Vec2Data, Vec3Data} from "./devtools/data";
 | 
			
		||||
 | 
			
		||||
let cc_inspector = {
 | 
			
		||||
  inspectorGameMemoryStorage: {},
 | 
			
		||||
@@ -22,19 +23,18 @@ let cc_inspector = {
 | 
			
		||||
      // }
 | 
			
		||||
    }.bind(this), 1000);
 | 
			
		||||
    // 注册cc_after_render事件
 | 
			
		||||
    window.addEventListener('message', function (event) {
 | 
			
		||||
    window.addEventListener("message", function (event) {
 | 
			
		||||
      if (event.data.msg === PluginMsg.Msg.UrlChange) {
 | 
			
		||||
        this.checkIsGamePage(true);
 | 
			
		||||
        let isCocosGame = this.checkIsGamePage();
 | 
			
		||||
      }
 | 
			
		||||
    }.bind(this));
 | 
			
		||||
  },
 | 
			
		||||
  updateTreeInfo() {
 | 
			
		||||
    let isCocosCreatorGame = this.checkIsGamePage(true);
 | 
			
		||||
    let isCocosCreatorGame = this.checkIsGamePage();
 | 
			
		||||
    if (isCocosCreatorGame) {
 | 
			
		||||
      let scene = cc.director.getScene();
 | 
			
		||||
      if (scene) {
 | 
			
		||||
        this.postData.scene = {
 | 
			
		||||
          type: 1,// 标识类型
 | 
			
		||||
        let sendData = {
 | 
			
		||||
          uuid: scene.uuid,
 | 
			
		||||
          name: scene.name,
 | 
			
		||||
          children: [],
 | 
			
		||||
@@ -43,40 +43,25 @@ let cc_inspector = {
 | 
			
		||||
        let sceneChildren = scene.getChildren();
 | 
			
		||||
        for (let i = 0; i < sceneChildren.length; i++) {
 | 
			
		||||
          let node = sceneChildren[i];
 | 
			
		||||
          this.getNodeChildren(node, this.postData.scene.children);
 | 
			
		||||
          this.getNodeChildren(node, sendData.children);
 | 
			
		||||
        }
 | 
			
		||||
        // console.log(postData);
 | 
			
		||||
        this.sendMsgToDevTools(PluginMsg.Msg.ListInfo, this.postData);
 | 
			
		||||
        this.sendMsgToDevTools(PluginMsg.Msg.ListInfo, sendData);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.postData.scene = null;
 | 
			
		||||
        this.sendMsgToDevTools(PluginMsg.Msg.Support, {support: false, msg: "未发现游戏场景,不支持调试游戏!"});
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  checkIsGamePage(isLog) {
 | 
			
		||||
    // 检测是否包含cc变量
 | 
			
		||||
    let isCocosCreatorGame = true;
 | 
			
		||||
    let msg = "支持调试游戏!";
 | 
			
		||||
    try {
 | 
			
		||||
      cc
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      isCocosCreatorGame = false;
 | 
			
		||||
      msg = "不支持调试游戏!";
 | 
			
		||||
    }
 | 
			
		||||
    this.sendMsgToDevTools(PluginMsg.Msg.Support, {support: isCocosCreatorGame, msg: msg, log: isLog});
 | 
			
		||||
    return isCocosCreatorGame;
 | 
			
		||||
  },
 | 
			
		||||
  testEval() {
 | 
			
		||||
    console.log("hello devtools eval")
 | 
			
		||||
  // 检测是否包含cc变量
 | 
			
		||||
  checkIsGamePage() {
 | 
			
		||||
    let isCocosGame = typeof cc !== "undefined";
 | 
			
		||||
    this.sendMsgToDevTools(PluginMsg.Msg.Support, {support: isCocosGame});
 | 
			
		||||
    return isCocosGame;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  testMsg2() {
 | 
			
		||||
    debugger
 | 
			
		||||
    chrome.runtime.connect({name: "inject"});
 | 
			
		||||
  },
 | 
			
		||||
  testMsg3() {
 | 
			
		||||
    debugger
 | 
			
		||||
    chrome.runtime.sendMessage("ffff");
 | 
			
		||||
  },
 | 
			
		||||
  // 收集组件信息
 | 
			
		||||
  getNodeComponentsInfo(node) {
 | 
			
		||||
    let ret = [];
 | 
			
		||||
@@ -130,42 +115,118 @@ let cc_inspector = {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  _getNodeKeys(node) {
 | 
			
		||||
    let keys = [];
 | 
			
		||||
    let excludeProperty = ["children", "quat"];
 | 
			
		||||
    for (let key in node) {
 | 
			
		||||
      if (!key.startsWith("_") &&
 | 
			
		||||
          !excludeProperty.includes(key) &&
 | 
			
		||||
          typeof node[key] !== "function") {
 | 
			
		||||
        keys.push(key);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return keys;
 | 
			
		||||
  },
 | 
			
		||||
  _getPairProperty(key) {
 | 
			
		||||
    let pairProperty = {
 | 
			
		||||
      rotation: ["rotationX", "rotationY"],
 | 
			
		||||
      anchor: ["anchorX", "anchorY"],
 | 
			
		||||
      size: ["width", "height"],
 | 
			
		||||
      position: ["x", "y", "z"],
 | 
			
		||||
      scale: ["scaleX", "scaleY", "scaleZ"],
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
    for (let value in pairProperty) {
 | 
			
		||||
      let pair = pairProperty[value];
 | 
			
		||||
      if (pair.includes(key)) {
 | 
			
		||||
        return {key: value, values: pair};
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  },
 | 
			
		||||
  _genInfoData(propertyValue) {
 | 
			
		||||
    let info = null;
 | 
			
		||||
    switch (typeof propertyValue) {
 | 
			
		||||
      case "boolean":
 | 
			
		||||
        info = new BoolData(propertyValue);
 | 
			
		||||
        break;
 | 
			
		||||
      case "number":
 | 
			
		||||
        info = new NumberData(propertyValue);
 | 
			
		||||
        break;
 | 
			
		||||
      case "string":
 | 
			
		||||
        info = new StringData(propertyValue);
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        if (Array.isArray(propertyValue)) {
 | 
			
		||||
 | 
			
		||||
        } else if (propertyValue instanceof cc.Color) {
 | 
			
		||||
          let hex = propertyValue.toHEX();
 | 
			
		||||
          info = new ColorData(`#${hex}`);
 | 
			
		||||
        } else {
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (!info) {
 | 
			
		||||
      console.error(`暂不支持的属性值`, propertyValue);
 | 
			
		||||
    }
 | 
			
		||||
    return info;
 | 
			
		||||
  },
 | 
			
		||||
  // 获取节点信息
 | 
			
		||||
  getNodeInfo(uuid) {
 | 
			
		||||
    debugger
 | 
			
		||||
    let node = this.inspectorGameMemoryStorage[uuid];
 | 
			
		||||
    if (node) {
 | 
			
		||||
      let nodeGroup = new Group("Node");
 | 
			
		||||
      let keys = this._getNodeKeys(node);
 | 
			
		||||
      for (let i = 0; i < keys.length; i++) {
 | 
			
		||||
        let key = keys[i];
 | 
			
		||||
        let propertyValue = node[key];
 | 
			
		||||
        let pair = this._getPairProperty(key);
 | 
			
		||||
        if (pair) {
 | 
			
		||||
          // 把这个成对的属性剔除掉
 | 
			
		||||
          pair.values.forEach(item => {
 | 
			
		||||
            let index = keys.findIndex(el => el === item);
 | 
			
		||||
            if (index !== -1) {
 | 
			
		||||
              keys.splice(index, 1);
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          // 序列化成对的属性
 | 
			
		||||
          let info = null;
 | 
			
		||||
          let pairValues = pair.values;
 | 
			
		||||
          if (pairValues.length === 2) {
 | 
			
		||||
            info = new Vec2Data();
 | 
			
		||||
          } else if (pairValues.length === 3) {
 | 
			
		||||
            info = new Vec3Data();
 | 
			
		||||
          }
 | 
			
		||||
          pairValues.forEach(el => {
 | 
			
		||||
            if (el in node) {
 | 
			
		||||
              let vecData = this._genInfoData(node[el]);
 | 
			
		||||
              if (vecData) {
 | 
			
		||||
                info.add(new Property(el, vecData));
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              console.warn(`属性异常,节点丢失属性: ${el},请检查 pairProperty的设置`);
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          if (info) {
 | 
			
		||||
            let property = new Property(pair.key, info);
 | 
			
		||||
            nodeGroup.addProperty(property);
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          let info = this._genInfoData(propertyValue);
 | 
			
		||||
          if (info) {
 | 
			
		||||
            nodeGroup.addProperty(new Property(key, info));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      let nodeComp = this.getNodeComponentsInfo(node);
 | 
			
		||||
      let nodeData = {
 | 
			
		||||
        type: node.constructor.name,
 | 
			
		||||
        uuid: node.uuid,
 | 
			
		||||
        name: node.name,
 | 
			
		||||
        x: node.x,
 | 
			
		||||
        y: node.y,
 | 
			
		||||
        zIndex: node.zIndex,
 | 
			
		||||
        childrenCount: node.childrenCount,
 | 
			
		||||
        children: [],
 | 
			
		||||
        width: node.width,
 | 
			
		||||
        height: node.height,
 | 
			
		||||
        color: node.color.toCSS(),
 | 
			
		||||
        opacity: node.opacity,
 | 
			
		||||
        rotation: node.rotation,
 | 
			
		||||
        rotationX: node.rotationX,
 | 
			
		||||
        rotationY: node.rotationY,
 | 
			
		||||
        anchorX: node.anchorX,
 | 
			
		||||
        anchorY: node.anchorY,
 | 
			
		||||
        scaleX: node.scaleX,
 | 
			
		||||
        scaleY: node.scaleY,
 | 
			
		||||
        skewX: node.skewX,
 | 
			
		||||
        skewY: node.skewY,
 | 
			
		||||
        components: nodeComp
 | 
			
		||||
      };
 | 
			
		||||
      let nodeType = node.constructor.name;
 | 
			
		||||
      if (nodeType === 'cc_Scene') {
 | 
			
		||||
 | 
			
		||||
      } else {
 | 
			
		||||
        nodeData.active = node.active;
 | 
			
		||||
      }
 | 
			
		||||
      this.sendMsgToDevTools(PluginMsg.Msg.NodeInfo, nodeData);
 | 
			
		||||
      this.sendMsgToDevTools(PluginMsg.Msg.NodeInfo, nodeGroup);
 | 
			
		||||
    } else {
 | 
			
		||||
      // 未获取到节点数据
 | 
			
		||||
      console.log("未获取到节点数据");
 | 
			
		||||
@@ -190,6 +251,7 @@ let cc_inspector = {
 | 
			
		||||
    data.push(nodeData);
 | 
			
		||||
  },
 | 
			
		||||
  sendMsgToDevTools(msg, data) {
 | 
			
		||||
    // 发送给content.js处理
 | 
			
		||||
    window.postMessage({msg: msg, data: data}, "*");
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@@ -207,7 +269,7 @@ let cc_inspector = {
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
window.ccinspector = window.ccinspector || cc_inspector;
 | 
			
		||||
window.ccinspector.init && window.ccinspector.init();// 执行初始化函数
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,13 +28,7 @@ module.exports = {
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  configureWebpack: {
 | 
			
		||||
    mode: "development",
 | 
			
		||||
    mode: "development",// production
 | 
			
		||||
    devtool: "#source-map",
 | 
			
		||||
    entry: {
 | 
			
		||||
      inject: Path.join(__dirname, "src/inject.js"),
 | 
			
		||||
    },
 | 
			
		||||
    plugins: [
 | 
			
		||||
      // new Copy([{src: "src/inject.js", dest: "js/inject.js"}]),
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user