BiliBili动态隐藏

根据Up主名称,在动态页进行筛选,隐藏屏蔽的Up主动态。

// ==UserScript==
// @name		BiliBili动态隐藏
// @author		Yiero
// @description		根据Up主名称,在动态页进行筛选,隐藏屏蔽的Up主动态。
// @version		1.4.5
// @namespace		https://github.com/AliubYiero/TamperMonkeyScripts
// @match		https://t.bilibili.com/*
// @icon		https://t.bilibili.com/favicon.ico
// @resource		layuiCss https://cdn.staticfile.org/layui/2.8.11/css/layui.min.css
// @license		GPL
// @grant		GM_registerMenuCommand
// @grant		GM_unregisterMenuCommand
// @grant		GM_addStyle
// @grant		GM_getResourceText
// @run-at		document-start
// @updateUrl		https://raw.githubusercontent.com/AliubYiero/TamperMonkeyScripts/master/dist/HideDynamic.js
// @downloadUrl		https://raw.githubusercontent.com/AliubYiero/TamperMonkeyScripts/master/dist/HideDynamic.js
// ==/UserScript==
GM_addStyle( GM_getResourceText( 'layuiCss' ) )

var __defProp = Object.defineProperty;
var __defNormalProp = ( obj, key, value ) => key in obj ? __defProp( obj, key, {
	enumerable: true,
	configurable: true,
	writable: true,
	value
} ) : obj[key] = value;
var __publicField = ( obj, key, value ) => {
	__defNormalProp( obj, typeof key !== "symbol" ? key + "" : key, value );
	return value;
};

function getElement( parent, selector, timeout = 0 ) {
	return new Promise( ( resolve ) => {
		let result = parent.querySelector( selector );
		if ( result ) {
			return resolve( result );
		}
		let timer;
		const mutationObserver = window.MutationObserver || window.WebkitMutationObserver || window.MozMutationObserver;
		if ( mutationObserver ) {
			const observer = new mutationObserver( ( mutations ) => {
				for ( let mutation of mutations ) {
					for ( let addedNode of mutation.addedNodes ) {
						if ( addedNode instanceof Element ) {
							result = addedNode.matches( selector ) ? addedNode : addedNode.querySelector( selector );
							if ( result ) {
								observer.disconnect();
								timer && clearTimeout( timer );
								return resolve( result );
							}
						}
					}
				}
			} );
			observer.observe( parent, {
				childList: true,
				subtree: true
			} );
			if ( timeout > 0 ) {
				timer = setTimeout( () => {
					observer.disconnect();
					return resolve( null );
				}, timeout );
			}
		}
		else {
			const listener = ( e ) => {
				if ( e.target instanceof Element ) {
					result = e.target.matches( selector ) ? e.target : e.target.querySelector( selector );
					if ( result ) {
						parent.removeEventListener( "DOMNodeInserted", listener, true );
						timer && clearTimeout( timer );
						return resolve( result );
					}
				}
			};
			parent.addEventListener( "DOMNodeInserted", listener, true );
			if ( timeout > 0 ) {
				timer = setTimeout( () => {
					parent.removeEventListener( "DOMNodeInserted", listener, true );
					return resolve( null );
				}, timeout );
			}
		}
	} );
}

const registerMenu = ( title, callback ) => {
	GM_registerMenuCommand( title, function () {
		callback();
	} );
};
const createElement = ( elementConfig ) => {
	const { tagName, className, id, innerHTML, innerText } = elementConfig;
	const element = document.createElement( tagName );
	if ( className && typeof className === "string" ) {
		element.classList.add( className );
	}
	else if ( className && Array.isArray( className ) ) {
		element.classList.add( ...className );
	}
	if ( id ) {
		element.id = id;
	}
	if ( innerHTML ) {
		element.innerHTML = innerHTML;
	}
	if ( innerText ) {
		element.innerText = innerText;
	}
	for ( let elementConfigKey in elementConfig ) {
		if ( [ "tagName", "className", "id", "innerHTML", "innerText" ].indexOf( elementConfigKey ) !== -1 ) {
			continue;
		}
		element.setAttribute( elementConfigKey, elementConfig[elementConfigKey] );
	}
	return element;
};
const addElementToDocument = ( element, cssString, fatherElement = document.body ) => {
	fatherElement.append( element );
	GM_addStyle( cssString );
};
const addStyle = ( cssString ) => {
	GM_addStyle( cssString );
};

