[SNOLAB] Google Calendar keyboard enhance

^】Google日历键盘增强,雪星自用,功能:双击复制日程视图里的文本内容, Alt+hjkl 移动日程

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
  1. // ==UserScript==
  2. // @name [SNOLAB] Google Calendar keyboard enhance
  3. // @name:zh [SNOLAB] Google 日历键盘操作增强
  4. // @namespace https://userscript.snomiao.com/
  5. // @version 0.1.0
  6. // @description ^】Google日历键盘增强,雪星自用,功能:双击复制日程视图里的文本内容, Alt+hjkl 移动日程
  7. // @author snomiao@gmail.com
  8. // @match *://calendar.google.com/*
  9. // @grant none
  10. // ==/UserScript==
  11. //
  12. // 1. event move enhance
  13. // - date time input change
  14. // - event drag
  15. // 2. journal view text copy for the day-summary
  16. //
  17.  
  18. /* eslint-disable */
  19.  
  20. // clipboardy/browser
  21. var clipboard = {};
  22. clipboard.write = async (text) => {
  23. await navigator.clipboard.writeText(text);
  24. };
  25. clipboard.read = async () => navigator.clipboard.readText();
  26. clipboard.readSync = () => {
  27. throw new Error("`.readSync()` is not supported in browsers!");
  28. };
  29. clipboard.writeSync = () => {
  30. throw new Error("`.writeSync()` is not supported in browsers!");
  31. };
  32. var browser_default = clipboard;
  33.  
  34. // hotkey-mapper
  35. var { keys } = Object;
  36. function mapObject(fn, obj) {
  37. if (arguments.length === 1) {
  38. return (_obj) => mapObject(fn, _obj);
  39. }
  40. let index = 0;
  41. const objKeys = keys(obj);
  42. const len = objKeys.length;
  43. const willReturn = {};
  44. while (index < len) {
  45. const key = objKeys[index];
  46. willReturn[key] = fn(
  47. obj[key],
  48. key,
  49. obj
  50. );
  51. index++;
  52. }
  53. return willReturn;
  54. }
  55. var mapObjIndexed = mapObject;
  56. function hotkeyMapper(mapping, on = "keydown", options) {
  57. const handler = (event) => {
  58. const mainKey = `${event.code.replace(/^Key/, "").toLowerCase()}Key`;
  59. event[mainKey] = true;
  60. const mods = "meta+alt+shift+ctrl";
  61. mapObjIndexed((fn, hotkey) => {
  62. const conds = `${mods}+${hotkey.toLowerCase()}`.replace(/win|command|search/, "meta").replace(/control/, "ctrl").split("+").map((k, i) => [k, Boolean(i >= 4) === Boolean(event[`${k}Key`])]);
  63. if (!Object.entries(Object.fromEntries(conds)).every(([, ok]) => ok))
  64. return;
  65. event.stopPropagation(), event.preventDefault();
  66. return fn(event);
  67. }, mapping);
  68. };
  69. window.addEventListener(on, handler, options);
  70. return function unload() {
  71. window.removeEventListener(on, handler, options);
  72. };
  73. }
  74.  
  75. // rambda
  76. var cloneList = (list) => Array.prototype.slice.call(list);
  77. function curry(fn, args = []) {
  78. return (..._args) => ((rest) => rest.length >= fn.length ? fn(...rest) : curry(fn, rest))([...args, ..._args]);
  79. }
  80. function adjustFn(index, replaceFn2, list) {
  81. const actualIndex = index < 0 ? list.length + index : index;
  82. if (index >= list.length || actualIndex < 0)
  83. return list;
  84. const clone = cloneList(list);
  85. clone[actualIndex] = replaceFn2(clone[actualIndex]);
  86. return clone;
  87. }
  88. var adjust = curry(adjustFn);
  89. function always(x) {
  90. return (_) => x;
  91. }
  92. var {
  93. isArray
  94. } = Array;
  95. function assocFn(prop2, newValue, obj) {
  96. return Object.assign({}, obj, {
  97. [prop2]: newValue
  98. });
  99. }
  100. var assoc = curry(assocFn);
  101. function _isInteger(n) {
  102. return n << 0 === n;
  103. }
  104. var isInteger = Number.isInteger || _isInteger;
  105. function assocPathFn(path2, newValue, input) {
  106. const pathArrValue = typeof path2 === "string" ? path2.split(".").map((x) => isInteger(Number(x)) ? Number(x) : x) : path2;
  107. if (pathArrValue.length === 0) {
  108. return newValue;
  109. }
  110. const index = pathArrValue[0];
  111. if (pathArrValue.length > 1) {
  112. const condition = typeof input !== "object" || input === null || !input.hasOwnProperty(index);
  113. const nextInput = condition ? isInteger(pathArrValue[1]) ? [] : {} : input[index];
  114. newValue = assocPathFn(Array.prototype.slice.call(pathArrValue, 1), newValue, nextInput);
  115. }
  116. if (isInteger(index) && isArray(input)) {
  117. const arr = cloneList(input);
  118. arr[index] = newValue;
  119. return arr;
  120. }
  121. return assoc(index, newValue, input);
  122. }
  123. var assocPath = curry(assocPathFn);
  124. function clampFn(min, max, input) {
  125. if (min > max) {
  126. throw new Error("min must not be greater than max in clamp(min, max, value)");
  127. }
  128. if (input >= min && input <= max)
  129. return input;
  130. if (input > max)
  131. return max;
  132. if (input < min)
  133. return min;
  134. }
  135. var clamp = curry(clampFn);
  136. var ReduceStopper = class {
  137. constructor(value) {
  138. this.value = value;
  139. }
  140. };
  141. function reduceFn(reducer, acc, list) {
  142. if (!isArray(list)) {
  143. throw new TypeError("reduce: list must be array or iterable");
  144. }
  145. let index = 0;
  146. const len = list.length;
  147. while (index < len) {
  148. acc = reducer(acc, list[index], index, list);
  149. if (acc instanceof ReduceStopper) {
  150. return acc.value;
  151. }
  152. index++;
  153. }
  154. return acc;
  155. }
  156. var reduce = curry(reduceFn);
  157. var {
  158. keys: keys$1
  159. } = Object;
  160. function isFalsy(input) {
  161. return input === void 0 || input === null || Number.isNaN(input) === true;
  162. }
  163. function defaultTo(defaultArgument, input) {
  164. if (arguments.length === 1) {
  165. return (_input) => defaultTo(defaultArgument, _input);
  166. }
  167. return isFalsy(input) ? defaultArgument : input;
  168. }
  169. function type(input) {
  170. if (input === null) {
  171. return "Null";
  172. } else if (input === void 0) {
  173. return "Undefined";
  174. } else if (Number.isNaN(input)) {
  175. return "NaN";
  176. }
  177. const typeResult = Object.prototype.toString.call(input).slice(8, -1);
  178. return typeResult === "AsyncFunction" ? "Promise" : typeResult;
  179. }
  180. function _indexOf(valueToFind, list) {
  181. if (!isArray(list)) {
  182. throw new Error(`Cannot read property 'indexOf' of ${list}`);
  183. }
  184. const typeOfValue = type(valueToFind);
  185. if (!["Object", "Array", "NaN", "RegExp"].includes(typeOfValue))
  186. return list.indexOf(valueToFind);
  187. let index = -1;
  188. let foundIndex = -1;
  189. const {
  190. length
  191. } = list;
  192. while (++index < length && foundIndex === -1) {
  193. if (equals(list[index], valueToFind)) {
  194. foundIndex = index;
  195. }
  196. }
  197. return foundIndex;
  198. }
  199. function _arrayFromIterator(iter) {
  200. const list = [];
  201. let next;
  202. while (!(next = iter.next()).done) {
  203. list.push(next.value);
  204. }
  205. return list;
  206. }
  207. function _equalsSets(a, b) {
  208. if (a.size !== b.size) {
  209. return false;
  210. }
  211. const aList = _arrayFromIterator(a.values());
  212. const bList = _arrayFromIterator(b.values());
  213. const filtered = aList.filter((aInstance) => _indexOf(aInstance, bList) === -1);
  214. return filtered.length === 0;
  215. }
  216. function parseError(maybeError) {
  217. const typeofError = maybeError.__proto__.toString();
  218. if (!["Error", "TypeError"].includes(typeofError))
  219. return [];
  220. return [typeofError, maybeError.message];
  221. }
  222. function parseDate(maybeDate) {
  223. if (!maybeDate.toDateString)
  224. return [false];
  225. return [true, maybeDate.getTime()];
  226. }
  227. function parseRegex(maybeRegex) {
  228. if (maybeRegex.constructor !== RegExp)
  229. return [false];
  230. return [true, maybeRegex.toString()];
  231. }
  232. function equals(a, b) {
  233. if (arguments.length === 1)
  234. return (_b) => equals(a, _b);
  235. const aType = type(a);
  236. if (aType !== type(b))
  237. return false;
  238. if (aType === "Function") {
  239. return a.name === void 0 ? false : a.name === b.name;
  240. }
  241. if (["NaN", "Undefined", "Null"].includes(aType))
  242. return true;
  243. if (aType === "Number") {
  244. if (Object.is(-0, a) !== Object.is(-0, b))
  245. return false;
  246. return a.toString() === b.toString();
  247. }
  248. if (["String", "Boolean"].includes(aType)) {
  249. return a.toString() === b.toString();
  250. }
  251. if (aType === "Array") {
  252. const aClone = Array.from(a);
  253. const bClone = Array.from(b);
  254. if (aClone.toString() !== bClone.toString()) {
  255. return false;
  256. }
  257. let loopArrayFlag = true;
  258. aClone.forEach((aCloneInstance, aCloneIndex) => {
  259. if (loopArrayFlag) {
  260. if (aCloneInstance !== bClone[aCloneIndex] && !equals(aCloneInstance, bClone[aCloneIndex])) {
  261. loopArrayFlag = false;
  262. }
  263. }
  264. });
  265. return loopArrayFlag;
  266. }
  267. const aRegex = parseRegex(a);
  268. const bRegex = parseRegex(b);
  269. if (aRegex[0]) {
  270. return bRegex[0] ? aRegex[1] === bRegex[1] : false;
  271. } else if (bRegex[0])
  272. return false;
  273. const aDate = parseDate(a);
  274. const bDate = parseDate(b);
  275. if (aDate[0]) {
  276. return bDate[0] ? aDate[1] === bDate[1] : false;
  277. } else if (bDate[0])
  278. return false;
  279. const aError = parseError(a);
  280. const bError = parseError(b);
  281. if (aError[0]) {
  282. return bError[0] ? aError[0] === bError[0] && aError[1] === bError[1] : false;
  283. }
  284. if (aType === "Set") {
  285. return _equalsSets(a, b);
  286. }
  287. if (aType === "Object") {
  288. const aKeys = Object.keys(a);
  289. if (aKeys.length !== Object.keys(b).length) {
  290. return false;
  291. }
  292. let loopObjectFlag = true;
  293. aKeys.forEach((aKeyInstance) => {
  294. if (loopObjectFlag) {
  295. const aValue = a[aKeyInstance];
  296. const bValue = b[aKeyInstance];
  297. if (aValue !== bValue && !equals(aValue, bValue)) {
  298. loopObjectFlag = false;
  299. }
  300. }
  301. });
  302. return loopObjectFlag;
  303. }
  304. return false;
  305. }
  306. function prop(propToFind, obj) {
  307. if (arguments.length === 1)
  308. return (_obj) => prop(propToFind, _obj);
  309. if (!obj)
  310. return void 0;
  311. return obj[propToFind];
  312. }
  313. function eqPropsFn(property, objA, objB) {
  314. return equals(prop(property, objA), prop(property, objB));
  315. }
  316. var eqProps = curry(eqPropsFn);
  317. function createPath(path2, delimiter = ".") {
  318. return typeof path2 === "string" ? path2.split(delimiter) : path2;
  319. }
  320. function path(pathInput, obj) {
  321. if (arguments.length === 1)
  322. return (_obj) => path(pathInput, _obj);
  323. if (obj === null || obj === void 0) {
  324. return void 0;
  325. }
  326. let willReturn = obj;
  327. let counter = 0;
  328. const pathArrValue = createPath(pathInput);
  329. while (counter < pathArrValue.length) {
  330. if (willReturn === null || willReturn === void 0) {
  331. return void 0;
  332. }
  333. if (willReturn[pathArrValue[counter]] === null)
  334. return void 0;
  335. willReturn = willReturn[pathArrValue[counter]];
  336. counter++;
  337. }
  338. return willReturn;
  339. }
  340. function ifElseFn(condition, onTrue, onFalse) {
  341. return (...input) => {
  342. const conditionResult = typeof condition === "boolean" ? condition : condition(...input);
  343. if (conditionResult === true) {
  344. return onTrue(...input);
  345. }
  346. return onFalse(...input);
  347. };
  348. }
  349. var ifElse = curry(ifElseFn);
  350. function baseSlice(array, start, end) {
  351. let index = -1;
  352. let {
  353. length
  354. } = array;
  355. end = end > length ? length : end;
  356. if (end < 0) {
  357. end += length;
  358. }
  359. length = start > end ? 0 : end - start >>> 0;
  360. start >>>= 0;
  361. const result = Array(length);
  362. while (++index < length) {
  363. result[index] = array[index + start];
  364. }
  365. return result;
  366. }
  367. function is(targetPrototype, x) {
  368. if (arguments.length === 1)
  369. return (_x) => is(targetPrototype, _x);
  370. return x != null && x.constructor === targetPrototype || x instanceof targetPrototype;
  371. }
  372. function updateFn(index, newValue, list) {
  373. const clone = cloneList(list);
  374. if (index === -1)
  375. return clone.fill(newValue, index);
  376. return clone.fill(newValue, index, index + 1);
  377. }
  378. var update = curry(updateFn);
  379. function maxByFn(compareFn, x, y) {
  380. return compareFn(y) > compareFn(x) ? y : x;
  381. }
  382. var maxBy = curry(maxByFn);
  383. function mergeWithFn(mergeFn, a, b) {
  384. const willReturn = {};
  385. Object.keys(a).forEach((key) => {
  386. if (b[key] === void 0) {
  387. willReturn[key] = a[key];
  388. } else {
  389. willReturn[key] = mergeFn(a[key], b[key]);
  390. }
  391. });
  392. Object.keys(b).forEach((key) => {
  393. if (willReturn[key] !== void 0)
  394. return;
  395. if (a[key] === void 0) {
  396. willReturn[key] = b[key];
  397. } else {
  398. willReturn[key] = mergeFn(a[key], b[key]);
  399. }
  400. });
  401. return willReturn;
  402. }
  403. var mergeWith = curry(mergeWithFn);
  404. function minByFn(compareFn, x, y) {
  405. return compareFn(y) < compareFn(x) ? y : x;
  406. }
  407. var minBy = curry(minByFn);
  408. function ownKeys(object, enumerableOnly) {
  409. var keys2 = Object.keys(object);
  410. if (Object.getOwnPropertySymbols) {
  411. var symbols = Object.getOwnPropertySymbols(object);
  412. enumerableOnly && (symbols = symbols.filter(function(sym) {
  413. return Object.getOwnPropertyDescriptor(object, sym).enumerable;
  414. })), keys2.push.apply(keys2, symbols);
  415. }
  416. return keys2;
  417. }
  418. function _objectSpread2(target) {
  419. for (var i = 1; i < arguments.length; i++) {
  420. var source = null != arguments[i] ? arguments[i] : {};
  421. i % 2 ? ownKeys(Object(source), true).forEach(function(key) {
  422. _defineProperty(target, key, source[key]);
  423. }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
  424. Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
  425. });
  426. }
  427. return target;
  428. }
  429. function _defineProperty(obj, key, value) {
  430. if (key in obj) {
  431. Object.defineProperty(obj, key, {
  432. value,
  433. enumerable: true,
  434. configurable: true,
  435. writable: true
  436. });
  437. } else {
  438. obj[key] = value;
  439. }
  440. return obj;
  441. }
  442. function isIterable(input) {
  443. return Array.isArray(input) || type(input) === "Object";
  444. }
  445. function modifyFn(property, fn, iterable) {
  446. if (!isIterable(iterable))
  447. return iterable;
  448. if (iterable[property] === void 0)
  449. return iterable;
  450. if (isArray(iterable)) {
  451. return updateFn(property, fn(iterable[property]), iterable);
  452. }
  453. return _objectSpread2(_objectSpread2({}, iterable), {}, {
  454. [property]: fn(iterable[property])
  455. });
  456. }
  457. var modify = curry(modifyFn);
  458. function modifyPathFn(pathInput, fn, object) {
  459. const path$1 = createPath(pathInput);
  460. if (path$1.length === 1) {
  461. return _objectSpread2(_objectSpread2({}, object), {}, {
  462. [path$1[0]]: fn(object[path$1[0]])
  463. });
  464. }
  465. if (path(path$1, object) === void 0)
  466. return object;
  467. const val = modifyPath(Array.prototype.slice.call(path$1, 1), fn, object[path$1[0]]);
  468. if (val === object[path$1[0]]) {
  469. return object;
  470. }
  471. return assoc(path$1[0], val, object);
  472. }
  473. var modifyPath = curry(modifyPathFn);
  474. function moveFn(fromIndex, toIndex, list) {
  475. if (fromIndex < 0 || toIndex < 0) {
  476. throw new Error("Rambda.move does not support negative indexes");
  477. }
  478. if (fromIndex > list.length - 1 || toIndex > list.length - 1)
  479. return list;
  480. const clone = cloneList(list);
  481. clone[fromIndex] = list[toIndex];
  482. clone[toIndex] = list[fromIndex];
  483. return clone;
  484. }
  485. var move = curry(moveFn);
  486. function multiply(x, y) {
  487. if (arguments.length === 1)
  488. return (_y) => multiply(x, _y);
  489. return x * y;
  490. }
  491. var Identity = (x) => ({
  492. x,
  493. map: (fn) => Identity(fn(x))
  494. });
  495. function overFn(lens, fn, object) {
  496. return lens((x) => Identity(fn(x)))(object).x;
  497. }
  498. var over = curry(overFn);
  499. function pathEqFn(pathToSearch, target, input) {
  500. return equals(path(pathToSearch, input), target);
  501. }
  502. var pathEq = curry(pathEqFn);
  503. function pathOrFn(defaultValue, pathInput, obj) {
  504. return defaultTo(defaultValue, path(pathInput, obj));
  505. }
  506. var pathOr = curry(pathOrFn);
  507. var product = reduce(multiply, 1);
  508. function propEqFn(propToFind, valueToMatch, obj) {
  509. if (!obj)
  510. return false;
  511. return equals(valueToMatch, prop(propToFind, obj));
  512. }
  513. var propEq = curry(propEqFn);
  514. function propIsFn(targetPrototype, property, obj) {
  515. return is(targetPrototype, obj[property]);
  516. }
  517. var propIs = curry(propIsFn);
  518. function propOrFn(defaultValue, property, obj) {
  519. if (!obj)
  520. return defaultValue;
  521. return defaultTo(defaultValue, obj[property]);
  522. }
  523. var propOr = curry(propOrFn);
  524. function propSatisfiesFn(predicate, property, obj) {
  525. return predicate(prop(property, obj));
  526. }
  527. var propSatisfies = curry(propSatisfiesFn);
  528. function replaceFn(pattern, replacer, str) {
  529. return str.replace(pattern, replacer);
  530. }
  531. var replace = curry(replaceFn);
  532. function setFn(lens, replacer, x) {
  533. return over(lens, always(replacer), x);
  534. }
  535. var set = curry(setFn);
  536. function sliceFn(from, to, list) {
  537. return list.slice(from, to);
  538. }
  539. var slice = curry(sliceFn);
  540. function take(howMany, listOrString) {
  541. if (arguments.length === 1)
  542. return (_listOrString) => take(howMany, _listOrString);
  543. if (howMany < 0)
  544. return listOrString.slice();
  545. if (typeof listOrString === "string")
  546. return listOrString.slice(0, howMany);
  547. return baseSlice(listOrString, 0, howMany);
  548. }
  549. var isFunction = (x) => ["Promise", "Function"].includes(type(x));
  550. function tryCatch(fn, fallback) {
  551. if (!isFunction(fn)) {
  552. throw new Error(`R.tryCatch | fn '${fn}'`);
  553. }
  554. const passFallback = isFunction(fallback);
  555. return (...inputs) => {
  556. try {
  557. return fn(...inputs);
  558. } catch (e) {
  559. return passFallback ? fallback(e, ...inputs) : fallback;
  560. }
  561. };
  562. }
  563. function whenFn(predicate, whenTrueFn, input) {
  564. if (!predicate(input))
  565. return input;
  566. return whenTrueFn(input);
  567. }
  568. var when = curry(whenFn);
  569. function zipWithFn(fn, x, y) {
  570. return take(x.length > y.length ? y.length : x.length, x).map((xInstance, i) => fn(xInstance, y[i]));
  571. }
  572. var zipWith = curry(zipWithFn);
  573.  
  574. // $$
  575. function $$(sel2, el = document) {
  576. return [...el.querySelectorAll(sel2)];
  577. }
  578.  
  579. // po2dt
  580. var SPAN_PRECISION = 15 * 6e4;
  581. function po2dt([dday, dtime]) {
  582. return dday * 864e5 + dtime * SPAN_PRECISION;
  583. }
  584.  
  585. // google-calendar-keys
  586. var gkcs_unload = globalThis.gkcs_unload;
  587. gkcs_unload?.();
  588. globalThis.gkcs_unload = main();
  589. globalThis.gkcs_verbose = true;
  590. var { draggingGet: dg, draggingSet: ds } = draggingUse();
  591. function main() {
  592. console.clear();
  593. const unloaders = [];
  594. unloaders.push(
  595. hotkeyMapper(
  596. {
  597. "ctrl+b": async () => {
  598. const menuBtn = $visiable(sel.Menu);
  599. menuBtn?.click();
  600. },
  601. "alt+v": async () => await cpr(),
  602. "alt+k": () => eventMove([0, -1]),
  603. "alt+j": () => eventMove([0, 1]),
  604. "alt+h": () => eventMove([-1, 0]),
  605. "alt+l": () => eventMove([1, 0]),
  606. "alt+shift+k": () => eventExpand([0, -1]),
  607. "alt+shift+j": () => eventExpand([0, 1]),
  608. "alt+shift+h": () => eventExpand([-1, 0]),
  609. "alt+shift+l": () => eventExpand([1, 0])
  610. },
  611. "keydown",
  612. true
  613. )
  614. );
  615. return () => [...unloaders].reverse().forEach((e) => e?.());
  616. }
  617. async function cpr() {
  618. const r = $$("input,[role=button]").map((element) => {
  619. const { ps, deep } = onlyPatternSelectorGenerate(element);
  620. const label = element.ariaLabel ?? "";
  621. return {
  622. e: element,
  623. label,
  624. ps,
  625. deep
  626. };
  627. }).filter((e) => Boolean(e.label));
  628. const cpr2 = r.map(({ e, ...r2 }) => r2);
  629. console.table(r);
  630. globalThis.patternSelectorGenerate = patternSelectorGenerate;
  631. await browser_default.write(JSON.stringify(cpr2, null, 2));
  632. }
  633. function useListener(target = window) {
  634. return (event, onEvent, options) => {
  635. target.addEventListener(event, onEvent, options);
  636. const unload = () => target.removeEventListener(event, onEvent, options);
  637. return unload;
  638. };
  639. }
  640. async function eventExpand([dx, dy] = [0, 0]) {
  641. if (dy && await timeAddTry())
  642. return;
  643. return tryCatch(
  644. () => eventDrag([dx, dy], { expand: true }),
  645. () => inputDateTimeChange(0, po2dt([dx, dy]))
  646. )(null);
  647. }
  648. async function eventMove([dx, dy] = [0, 0]) {
  649. if (dy && await timeAddTry())
  650. return;
  651. return tryCatch(
  652. () => eventDrag([dx, dy]),
  653. () => inputDateTimeChange(po2dt([dx, dy]), 0)
  654. )(null);
  655. }
  656. async function inputValueSet(el, value) {
  657. console.log("inputValueSet", el, value);
  658. if (!el)
  659. throw new Error("no element");
  660. if (void 0 === value)
  661. throw new Error("no value");
  662. el.value = value;
  663. el.dispatchEvent(new InputEvent("input", { bubbles: true }));
  664. el.dispatchEvent(new Event("change", { bubbles: true }));
  665. el.dispatchEvent(
  666. new KeyboardEvent("keydown", {
  667. bubbles: true,
  668. keyCode: 13
  669. })
  670. );
  671. el.focus();
  672. await sleep(0);
  673. el.blur();
  674. }
  675. async function dateInputParse(dateInput, timeInput) {
  676. const dataDate = dateInput.getAttribute("data-date");
  677. const dataIcalElement = parentList(dateInput).find(
  678. (e) => e.getAttribute("data-ical")
  679. );
  680. if (!dataIcalElement)
  681. throw new Error("dataIcalElement not found");
  682. const dataIcal = dataIcalElement.getAttribute("data-ical");
  683. const datestringRaw = dataDate || dataIcal;
  684. if (!datestringRaw)
  685. throw new Error("no datestring");
  686. const dateString = datestringRaw.replace(
  687. /(\d{4})(\d{2})(\d{2})/,
  688. (_, a, b, c) => [a, b, c].join("-")
  689. );
  690. const timeString = timeInput?.value || "00:00";
  691. return new Date(`${dateString} ${timeString} Z`);
  692. }
  693. function dateParse(dateObj) {
  694. const m = dateObj.toISOString().match(/(\d\d\d\d-\d\d-\d\d)T(\d\d:\d\d):\d\d\.\d\d\dZ/);
  695. if (!m)
  696. throw m;
  697. const [date, time] = m.slice(1);
  698. return [date, time];
  699. }
  700. var sel = {
  701. Menu: '[aria-label="\u30E1\u30A4\u30F3\u30C9\u30ED\u30EF\u30FC"]',
  702. Summary: [
  703. '[aria-label="\u30BF\u30A4\u30C8\u30EB\u3068\u65E5\u6642\u3092\u8FFD\u52A0"]',
  704. '[aria-label="\u30BF\u30A4\u30C8\u30EB\u3092\u8FFD\u52A0"]',
  705. '[aria-label="\u30BF\u30A4\u30C8\u30EB"]'
  706. ].join(","),
  707. StartDate: '[aria-label="\u958B\u59CB\u65E5"]',
  708. StartTime: '[aria-label="\u958B\u59CB\u6642\u9593"]',
  709. EndTime: '[aria-label="\u7D42\u4E86\u6642\u9593"]',
  710. EndDate: '[aria-label="\u7D42\u4E86\u65E5"]',
  711. AllDay: '[aria-label="\u7D42\u65E5"]',
  712. TimeZone: '[aria-label="\u30BF\u30A4\u30E0\u30BE\u30FC\u30F3"]',
  713. Guests: '[aria-label="\u30B2\u30B9\u30C8"]'
  714. };
  715. async function inputDateTimeChange(sdt = 0, edt = 0) {
  716. const startDateInputPeek = $visiable(sel.StartDate);
  717. if (!startDateInputPeek) {
  718. const tz = $visiable(sel.TimeZone);
  719. if (!tz)
  720. throw new Error("tz not found");
  721. const editBtn = parentList(tz)?.find((e) => e.querySelector('[role="button"]'))?.querySelector('[role="button"]');
  722. if (!editBtn)
  723. throw new Error("No editable input");
  724. editBtn.click();
  725. await sleep(64);
  726. }
  727. const startDateInput = $visiable(sel.StartDate);
  728. const startTimeInput = $visiable(sel.StartTime);
  729. const endTimeInput = $visiable(sel.EndTime);
  730. const endDateInput = $visiable(sel.EndDate);
  731. const endDateInput1 = endDateInput ?? startDateInput;
  732. if (!startDateInput)
  733. throw new Error("no startDateInput");
  734. const startDate = await dateInputParse(startDateInput, startTimeInput);
  735. const endDate = await dateInputParse(endDateInput1, endTimeInput);
  736. const startDateObj1 = new Date(+startDate + sdt);
  737. const endDateObj1 = new Date(+endDate + edt);
  738. const [startDate0, startTime0] = dateParse(startDate);
  739. const [endDate0, endTime0] = dateParse(endDate);
  740. const [startDate1, startTime1] = dateParse(startDateObj1);
  741. const [endDate1, endTime1] = dateParse(endDateObj1);
  742. if (globalThis.gkcs_verbose)
  743. console.table({
  744. startDate: startDate.toISOString(),
  745. endDate: endDate.toISOString(),
  746. startDateObj1: startDateObj1.toISOString(),
  747. endDateObj1: endDateObj1.toISOString()
  748. });
  749. if (globalThis.gkcs_verbose)
  750. console.table({
  751. startDateInput: Boolean(startDateInput),
  752. startTimeInput: Boolean(startTimeInput),
  753. endDateInput: Boolean(endDateInput),
  754. endTimeInput: Boolean(endTimeInput),
  755. startDate0,
  756. startDate1,
  757. startTime0,
  758. startTime1,
  759. endDate0,
  760. endDate1,
  761. endTime0,
  762. endTime1
  763. });
  764. startDateInput && startDate1 !== startDate0 && await inputValueSet(startDateInput, startDate1);
  765. endDateInput && endDate1 !== endDate0 && await inputValueSet(endDateInput, endDate1);
  766. startTimeInput && startTime1 !== startTime0 && await inputValueSet(startTimeInput, startTime1);
  767. endTimeInput && endTime1 !== endTime0 && await inputValueSet(endTimeInput, endTime1);
  768. $visiable(sel.Summary)?.focus();
  769. }
  770. async function timeAddTry() {
  771. const btn = $$("button").find(
  772. (e) => ["Add time", "\u6642\u9593\u3092\u8FFD\u52A0"].includes(e.textContent ?? "")
  773. );
  774. if (!btn)
  775. return 0;
  776. btn.click();
  777. await sleep(64);
  778. return 1;
  779. }
  780. function eleVisiable(ele) {
  781. return (ele.getClientRects().length && ele) ?? false;
  782. }
  783. function $visiable(sel2, el = document) {
  784. return $$(sel2, el).filter(eleVisiable)[0] ?? null;
  785. }
  786. function parentList(el) {
  787. const parent = el?.parentElement;
  788. if (!parent)
  789. return [];
  790. return [parent, ...parentList(parent)];
  791. }
  792. function sleep(ms = 0) {
  793. return new Promise((resolve) => setTimeout(resolve, ms));
  794. }
  795. function mouseOpt([x, y]) {
  796. return {
  797. isTrusted: true,
  798. bubbles: true,
  799. button: 0,
  800. buttons: 1,
  801. cancelBubble: false,
  802. cancelable: true,
  803. clientX: x,
  804. clientY: y,
  805. movementX: 0,
  806. movementY: 0,
  807. x,
  808. y
  809. };
  810. }
  811. function centerGet(element) {
  812. const { x, y, width: w, height: h } = element.getBoundingClientRect();
  813. return [x + w / 2, y + h / 2];
  814. }
  815. function vec2add([x, y], [z, w]) {
  816. return [x + z, y + w];
  817. }
  818. function $(sel2) {
  819. return document.querySelector(sel2);
  820. }
  821. function onlyPatternSelectorGenerate(element) {
  822. let deep = 0;
  823. let ps = "";
  824. while (1) {
  825. ps = patternSelectorGenerate(element, { deep });
  826. if ($$(ps).length <= 1)
  827. break;
  828. deep++;
  829. }
  830. return { ps, deep };
  831. }
  832. function patternSelectorGenerate(element, { deep = 0 } = {}) {
  833. const psg = (e) => patternSelectorGenerate(e, { deep: deep - 1 });
  834. const tag = element.tagName.toLowerCase();
  835. const attrs = [
  836. "aria-label",
  837. "data-key",
  838. "role",
  839. "type"
  840. ].map((name) => attrSel(element, name)).filter((e) => !e.match("\n")).join("");
  841. let base = `${tag}${attrs}`;
  842. if (!deep)
  843. return base;
  844. const next = element.nextElementSibling;
  845. if (next)
  846. base = `${base}:has(+${psg(next).replace(/:has\(.*?\)/g, "")})`;
  847. const prev = element.previousElementSibling;
  848. if (prev)
  849. return `${psg(prev)}+${base}`;
  850. const parent = element.parentElement;
  851. if (!parent)
  852. return base;
  853. const children = [...parent.children];
  854. const nth = children.findIndex((v) => element === v) + 1;
  855. const nthl = children.length - nth + 1;
  856. if (!nth)
  857. return base;
  858. const parentSelector = psg(parent);
  859. return `${parentSelector}>${base}:nth-child(${nth}):nth-last-child(${nthl})`;
  860. }
  861. function attrSel(element, name) {
  862. const attr = element.getAttribute(name);
  863. const dataKey = attr !== null ? attr ? `[${name}="${attr}"]` : `[${name}]` : "";
  864. return dataKey;
  865. }
  866. function eventDrag([dx, dy] = [0, 0], { expand = false } = {}) {
  867. const summaryInput = $visiable(sel.Summary);
  868. summaryInput?.focus();
  869. if (!dg()) {
  870. const floatingBtns = [
  871. .../* @__PURE__ */ new Set([
  872. ...$$('div[role="button"]').reverse().filter((e) => getComputedStyle(e).zIndex === "5004"),
  873. ...$$('div:has([role="button"])').reverse().filter((e) => getComputedStyle(e).zIndex === "5004")
  874. ])
  875. ];
  876. if (floatingBtns.length > 1)
  877. throw new Error("Multiple floating");
  878. const floatingBtn = floatingBtns[0];
  879. if (!floatingBtn)
  880. throw new Error("no event selected");
  881. const target = expand ? floatingBtn.querySelector('*[data-dragsource-type="3"]') : floatingBtn;
  882. if (!target)
  883. throw new Error("no dragTarget exists");
  884. console.log(target);
  885. const pos = centerGet(target);
  886. console.log("cpos", pos);
  887. ds({ pos, target });
  888. posHint(pos);
  889. target.dispatchEvent(new MouseEvent("mousedown", mouseOpt(pos)));
  890. document.dispatchEvent(new MouseEvent("mousemove", mouseOpt(pos)));
  891. }
  892. if (dg()) {
  893. const container = $('[role="row"][data-dragsource-type="4"]');
  894. const gridcells = [...container.querySelectorAll('[role="gridcell"]')];
  895. const containerSize = container.getBoundingClientRect();
  896. const w = containerSize.width / gridcells.length;
  897. const h = containerSize.height / 24 / 4;
  898. ds({
  899. pos: vec2add(dg().pos, [dx * w, dy * h]),
  900. target: dg().target
  901. });
  902. const pos = dg().pos;
  903. posHint(pos);
  904. document.body.dispatchEvent(new MouseEvent("mousemove", mouseOpt(pos)));
  905. }
  906. const unload = useListener()("keyup", (e) => {
  907. if (!["AltLeft", "AltRight"].includes(e.code))
  908. return;
  909. unload();
  910. ds(null);
  911. document.dispatchEvent(
  912. new MouseEvent("mouseup", { bubbles: true, cancelable: true })
  913. );
  914. });
  915. return unload;
  916. }
  917. function posHint(pos) {
  918. const div = document.createElement("div");
  919. div.style.background = "red";
  920. div.style.position = "absolute";
  921. div.style.left = pos[0] + "px";
  922. div.style.top = pos[1] + "px";
  923. div.style.width = "1px";
  924. div.style.height = "1px";
  925. div.style.zIndex = "10000";
  926. document.body.appendChild(div);
  927. setTimeout(() => div.remove(), 200);
  928. }
  929. function draggingUse() {
  930. const draggingGet = () => globalThis.gcks_dragging;
  931. const draggingSet = (s) => globalThis.gcks_dragging = s;
  932. return { draggingGet, draggingSet };
  933. }