awesome.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /**
  2. * 工具更新
  3. * @type {{download}}
  4. */
  5. import toolMap from './tools.js';
  6. let Awesome = (() => {
  7. let manifest = chrome.runtime ? chrome.runtime.getManifest() : {};
  8. const SERVER_SITE = manifest.homepage_url;
  9. const URL_TOOL_TPL = `${SERVER_SITE}/#TOOL-NAME#/index.html`;
  10. const TOOL_NAME_TPL = 'DYNAMIC_TOOL:#TOOL-NAME#';
  11. const TOOL_CONTENT_SCRIPT_TPL = 'DYNAMIC_TOOL:CS:#TOOL-NAME#';
  12. const TOOL_CONTENT_SCRIPT_CSS_TPL = 'DYNAMIC_TOOL:CS:CSS:#TOOL-NAME#';
  13. const TOOL_MENU_TPL = 'DYNAMIC_MENU:#TOOL-NAME#';
  14. /**
  15. * 管理本地存储
  16. */
  17. let StorageMgr = (() => {
  18. let get = keyArr => {
  19. return new Promise((resolve, reject) => {
  20. chrome.storage.local.get(keyArr, result => {
  21. resolve(typeof keyArr === 'string' ? result[keyArr] : result);
  22. });
  23. });
  24. };
  25. let getSync = async (keyArr) => {
  26. return await (new Promise((resolve, reject) => {
  27. chrome.storage.local.get(keyArr, result => {
  28. resolve(typeof keyArr === 'string' ? result[keyArr] : result);
  29. });
  30. }));
  31. };
  32. let set = (items, values) => {
  33. return new Promise((resolve, reject) => {
  34. if (typeof items === 'string') {
  35. let tmp = {};
  36. tmp[items] = values;
  37. items = tmp;
  38. }
  39. chrome.storage.local.set(items, () => {
  40. resolve();
  41. });
  42. });
  43. };
  44. let remove = keyArr => {
  45. return new Promise((resolve, reject) => {
  46. keyArr = [].concat(keyArr);
  47. chrome.storage.local.remove(keyArr, () => {
  48. resolve();
  49. });
  50. });
  51. };
  52. return {get, set, remove,getSync};
  53. })();
  54. /**
  55. * 检测工具是否已被成功安装
  56. * @param toolName 工具名称
  57. * @param detectMenu 是否进一步检测Menu的设置情况
  58. * @returns {Promise}
  59. */
  60. let detectInstall = (toolName, detectMenu) => {
  61. let menuKey = TOOL_MENU_TPL.replace('#TOOL-NAME#', toolName);
  62. let toolKey = TOOL_NAME_TPL.replace('#TOOL-NAME#', toolName);
  63. return Promise.all([StorageMgr.get(toolKey), StorageMgr.get(menuKey)]).then(values => {
  64. let toolInstalled = !!values[0];
  65. // 系统预置的功能,是强制 installed 状态的
  66. if(toolMap[toolName] && toolMap[toolName].systemInstalled) {
  67. toolInstalled = true;
  68. }
  69. if (detectMenu) {
  70. return toolInstalled && String(values[1]) === '1';
  71. }
  72. return toolInstalled;
  73. });
  74. };
  75. let log = (txt) => {
  76. // console.log(String(new Date(new Date() * 1 - (new Date().getTimezoneOffset()) * 60 * 1000).toJSON()).replace(/T/i, ' ').replace(/Z/i, '') + '>', txt);
  77. };
  78. /**
  79. * 安装/更新工具,支持显示安装进度
  80. * @param toolName
  81. * @param fnProgress
  82. * @returns {Promise<any>}
  83. */
  84. let install = (toolName, fnProgress) => {
  85. return new Promise((resolve, reject) => {
  86. // 存储html文件
  87. StorageMgr.set(TOOL_NAME_TPL.replace('#TOOL-NAME#', toolName), '&nbsp;');
  88. log(toolName + '工具html模板安装/更新成功!');
  89. resolve();
  90. });
  91. };
  92. let offLoad = (toolName) => {
  93. let items = [];
  94. items.push(TOOL_NAME_TPL.replace('#TOOL-NAME#', toolName));
  95. items.push(TOOL_CONTENT_SCRIPT_TPL.replace('#TOOL-NAME#', toolName));
  96. items.push(TOOL_CONTENT_SCRIPT_CSS_TPL.replace('#TOOL-NAME#', toolName));
  97. // 删除所有静态文件
  98. chrome.storage.local.get(null, allDatas => {
  99. StorageMgr.remove(Object.keys(allDatas).filter(key => String(key).startsWith(`../${toolName}/`)));
  100. });
  101. log(toolName + ' 卸载成功!');
  102. return StorageMgr.remove(items);
  103. };
  104. /**
  105. * 有些工具其实已经卸载过了,但是本地还有冗余的静态文件,都需要统一清理一遍
  106. */
  107. let gcLocalFiles = () => getAllTools().then(tools => Object.keys(tools).forEach(tool => {
  108. if (!tools[tool] || !tools[tool]._devTool && !tools[tool].installed) {
  109. offLoad(tool);
  110. }
  111. }));
  112. let getAllTools = async () => {
  113. // 获取本地开发的插件,也拼接进来
  114. try {
  115. const DEV_TOOLS_MY_TOOLS = 'DEV-TOOLS:MY-TOOLS';
  116. let _tools = await StorageMgr.getSync(DEV_TOOLS_MY_TOOLS);
  117. let localDevTools = JSON.parse(_tools || '{}');
  118. Object.keys(localDevTools).forEach(tool => {
  119. toolMap[tool] = localDevTools[tool];
  120. });
  121. } catch (e) {
  122. }
  123. let tools = Object.keys(toolMap);
  124. let promises = [];
  125. tools.forEach(tool => {
  126. promises = promises.concat([detectInstall(tool), detectInstall(tool, true)])
  127. });
  128. let pAll = Promise.all(promises).then(values => {
  129. values.forEach((v, i) => {
  130. let tool = tools[Math.floor(i / 2)];
  131. let key = i % 2 === 0 ? 'installed' : 'menu';
  132. toolMap[tool][key] = v;
  133. // 本地工具,还需要看是否处于开启状态
  134. if (toolMap[tool].hasOwnProperty('_devTool')) {
  135. toolMap[tool][key] = toolMap[tool][key] && toolMap[tool]._enable;
  136. }
  137. });
  138. return toolMap;
  139. });
  140. let pSort = SortToolMgr.get();
  141. return Promise.all([pAll,pSort]).then(vs => {
  142. let allTools = vs[0];
  143. let sortTools = vs[1];
  144. if (sortTools && sortTools.length) {
  145. let map = {};
  146. sortTools.forEach(tool => {
  147. if(allTools[tool]) {
  148. map[tool] = allTools[tool];
  149. }
  150. });
  151. Object.keys(allTools).forEach(tool => {
  152. if (!map[tool]) {
  153. map[tool] = allTools[tool];
  154. }
  155. });
  156. return map;
  157. }else{
  158. return allTools;
  159. }
  160. });
  161. };
  162. /**
  163. * 检查看本地已安装过哪些工具
  164. * @returns {Promise}
  165. */
  166. let getInstalledTools = () => getAllTools().then(tools => {
  167. let istTolls = {};
  168. Object.keys(tools).filter(tool => {
  169. if (tools[tool] && tools[tool].installed) {
  170. istTolls[tool] = tools[tool];
  171. }
  172. });
  173. return istTolls;
  174. });
  175. /**
  176. * 获取工具的content-script
  177. * @param toolName
  178. * @param cssMode
  179. */
  180. let getContentScript = (toolName, cssMode) => {
  181. return StorageMgr.get(cssMode ? TOOL_CONTENT_SCRIPT_CSS_TPL.replace('#TOOL-NAME#', toolName)
  182. : TOOL_CONTENT_SCRIPT_TPL.replace('#TOOL-NAME#', toolName));
  183. };
  184. /**
  185. * 获取工具的html模板
  186. * @param toolName
  187. * @returns {*}
  188. */
  189. let getToolTpl = (toolName) => StorageMgr.get(TOOL_NAME_TPL.replace('#TOOL-NAME#', toolName));
  190. /**
  191. * 从服务器检查,看本地已安装的工具,有哪些又已经升级过了
  192. * @param tool
  193. */
  194. let checkUpgrade = (tool) => {
  195. let getOnline = (toolName) => fetch(URL_TOOL_TPL.replace('#TOOL-NAME#', toolName)).then(resp => resp.text());
  196. let getOffline = (toolName) => StorageMgr.get(TOOL_NAME_TPL.replace('#TOOL-NAME#', toolName));
  197. return Promise.all([getOnline(tool), getOffline(tool)]).then(values => {
  198. let onlineData = _tplHandler(tool, values[0]);
  199. let local = values[1];
  200. return local !== onlineData.html;
  201. });
  202. };
  203. /**
  204. * 管理右键菜单
  205. * @param toolName
  206. * @param action 具体动作install/offload/get
  207. * @returns {Promise<any>}
  208. */
  209. let menuMgr = (toolName, action) => {
  210. let menuKey = TOOL_MENU_TPL.replace('#TOOL-NAME#', toolName);
  211. switch (action) {
  212. case 'get':
  213. return StorageMgr.get(menuKey);
  214. case 'offload':
  215. // 必须用setItem模式,而不是removeItem,要处理 0/1/null三种结果
  216. log(toolName + ' 卸载成功!');
  217. return StorageMgr.set(menuKey, 0);
  218. case 'install':
  219. log(toolName + ' 安装成功!');
  220. return StorageMgr.set(menuKey, 1);
  221. }
  222. };
  223. /**
  224. * 远程获取的代码管理器
  225. * @type {{get, set}}
  226. */
  227. let CodeCacheMgr = (() => {
  228. const TOOLS_FROM_REMOTE = 'TOOLS_FROM_REMOTE';
  229. let get = () => {
  230. return StorageMgr.getSync(TOOLS_FROM_REMOTE);
  231. };
  232. let set = (remoteCodes) => {
  233. let obj = {};
  234. obj[TOOLS_FROM_REMOTE]=remoteCodes;
  235. chrome.storage.local.set(obj);
  236. };
  237. return {get, set};
  238. })();
  239. /**
  240. * 工具排序管理器
  241. * @type {{get, set}}
  242. */
  243. let SortToolMgr = (() => {
  244. const TOOLS_CUSTOM_SORT = 'TOOLS_CUSTOM_SORT';
  245. let get = async () => {
  246. let cache = await StorageMgr.getSync(TOOLS_CUSTOM_SORT);
  247. return [].concat(JSON.parse(cache || '[]')).filter(t => !!t);
  248. };
  249. let set = (newSortArray) => {
  250. let obj = {};
  251. obj[TOOLS_CUSTOM_SORT] = JSON.stringify([].concat(newSortArray || []).filter(t => !!t));
  252. chrome.storage.local.set(obj);
  253. };
  254. return {get, set};
  255. })();
  256. /**
  257. * 采集客户端信息并发送给background
  258. */
  259. let collectAndSendClientInfo = () => {
  260. try {
  261. const nav = navigator;
  262. const screenInfo = window.screen;
  263. const lang = nav.language || nav.userLanguage || '';
  264. const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || '';
  265. const ua = nav.userAgent;
  266. const platform = nav.platform;
  267. const vendor = nav.vendor;
  268. const colorDepth = screenInfo.colorDepth;
  269. const screenWidth = screenInfo.width;
  270. const screenHeight = screenInfo.height;
  271. const deviceMemory = nav.deviceMemory || '';
  272. const hardwareConcurrency = nav.hardwareConcurrency || '';
  273. const connection = nav.connection || nav.mozConnection || nav.webkitConnection || {};
  274. const screenOrientation = screenInfo.orientation ? screenInfo.orientation.type : '';
  275. const touchSupport = ('ontouchstart' in window) || (nav.maxTouchPoints > 0);
  276. let memoryJSHeapSize = '';
  277. if (window.performance && window.performance.memory) {
  278. memoryJSHeapSize = window.performance.memory.jsHeapSizeLimit;
  279. }
  280. const clientInfo = {
  281. language: lang,
  282. timezone,
  283. userAgent: ua,
  284. platform,
  285. vendor,
  286. colorDepth,
  287. screenWidth,
  288. screenHeight,
  289. deviceMemory,
  290. hardwareConcurrency,
  291. networkType: connection.effectiveType || '',
  292. downlink: connection.downlink || '',
  293. rtt: connection.rtt || '',
  294. online: nav.onLine,
  295. touchSupport,
  296. cookieEnabled: nav.cookieEnabled,
  297. doNotTrack: nav.doNotTrack,
  298. appVersion: nav.appVersion,
  299. appName: nav.appName,
  300. product: nav.product,
  301. vendorSub: nav.vendorSub,
  302. screenOrientation,
  303. memoryJSHeapSize
  304. };
  305. chrome.runtime.sendMessage({ type: 'clientInfo', data: clientInfo });
  306. } catch (e) {
  307. // 忽略采集异常
  308. }
  309. };
  310. return {
  311. StorageMgr,
  312. detectInstall,
  313. install,
  314. offLoad,
  315. getInstalledTools,
  316. menuMgr,
  317. checkUpgrade,
  318. getContentScript,
  319. getToolTpl,
  320. gcLocalFiles,
  321. getAllTools,
  322. SortToolMgr,
  323. CodeCacheMgr,
  324. collectAndSendClientInfo
  325. }
  326. })();
  327. export default Awesome;