class Info {
	constructor( projectName ) {
		// @ts-ignore
		__publicField( this, "projectName" );
		__publicField( this, "header" );
		this.projectName = projectName;
		this.header = `[${ projectName }]`;
	}
	
	log( ...msg ) {
		/* @__PURE__ */
		( () => {
		} )( ...this.contentInfo( ...msg ) );
	}
	
	info( ...msg ) {
		console.info( ...this.contentInfo( ...msg ) );
	}
	
	warn( ...msg ) {
		console.warn( ...this.contentInfo( ...msg ) );
	}
	
	error( ...msg ) {
		console.error( ...this.contentInfo( ...msg ) );
	}
	
	contentInfo( ...msg ) {
		return [ this.header, ...msg ];
	}
}

function importLayui() {
	let script = document.createElement( "script" );
	script.type = "text/javascript";
	script.src = "https://cdn.staticfile.org/layui/2.8.11/layui.min.js";
	document.head.appendChild( script );
}

class Sleep {
	/** 延时睡眠等待 */
	static time( delay = 1 ) {
		return new Promise( ( resolve ) => {
			setTimeout( resolve, delay * 1e3 );
		} );
	}
	
	/** 页面加载等待 */
	static windowLoad( delay = 0 ) {
		return new Promise( ( resolve ) => {
			window.addEventListener( "load", () => {
				setTimeout( resolve, delay * 1e3 );
			} );
		} );
	}
}

class Data {
	/** @param {array} newData */
	constructor( newData ) {
		/** @type {Map<string, {id: string, isBandLive: boolean, isBandDynamic: boolean, isBandVideo: boolean}>} */
		__publicField( this, "data" );
		__publicField( this, "originData" );
		if ( newData ) {
			this.setToLocalStorage( this.arrayToMap( newData ) );
		}
		this.getFromLocalStorage();
	}
	
	/** 防止多余数据提交到data中,进行重新赋值 */
	limitValue( obj ) {
		const { id, isBandLive, isBandDynamic, isBandVideo } = obj;
		return {
			id,
			isBandLive: isBandLive || false,
			isBandDynamic: isBandDynamic || false,
			isBandVideo: isBandVideo || false
		};
	}
	
	/** 添加一个对象 */
	set( newObj ) {
		if ( this.data.has( newObj.id ) ) {
			return;
		}
		const limitData = this.limitValue( newObj );
		this.data.set( newObj.id, limitData );
		this.setToLocalStorage();
		return limitData;
	}
	
	/** 更新其中一个对象 */
	update( newObj ) {
		const limitData = this.limitValue( newObj );
		this.data.set( newObj.id, limitData );
		this.setToLocalStorage();
	}
	
	/** 删除其中一个对象 */
	delete( newObjOrId ) {
		if ( typeof newObjOrId === "string" ) {
			this.data.delete( newObjOrId );
		}
		else {
			this.data.delete( newObjOrId.id );
		}
		this.setToLocalStorage();
	}
	
	/** 改变其中一个对象的键(删除原对象,建立新对象) */
	change( newObj, oldObjOrId ) {
		this.delete( oldObjOrId );
		this.set( this.limitValue( newObj ) );
		this.setToLocalStorage();
	}
	
	/** 从localStorage中获取data */
	getFromLocalStorage() {
		this.originData = JSON.parse( localStorage.getItem( "bandList" ) || "[]" );
		this.data = this.arrayToMap( this.originData );
	}
	
