瀏覽代碼

feat(content): add text-input and event-trigger handler

Ahmad Kholid 3 年之前
父節點
當前提交
cd19f5a96b

+ 0 - 2
src/background/blocks-handler.js

@@ -59,8 +59,6 @@ export function interactionHandler(block) {
     this._listenTabMessage(
       block.name,
       (data) => {
-        console.log(block.name, data, 'data');
-
         resolve({
           data,
           nextBlockId: getBlockConnection(block),

+ 0 - 1
src/background/workflow-engine.js

@@ -82,7 +82,6 @@ class WorkflowEngine {
     const handlerName = isInteraction
       ? 'interactionHandler'
       : toCamelCase(block?.name);
-    console.log(isInteraction, handlerName, tasks);
     const handler = blocksHandler[handlerName];
 
     if (handler) {

+ 1 - 1
src/components/newtab/workflow/WorkflowDetailsCard.vue

@@ -48,7 +48,7 @@
           <v-remixicon name="riMore2Line" />
         </ui-button>
       </template>
-      <ui-list class="w-36">
+      <ui-list>
         <ui-list-item class="cursor-pointer" @click="$emit('showDataColumns')">
           <v-remixicon name="riKey2Line" class="mr-2 -ml-1" />
           <span>Data columns</span>

+ 11 - 6
src/components/newtab/workflow/edit/EditTextInput.vue

@@ -8,7 +8,7 @@
     />
     <ui-input
       :model-value="data.delay"
-      label="Typing delay (0 to disable)"
+      label="Typing delay (millisecond)(0 to disable)"
       placeholder="Delay"
       class="w-full"
       min="0"
@@ -20,12 +20,12 @@
       <div class="grid grid-cols-2 gap-2 mt-2">
         <ui-checkbox
           v-for="event in events"
-          :key="event"
-          :model-value="data[event]"
+          :key="event.id"
+          :model-value="data[event.id]"
           class="capitalize"
-          @change="updateData({ [event]: $event })"
+          @change="updateData({ [event.id]: $event })"
         >
-          {{ event }}
+          {{ event.name }}
         </ui-checkbox>
       </div>
     </div>
@@ -42,7 +42,12 @@ const props = defineProps({
 });
 const emit = defineEmits(['update:data']);
 
-const events = ['change', 'input', 'keyup', 'keydown'];
+const events = [
+  { id: 'changeEvent', name: 'Change' },
+  { id: 'inputEvent', name: 'input' },
+  { id: 'keyupEvent', name: 'keyup' },
+  { id: 'keydownEvent', name: 'keydown' },
+];
 
 function updateData(value) {
   emit('update:data', { ...props.data, ...value });

+ 4 - 1
src/components/ui/UiButton.vue

@@ -7,7 +7,10 @@
       color ? color : variants[variant],
       icon ? 'p-2' : 'py-2 px-4',
       circle ? 'rounded-full' : 'rounded-lg',
-      { 'opacity-70': disabled, 'pointer-events-none': loading || disabled },
+      {
+        'opacity-70 bg-opacity-70': disabled,
+        'pointer-events-none': loading || disabled,
+      },
     ]"
     v-bind="{ disabled: loading || disabled, ...$attrs }"
   >

+ 119 - 25
src/content/blocks-handler.js

@@ -1,10 +1,15 @@
+/* eslint-disable consistent-return, no-param-reassign */
+import simulateEvent from '@/utils/simulate-event';
+
 function handleElement(data, callback) {
-  if (!data.selector) return;
+  if (!data.selector) return null;
 
   const element = data.multiple
     ? document.querySelectorAll(data.selector)
     : document.querySelector(data.selector);
 
+  if (typeof callback === 'boolean' && callback) return element;
+
   if (data.multiple) {
     element.forEach(callback);
   } else if (element) {
@@ -13,48 +18,137 @@ function handleElement(data, callback) {
 }
 
 export function eventClick({ data }) {
-  handleElement(data, (element) => {
-    element.click();
-  });
+  return new Promise((resolve) => {
+    handleElement(data, (element) => {
+      element.click();
+    });
 
-  return '';
+    resolve('');
+  });
 }
 
 export function getText({ data }) {
-  let regex;
-  let textResult = '';
+  return new Promise((resolve) => {
+    let regex;
+    let textResult = '';
 
-  if (data.regex) {
-    regex = new RegExp(data.regex, data.regexExp.join(''));
-  }
+    if (data.regex) {
+      regex = new RegExp(data.regex, data.regexExp.join(''));
+    }
 
-  handleElement(data, (element) => {
-    let text = element.innerText;
+    handleElement(data, (element) => {
+      let text = element.innerText;
 
-    if (regex) text = text.match(regex).join(' ');
+      if (regex) text = text.match(regex).join(' ');
 
-    textResult += `${text} `;
-  });
+      textResult += `${text} `;
+    });
 
-  return textResult;
+    resolve(textResult);
+  });
 }
 
 export function elementScroll({ data }) {
-  handleElement(data, (element) => {
-    element.scroll(data.scrollX, data.scrollY);
-  });
+  return new Promise((resolve) => {
+    handleElement(data, (element) => {
+      element.scroll(data.scrollX, data.scrollY);
+    });
 
-  return '';
+    resolve('');
+  });
 }
 
 export function attributeValue({ data }) {
-  let result = '';
+  return new Promise((resolve) => {
+    let result = '';
+
+    handleElement(data, (element) => {
+      const value = element.getAttribute(data.attributeName);
 
-  handleElement(data, (element) => {
-    const value = element.getAttribute(data.attributeName);
+      window.dispatchEvent(new Event('scroll'));
 
-    result += `${value} `;
+      result += `${value} `;
+    });
+
+    resolve(result);
   });
+}
+
+function inputText({ data, element, index = 0, callback }) {
+  const noDelay = data.delay === 0;
+  const currentChar = data.text[index];
+
+  if (noDelay) {
+    element.value = data.text;
+  } else {
+    element.value += currentChar;
+  }
 
-  return result;
+  const code = /\s/.test(currentChar)
+    ? 'Space'
+    : `key${currentChar.toUpperCase()}`;
+
+  console.log(code, currentChar, element.value);
+
+  if (data.keydownEvent) {
+    simulateEvent(element, 'keydown', { code, key: currentChar });
+  }
+  if (data.keyupEvent) {
+    simulateEvent(element, 'keyup', { code, key: currentChar });
+  }
+  if (data.changeEvent) {
+    element.dispatchEvent(new Event('change'));
+  }
+  if (data.inputEvent) {
+    element.dispatchEvent(
+      new InputEvent('input', { inputType: 'insertText', data: currentChar })
+    );
+  }
+
+  if (!noDelay && index + 1 !== data.text.length) {
+    setTimeout(() => {
+      inputText({ data, element, callback, index: index + 1 });
+    }, data.delay);
+  } else {
+    callback();
+  }
+}
+export function textInput({ data }) {
+  return new Promise((resolve) => {
+    const elements = handleElement(data, true);
+    const textFields = ['INPUT', 'TEXTAREA'];
+
+    if (data.multiple) {
+      const promises = Array.from(elements).map((element) => {
+        return new Promise((eventResolve) => {
+          if (textFields.includes(element.tagName))
+            inputText({ data, element, callback: eventResolve });
+          else eventResolve();
+        });
+      });
+
+      Promise.allSettled(promises).then(() => {
+        console.log('hola amigo');
+        resolve('');
+      });
+    } else if (textFields.includes(elements.tagName)) {
+      inputText({
+        element: elements,
+        callback: () => {
+          console.log('hola amigo 2');
+          resolve('');
+        },
+      });
+    }
+  });
+}
+
+export function triggerEvent({ data }) {
+  return new Promise((resolve) => {
+    handleElement(data, (element) => {
+      simulateEvent(element, data.eventName, data.eventParams);
+    });
+
+    resolve('');
+  });
 }

+ 3 - 3
src/content/index.js

@@ -7,9 +7,9 @@ browser.runtime.onConnect.addListener((port) => {
     const handler = blocksHandler[toCamelCase(data.name)];
     console.log(`${data.name}(${toCamelCase(data.name)}):`, data);
     if (handler) {
-      const result = handler(data);
-
-      port.postMessage({ type: data.name, data: result });
+      handler(data).then((result) => {
+        port.postMessage({ type: data.name, data: result });
+      });
     } else {
       console.error(`"${data.name}" doesn't have a handler`);
     }

+ 5 - 4
src/utils/shared.js

@@ -143,10 +143,10 @@ export const tasks = {
       multiple: false,
       text: '',
       delay: 0,
-      keyup: true,
-      keydown: true,
-      change: true,
-      input: true,
+      keyupEvent: true,
+      keydownEvent: true,
+      changeEvent: true,
+      inputEvent: true,
     },
   },
   'repeat-task': {
@@ -252,4 +252,5 @@ export const eventList = [
   { id: 'keyup', name: 'Keyup', type: 'keyboard-event' },
   { id: 'keypress', name: 'Keypress', type: 'keyboard-event' },
   { id: 'wheel', name: 'Wheel', type: 'wheel-event' },
+  { id: 'submit', name: 'Submit', type: 'submit-event' },
 ];

+ 39 - 0
src/utils/simulate-event.js

@@ -0,0 +1,39 @@
+import { eventList } from './shared';
+
+export function getEventObj(name, params) {
+  const eventType = eventList.find(({ id }) => id === name)?.type ?? '';
+  let event;
+
+  switch (eventType) {
+    case 'mouse-event':
+      event = new MouseEvent(name, params);
+      break;
+    case 'focus-event':
+      event = new FocusEvent(name, params);
+      break;
+    case 'touch-event':
+      event = new TouchEvent(name, params);
+      break;
+    case 'keyboard-event':
+      event = new KeyboardEvent(name, params);
+      break;
+    case 'wheel-event':
+      event = new WheelEvent(name, params);
+      break;
+    default:
+      event = new Event(name, params);
+  }
+
+  return event;
+}
+
+export default function (element, name, params) {
+  const event = getEventObj(name, params);
+  const useNativeEvents = ['focus', 'submit', 'blur'];
+
+  if (useNativeEvents.includes(name) && element[name]) {
+    element[name]();
+  } else {
+    element.dispatchEvent(event);
+  }
+}