	/** 将data设置到localStorage中 */
	setToLocalStorage( value = this.data ) {
		localStorage.setItem( "bandList", JSON.stringify( this.mapToArray( value ) ) );
	}
	
	/**
	 * 数组转Map
	 * @param {{id: string, [propName: string]: any}[]} array
	 * @return {Map}
	 * */
	arrayToMap( array ) {
		const map = /* @__PURE__ */ new Map();
		array.forEach( ( value ) => {
			value.id = value.id.trim();
			map.set( value.id, value );
		} );
		return map;
	}
	
	/**
	 * Map转数组
	 * @param {Map} map
	 * @return {any[]}
	 * */
	mapToArray( map ) {
		const array = [];
		for ( let value of map.values() ) {
			array.push( value );
		}
		return array;
	}
}

class UiEvent {
	constructor( data ) {
		__publicField( this, "data" );
		__publicField( this, "treeTable" );
		__publicField( this, "domList" );
		this.data = data;
		this.treeTable = layui.treeTable;
		this.domList = {
			main: void 0
		};
		this.getMainDom();
	}
	
	/** 添加数据 */
	add( res ) {
		const addData = ( res2 ) => {
			res2 = res2.trim();
			if ( !res2 ) {
				return;
			}
			const newData = this.data.set( { id: res2 } );
			if ( !newData ) {
				return;
			}
			this.treeTable.addNodes( "table-bili-band-config", {
				index: 0,
				data: newData
			} );
			this.treeTable.reloadData( "table-bili-band-config" );
		};
		if ( res ) {
			addData( res );
			return;
		}
		layer.prompt( { title: "输入要屏蔽Up主名" }, ( res2, index ) => {
			layer.close( index );
			addData( res2 );
		} );
	}
	
	/**
	 * 删除当前行数据
	 * @param {Object} e layui数据对象
	 * */
	delete( e ) {
		this.data.delete( e.data );
		e.del();
		this.treeTable.reloadData( "table-bili-band-config" );
	}
	
	/** 更新当前行的数据
	 * @param {Object} originData
	 * @param {'dynamic' | 'video' | 'live'} type
	 * @param {number} index
	 * */
	update( originData, type, index ) {
		let newData = { ...originData };
		switch ( type ) {
			case "dynamic":
				newData.isBandDynamic = !originData.isBandDynamic;
				break;
			case "video":
				newData.isBandVideo = !originData.isBandVideo;
				break;
			case "live":
				newData.isBandLive = !originData.isBandLive;
				break;
		}
		this.treeTable.updateNode( "table-bili-band-config", index, newData );
		this.data.update( newData );
	}
	
	/** 改变Up主名称(因为需要改变键值,所以不能直接更新) */
	change( e ) {
		this.data.change( e.data, e.oldValue );
		this.treeTable.reloadData( "table-bili-band-config" );
	}
	
	/** 展示UI界面 */
	show() {
		if ( !this.domList.main ) {
			this.getMainDom();
		}
		const mainContainer = this.domList.main;
		if ( mainContainer.style.display === "none" ) {
			mainContainer.style.display = "block";
		}
		mainContainer.classList.remove( "layui-anim-fadeout", "hide" );
		mainContainer.classList.add( "layui-anim-fadein" );
	}
	
	/** 隐藏UI界面 */
	hide() {
		if ( !this.domList.main ) {
			this.getMainDom();
		}
		const mainContainer = this.domList.main;
		mainContainer.classList.remove( "layui-anim-fadein" );
		mainContainer.classList.add( "layui-anim-fadeout", "hide" );
	}
	
	getMainDom() {
		this.domList.main = document.querySelector( ".bili-band-config-container" );
	}
	
	submitSearch( input ) {
		const { value } = input;
		if ( !value.trim() ) {
			return false;
		}
		input.value = "";
		const newData = [];
		let isMatchValue = false;
		this.data.mapToArray( this.data.data ).forEach( ( bandData ) => {
			if ( bandData.id.match( value ) ) {
				isMatchValue = true;
				newData.unshift( bandData );
			}
			else {
				newData.push( bandData );
			}
		} );
		if ( !isMatchValue ) {
			layer.confirm( `没有搜索到UP主 [${ value }] ,是否新建UP主
(2s后自动关闭窗口)`, {
				time: 2e3,
				btn: [ "确认", "取消" ]
			}, ( index ) => {
				layer.close( index );
				this.add( value );
			} );
			return false;
		}
		this.treeTable.reloadData( "table-bili-band-config", {
			data: newData,
			page: {
				// 将当前页面设置到第一页,避免看不到搜索结果
				curr: 1
			}
		} );
	}
}

class ConfigUI {
	constructor() {
		// @ts-ignore
		__publicField( this, "data" );
		// @ts-ignore
		__publicField( this, "uiEvent" );
		importLayui();
		Sleep.windowLoad().then(
			() => {
				this.data = new Data();
				this.uiEvent = new UiEvent( this.data );
				this.createContainer();
				this.createElementEvent();
				this.show = () => {
					this.uiEvent.show();
				};
				this.hide = () => {
					this.uiEvent.hide();
				};
			}
		);
	}
	
	/** 创建UI界面框架 */
	createContainer() {
		const container = createElement( {
			tagName: "main",
			className: [ "bili-band-config-container", "layui-anim", "hide" ],
			innerHTML: `<table class="layui-anim-fadeout" id="ID-table-bili-band-config" lay-filter="show"></table>`
		} );
		addElementToDocument( container, `.bili-band-config-container {position: fixed; top: 0; left:calc(50% - 355px);background: #ffffff; z-index: 10003; width: 710px;}`, document.body );
	}
	
	/**
	 * 创建UI内容,创建UI事件
	 * */
	createElementEvent() {
		layui.use( "table", () => {
			const { treeTable, form } = layui;
			treeTable.render( {
				elem: "#ID-table-bili-band-config",
				id: "table-bili-band-config",
				cols: [ [
					{
						field: "index",
						title: "编号",
						type: "numbers",
						width: 60
					},
					{
						field: "id",
						title: "UP主",
						width: 200,
						sort: true,
						align: "center",
						edit: true
					},
					{
						field: "dynamic",
						title: "动态卡片",
						width: 110,
						sort: true,
						align: "center",
						templet: ( d ) => `<input type="checkbox" lay-skin="switch" lay-filter="toggleDynamicStatus" ${ d.isBandDynamic ? "checked" : "" }/>`
					},
					{
						field: "video",
						title: "视频卡片",
						width: 110,
						sort: true,
						align: "center",
						templet: ( d ) => `<input type="checkbox" lay-skin="switch" lay-filter="toggleVideoStatus" ${ d.isBandVideo ? "checked" : "" }/>`
					},
					{
						field: "live",
						title: "直播卡片",
						width: 110,
						sort: true,
						align: "center",
						templet: ( d ) => `<input type="checkbox" lay-skin="switch" lay-filter="toggleLiveStatus" ${ d.isBandLive ? "checked" : "" }/>`
					},
					{
						field: "delete",
						title: "操作",
						width: 110,
						sort: false,
						align: "center",
						fixed: "right",
						templet: () => `<button type="button" class="layui-btn layui-btn-sm layui-btn-danger layui-btn-radius" lay-event="delete">Delete</button>`
					}
				] ],
				data: this.data.originData,
				size: "lg",
				skin: "line",
				page: {
					layout: [ "prev", "page", "next", "count", "skip" ]
				},
				// 底部工具栏
				pagebar: `
					<div>
						<button type="button" class="layui-btn layui-btn-sm" lay-event="add"> Add </button>
						<button type="button" class="layui-btn layui-btn-sm" lay-event="addFromClipboard"> ReadClipboard </button>
						<button type="button" class="layui-btn layui-btn-sm layui-btn-warm" lay-event="close"> Close </button>
					</div>
				`,
				// 顶部工具栏
				toolbar: `
					<div>
						<form class="form-search" style="display: flex;">
							<input type="text" class="layui-input" style="width: 200px;" placeholder="输入需要搜索的UP主"/>
							<button type="button" lay-submit lay-filter="table-search" class="layui-btn" style="margin-left: 20px;">Search</button>
							<button type="button" lay-submit lay-filter="table-clear" class="layui-btn" style="margin-left: 20px;">Clear</button>
						</form>
					</div>
				`,
				width: 710,
				// 不开启顶部右侧默认工具栏
				defaultToolbar: ""
			} );
			treeTable.on( "tool(show)", ( e ) => {
				this.uiEvent.delete( e );
			} );
			treeTable.on( "edit(show)", ( e ) => {
				this.uiEvent.change( e );
			} );
			
			class Checkbox {
				constructor( input ) {
					__publicField( this, "input" );
					__publicField( this, "domList" );
					this.input = input;
					this.domList = {
						input: this.input,
						tr: this.getTrDom()
					};
				}
				
				getTrDom() {
					var _a, _b, _c;
					return ( _c = ( _b = ( _a = this.input ) == null ? void 0 : _a.parentElement ) == null ? void 0 : _b.parentElement ) == null ? void 0 : _c.parentElement;
				}
				
				getTableDataIndex() {
					return parseInt( this.domList.tr.dataset.index );
				}
				
				getTableData( index ) {
					index || ( index = this.getTableDataIndex() );
					return treeTable.getNodeDataByIndex( "table-bili-band-config", index );
				}
			}
			
			form.on( "switch(toggleDynamicStatus)", ( e ) => {
				const input = new Checkbox( e.elem );
				const index = input.getTableDataIndex();
				const data = input.getTableData( index );
				this.uiEvent.update( data, "dynamic", index );
			} );
			form.on( "switch(toggleVideoStatus)", ( e ) => {
				const input = new Checkbox( e.elem );
				const index = input.getTableDataIndex();
				const data = input.getTableData( index );
				this.uiEvent.update( data, "video", index );
			} );
			form.on( "switch(toggleLiveStatus)", ( e ) => {
				const input = new Checkbox( e.elem );
				const index = input.getTableDataIndex();
				const data = input.getTableData( index );
				this.uiEvent.update( data, "live", index );
			} );
			treeTable.on( "pagebar(show)", ( e ) => {
				const { event } = e;
				if ( ![ "add", "close", "addFromClipboard" ].includes( event ) ) {
					return;
				}
				if ( event === "add" ) {
					this.uiEvent.add();
					return;
				}
				if ( event === "addFromClipboard" ) {
					navigator.clipboard.readText().then(
						( res ) => {
							this.uiEvent.add( res );
						},
						( err ) => {
							layer.msg( err );
						}
					);
					return;
				}
				if ( event === "close" ) {
					this.uiEvent.hide();
					return;
				}
			} );
			form.on( "submit(table-search)", ( res ) => {
				const input = res.form.querySelector( "input" );
				this.uiEvent.submitSearch( input );
				return false;
			} );
			form.on( "submit(table-clear)", ( res ) => {
				res.form.querySelector( "input" ).value = "";
				return false;
			} );
			const domList = {
				form: document.querySelector( ".form-search" ),
				input: document.querySelector( ".form-search > input" )
			};
			domList.form.addEventListener( "submit", ( e ) => {
				e.preventDefault();
				this.uiEvent.submitSearch( domList.input );
			} );
		} );
	}
	
	// 向外暴露出show事件
	// 初始化声明show函数,具体内容会在构造函数定义
	show() {
	}
	
	// 向外暴露出hide事件
	// 初始化声明hide函数,具体内容会在构造函数定义
	hide() {
	}
}

( async () => {
	const print = new Info( "BiliBiliHideDynamic" );
	addStyle( `.hide {display: none !important}` );
	print.info( "引入UI" );
	const configUI = new ConfigUI();
	await Sleep.windowLoad();
	
	class Observer {
		constructor() {
		}
		
		async observe( observerSelectorList, callback ) {
			await getElement( document.body, observerSelectorList.waitLoadElementSelector );
			const observer2 = new MutationObserver( ( e ) => {
				e.forEach( ( record ) => {
					var _a;
					const item = record.addedNodes[0];
					if ( !item || !( ( _a = item == null ? void 0 : item.classList ) == null ? void 0 : _a.contains( observerSelectorList.filterToken || "" ) ) ) {
						return;
					}
					callback( item );
				} );
			} );
			/* @__PURE__ */
			( () => {
			} )( observerSelectorList.observeElementSelector );
			observer2.observe( document.querySelector( observerSelectorList.observeElementSelector ), {
				childList: true
			} );
		}
	}
	
	class BandEvent {
		constructor( bandList ) {
			__publicField( this, "bandList" );
			__publicField( this, "bandTypeList", [ "dynamic", "video" ] );
			this.bandList = bandList;
		}
		
		/** 判断当前动态的Up主是否在屏蔽列表中,如果是则隐藏 */
		band( item, upNameSelector, bandType ) {
			var _a;
			const upName = ( _a = item.querySelector( upNameSelector ) ) == null ? void 0 : _a.innerText.trim();
			let bandTypeKey;
			bandTypeKey = {
				dynamic: "isBandDynamic",
				video: "isBandVideo",
				live: "isBandLive"
			};
			if ( this.bandList.has( upName ) && this.bandList.get( upName )[bandTypeKey[bandType]] ) {
				item.classList.add( "hide" );
			}
			else {
				item.classList.remove( "hide" );
			}
		}
		
		/**
		 * 获取当前所处的动态卡片编号,并根据动态卡片编号,返回不同的BandType
		 * */
		judgeBandType( tabsItemListSelector ) {
			let tabsItemIndex = 0;
			const tabsItemList = document.querySelectorAll( tabsItemListSelector );
			for ( let i = 0; i < tabsItemList.length; i++ ) {
				const tabsItem = tabsItemList[i];
				if ( tabsItem.classList.contains( "active" ) ) {
					tabsItemIndex = i;
					break;
				}
			}
			return this.bandTypeList[tabsItemIndex];
		}
		
		/** 刷新页面数据 */
		fresh( observerSelectorList, bandType ) {
			const { aimElementSelector, upNameSelector } = observerSelectorList;
			const upNameNodeList = document.querySelectorAll( aimElementSelector );
			upNameNodeList.forEach( ( aimElement ) => {
				this.band( aimElement, upNameSelector, bandType );
			} );
		}
		
		// 刷新动态卡片 / 视频卡片数据
		freshDynamic( dynamicSelectorList ) {
			const bandType = this.judgeBandType( dynamicSelectorList.tabsItemListSelector );
			bandEvent.fresh( dynamicSelectorList, bandType );
		}
		
		// 刷新直播数据
		freshLive( liveSelectorList ) {
			bandEvent.fresh( liveSelectorList, "live" );
		}
	}
	
	let domSelector = {
		dynamic: {
			waitLoadElementSelector: ".bili-dyn-list",
			observeElementSelector: ".bili-dyn-list__items",
			upNameSelector: ".bili-dyn-title__text",
			tabsItemListSelector: ".bili-dyn-list-tabs__item",
			filterToken: "bili-dyn-list__item",
			aimElementSelector: ".bili-dyn-list__item"
		},
		// Bilibili-Evolved直播侧边栏
		live: {
			waitLoadElementSelector: ".be-live-list-content",
			observeElementSelector: ".be-live-list-content",
			upNameSelector: ".be-live-list-item-user",
			filterToken: "be-live-list-item",
			aimElementSelector: ".be-live-list-item"
		},
		// 旧版直播侧边栏
		oldLive: {
			waitLoadElementSelector: ".bili-dyn-live-users__body",
			observeElementSelector: ".bili-dyn-live-users__body",
			upNameSelector: ".bili-dyn-live-users__item__uname",
			filterToken: "bili-dyn-live-users__item",
			aimElementSelector: ".bili-dyn-live-users__item"
		},
		// 新版直播侧边栏
		newLive: {
			waitLoadElementSelector: ".bili-dyn-live-users__body",
			observeElementSelector: ".bili-dyn-live-users__body",
			upNameSelector: ".bili-dyn-live-users__item__uname",
			filterToken: "bili-dyn-live-users__container",
			aimElementSelector: ".bili-dyn-live-users__container"
		}
	};
	( function judgeBiliUiVersion() {
		if ( document.querySelector( "#bili-header-container" ) && document.querySelector( ".bili-header.fixed-header" ) ) {
			domSelector.live = domSelector.newLive;
		}
		else if ( document.querySelector( "#bili-header-container" ) ) {
			;
		}
		else {
			domSelector.live = domSelector.oldLive;
		}
	} )();
	const observer = new Observer();
	const bandEvent = new BandEvent( configUI.data.data );
	await observer.observe( domSelector.dynamic, ( item ) => {
		const bandType = bandEvent.judgeBandType( domSelector.dynamic.tabsItemListSelector );
		bandEvent.band( item, domSelector.dynamic.upNameSelector, bandType );
	} );
	bandEvent.freshDynamic( domSelector.dynamic );
	await observer.observe( domSelector.live, ( item ) => {
		bandEvent.band( item, domSelector.live.upNameSelector, "live" );
	} );
	bandEvent.freshLive( domSelector.live );
	registerMenu( "添加屏蔽", () => {
		configUI.show();
	} );
	
	class DragEvent {
		constructor() {
			// Dom集合
			__publicField( this, "domList" );
			// 坐标集合
			__publicField( this, "position" );
			this.domList = {
				main: document.querySelector( ".bili-band-config-container" )
			};
			this.domList.tool = this.domList.main.querySelector( ".layui-table-tool" );
			this.position = {
				start: { x: 0, y: 0 },
				end: { x: 0, y: 0 },
				relative: { x: 0, y: 0 }
			};
			this.bindDragEvent( this.domList.tool );
		}
		
		/** 绑定拖拽事件 */
		bindDragEvent( willDraggableElement ) {
			willDraggableElement.draggable = true;
			willDraggableElement.addEventListener( "dragstart", ( e ) => {
				this.getStartPosition( e );
			} );
			willDraggableElement.addEventListener( "dragend", ( e ) => {
				e.preventDefault();
				this.getEndPosition( e );
				this.getRelativePosition();
				this.changeDomPosition();
			} );
		}
		
		/** 获取开始坐标 */
		getStartPosition( e ) {
			const { pageX, pageY } = e;
			this.position.start = {
				x: pageX,
				y: pageY
			};
		}
		
		/** 获取结束坐标 */
		getEndPosition( e ) {
			const { pageX, pageY } = e;
			this.position.end = {
				x: pageX,
				y: pageY
			};
		}
		
		/** 获取相对坐标 */
		getRelativePosition() {
			this.position.relative = {
				x: this.position.relative.x + this.position.end.x - this.position.start.x,
				y: this.position.relative.y + this.position.end.y - this.position.start.y
			};
		}
		
		/** 改变main容器的坐标位置 */
		changeDomPosition() {
			this.domList.main.style.transform = `translate(${ this.position.relative.x }px, ${ this.position.relative.y }px)`;
		}
	}
	
	( () => {
		const domList = {
			closeBtn: document.querySelector( "[lay-event=close]" )
		};
		domList.closeBtn.addEventListener( "click", () => {
			bandEvent.freshDynamic( domSelector.dynamic );
			bandEvent.freshLive( domSelector.live );
		} );
		new DragEvent();
	} )();
} )();