diff --git a/llamaindex-workflow/.env.example b/llamaindex-workflow/.env.example new file mode 100644 index 000000000..e69de29bb diff --git a/llamaindex-workflow/README.MD b/llamaindex-workflow/README.MD new file mode 100644 index 000000000..a0ba95449 --- /dev/null +++ b/llamaindex-workflow/README.MD @@ -0,0 +1,31 @@ +# LlamaIndex Workflow meets Chainlit +This cookbook aims to show how to use LlamaIndex's latest Workflow abstraction with Chainlit! + + + +## What is a Workflow? +A workflow is an event-driven abstraction. Unlike having to define agentic systems as digraphs, LlamaIndex's Workflow abstraction allows you to just define the events within the workflow and what happens when these events get triggered. The result is being able to write performant multi-agent / multi-llm applications within far fewer lines of code. Oh and it has type checking too so that goodbye type-related bugs! + +## Getting started +You will need your TAVILY API key and OpenAI API key to run this demo. Key those into your .env file. + +To install requirements: +``` +pip install -r requirements.txt +``` + +To run the app +``` +chainlit run app.py --watch +``` + +## The app +Our workflow application includes a function calling agent and a simple chat engine. When asked a question, the workflow triggers 2 events: a search online and answer event and a "simply answer" event without any searching. We route to both events whenever we receive a StartEvent (i.e. whenever we receive a question). +- The first event uses Tavily to do the online search, and the agent then extracts the search results to reply. +- The second event simply uses the LLM to reply without any searching. + +Since the second event might cause the LLM to hallucinate, we have a 3rd `compilation` event to select the best answer. + +It really is quite simple to visualize the "chain of thought" in the workflow - simply add a `@cl.step` decorator on top of LlamaIndex's Workflow `@step` decorator for each workflow event. + + \ No newline at end of file diff --git a/llamaindex-workflow/app.png b/llamaindex-workflow/app.png new file mode 100644 index 000000000..5c981e550 Binary files /dev/null and b/llamaindex-workflow/app.png differ diff --git a/llamaindex-workflow/app.py b/llamaindex-workflow/app.py new file mode 100644 index 000000000..b048436ea --- /dev/null +++ b/llamaindex-workflow/app.py @@ -0,0 +1,224 @@ +import chainlit as cl + +import os +from dotenv import load_dotenv, find_dotenv + +from llama_index.core.agent import ReActAgent +from llama_index.core.base.llms.types import ChatMessage, MessageRole +from llama_index.core.chat_engine import SimpleChatEngine +from llama_index.core.llms import LLM +from llama_index.core.workflow import ( + Workflow, + Context, + Event, + StartEvent, + StopEvent, + step +) +from llama_index.llms.gemini import Gemini +## Utility function to draw out the workflow +# from llama_index.utils.workflow import ( +# draw_all_possible_flows +# ) +from llama_index.tools.tavily_research import TavilyToolSpec + +from typing import Optional, Annotated, List, Any + +_ = load_dotenv(find_dotenv()) + +llm = Gemini(model="models/gemini-1.5-flash-latest") + +### Define tools +search_tool_spec = TavilyToolSpec(api_key=os.getenv("TAVILY")) +search_tools = search_tool_spec.to_tool_list() + +### Define events +class SearchEvent(Event): + """Requires the LLM to do an online search to answer the question""" + query: Annotated[str, "The user's query"] + +class AnswerEvent(Event): + """Allows the LLM to answer the question without searching""" + query: Annotated[str, "The user's query"] + +class ResponseEvent(Event): + """Collects LLM response""" + query: Annotated[str, "The user's query"] + answer: Annotated[str, "The LLM's response"] + +### Define workfow +class HashTagWorkflow(Workflow): + def __init__(self, *args: Any, **kwargs: Any): + self.llm = llm + super().__init__(*args, **kwargs) + + @cl.step(type="llm") + @step + async def generate_hashtag(self, ev: StartEvent) -> StopEvent: + response = await self.llm.acomplete(f"Generate 1-3 hashtags related to {ev.response}") + return StopEvent(str(response)) + +class MixtureOfAnswers(Workflow): + def __init__( + self, + *args: Any, + llm: Optional[LLM] = llm, + **kwargs: Any + ): + """Class constructor. Takes in an llm instance and constructs + 1. A function calling agent with search tools + 2. A simple chat engine instance + 3. A common memory instance across the workflow + + Args: + llm (Optional[LLM], optional): LLM instance. Defaults to Settings.llm. + """ + super().__init__(*args, **kwargs) + self.llm = llm + self.search_agent=ReActAgent.from_tools( + tools = search_tools, + llm = self.llm + ) + # self.search_agent = self.search_agent_worker.as_agent() + self.answer_without_search_engine = SimpleChatEngine.from_defaults( + llm = self.llm + ) + self.history: List[ChatMessage] = [] + + @cl.step(type="llm") + @step() + async def route_to_llm( + self, + ev: StartEvent + ) -> SearchEvent | AnswerEvent: + """Generates a search event and an answer event once given a start event""" + + ## Update memory + self.history.append( + ChatMessage( + role = MessageRole.USER, + content = ev.query + ) + ) + + ## Routes to both events. But you can also write a router component to decide + ## which event to route to. + self.send_event(SearchEvent(query = ev.query)) + self.send_event(AnswerEvent(query = ev.query)) + + @cl.step(type="tool") + @step() + async def search_and_answer( + self, + ev: SearchEvent + ) -> ResponseEvent: + """Uses the tavily search tool to answer the question""" + + ## Synthesize response + response = await self.search_agent.achat( + ev.query, + chat_history = self.history + ) + + ## [OPTIONAL] Show intermediate response in the frontend + # await cl.Message(content="ANSWER WITH SEARCH: " + str(response)).send() + + ## Update memory + self.history.append( + ChatMessage( + role = MessageRole.ASSISTANT, + content = "ANSWER WITH SEARCH: " + str(response) + ) + ) + + return ResponseEvent(query = ev.query, answer = str(response)) + + @cl.step(type="llm") + @step() + async def simply_answer( + self, + ev: AnswerEvent + ) -> ResponseEvent: + """Uses the LLM to simple answer the question""" + + ## Synthesize response + response = await self.answer_without_search_engine.achat( + ev.query, + chat_history = self.history + ) + + ## [OPTIONAL] Show intermediate response in the frontend + # await cl.Message(content="ANSWER WITHOUT SEARCH: " + str(response)).send() + + ## Update memory + self.history.append( + ChatMessage( + role = MessageRole.ASSISTANT, + content = "ANSWER WITHOUT SEARCH: " + str(response) + ) + ) + + return ResponseEvent(query = ev.query, answer = str(response)) + + @cl.step(type="llm") + @step() + async def compile( + self, + ctx: Context, + ev: ResponseEvent, + hashtag_workflow: Workflow = HashTagWorkflow() + ) -> StopEvent: + """Compiles and summarizes answers from all response events""" + + ## There are 2 response events from routing to 2 different agents. This can + ## also be a dynamic number of events. + ready = ctx.collect_events(ev, [ResponseEvent] * 2) + + if ready is None: + return None + + response = await self.llm.acomplete( + f""" + A user has asked us a question and we have responded accordingly using a + search tool and without using a search tool. Your job is to decide which + response best answered the question and summarize the response into a crisp + reply. If both responses answered the question, summarize both responses + into a single answer. + + The user's query was: {ev.query} + + The responses are: + {ready[0].answer} & + {ready[1].answer} + """ + ) + + ## Add hashtag + hashtag = await hashtag_workflow.run(response=str(response)) + + ## Update memory + self.history.append( + ChatMessage( + role = MessageRole.ASSISTANT, + content = "FINAL ANSWER: " + str(response) + str(hashtag) + ) + ) + + return StopEvent(result = str(response) + str(hashtag)) + +### Define the app - with just a few lines of code +@cl.on_chat_start +async def on_chat_start(): + app = MixtureOfAnswers( + verbose = True, + timeout = 6000 + ) #The app times out if it runs for 6000s without any result + app.add_workflows(hashtag_workflow = HashTagWorkflow(timeout=6000)) + cl.user_session.set("app", app) + await cl.Message("Hello! Ask me anything!").send() + +@cl.on_message +async def on_message(message: cl.Message): + app = cl.user_session.get("app") + result = await app.run(query = message.content) + await cl.Message(content = result).send() \ No newline at end of file diff --git a/llamaindex-workflow/chainlit.md b/llamaindex-workflow/chainlit.md new file mode 100644 index 000000000..4507ac467 --- /dev/null +++ b/llamaindex-workflow/chainlit.md @@ -0,0 +1,14 @@ +# Welcome to Chainlit! 🚀🤖 + +Hi there, Developer! 👋 We're excited to have you on board. Chainlit is a powerful tool designed to help you prototype, debug and share applications built on top of LLMs. + +## Useful Links 🔗 + +- **Documentation:** Get started with our comprehensive [Chainlit Documentation](https://docs.chainlit.io) 📚 +- **Discord Community:** Join our friendly [Chainlit Discord](https://discord.gg/k73SQ3FyUh) to ask questions, share your projects, and connect with other developers! 💬 + +We can't wait to see what you create with Chainlit! Happy coding! 💻😊 + +## Welcome screen + +To modify the welcome screen, edit the `chainlit.md` file at the root of your project. If you do not want a welcome screen, just leave this file empty. diff --git a/llamaindex-workflow/get_workflow.ipynb b/llamaindex-workflow/get_workflow.ipynb new file mode 100644 index 000000000..2f704dcff --- /dev/null +++ b/llamaindex-workflow/get_workflow.ipynb @@ -0,0 +1,306 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## This notebook only seeks to draw the workflow out" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from llama_index.core.agent import FunctionCallingAgentWorker\n", + "from llama_index.core.base.llms.types import ChatMessage, MessageRole\n", + "from llama_index.core.chat_engine import SimpleChatEngine\n", + "from llama_index.core.llms import LLM\n", + "from llama_index.core.workflow import (\n", + " Workflow,\n", + " Context,\n", + " Event,\n", + " StartEvent,\n", + " StopEvent,\n", + " step\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from llama_index.tools.tavily_research import TavilyToolSpec\n", + "from llama_index.llms.openai import OpenAI" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from llama_index.utils.workflow import (\n", + " draw_all_possible_flows\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Optional, Annotated, List, Any\n", + "\n", + "class SearchEvent(Event):\n", + " \"\"\"Requires the LLM to do an online search to answer the question\"\"\"\n", + " query: Annotated[str, \"The user's query\"]\n", + "\n", + "class AnswerEvent(Event):\n", + " \"\"\"Allows the LLM to answer the question without searching\"\"\"\n", + " query: Annotated[str, \"The user's query\"]\n", + "\n", + "class ResponseEvent(Event):\n", + " \"\"\"Collects LLM response\"\"\"\n", + " query: Annotated[str, \"The user's query\"]\n", + " answer: Annotated[str, \"The LLM's response\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/anaconda3/envs/llamaindex/lib/python3.12/site-packages/pydantic/_internal/_config.py:341: UserWarning: Valid config keys have changed in V2:\n", + "* 'allow_population_by_field_name' has been renamed to 'populate_by_name'\n", + "* 'smart_union' has been removed\n", + " warnings.warn(message, UserWarning)\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "llm = OpenAI(model=\"gpt-4o-mini\")\n", + "\n", + "### Define tools\n", + "search_tool_spec = TavilyToolSpec(api_key=os.getenv(\"TAVILY\"))\n", + "search_tools = search_tool_spec.to_tool_list()\n", + "\n", + "### Define events\n", + "class SearchEvent(Event):\n", + " \"\"\"Requires the LLM to do an online search to answer the question\"\"\"\n", + " query: Annotated[str, \"The user's query\"]\n", + "\n", + "class AnswerEvent(Event):\n", + " \"\"\"Allows the LLM to answer the question without searching\"\"\"\n", + " query: Annotated[str, \"The user's query\"]\n", + "\n", + "class ResponseEvent(Event):\n", + " \"\"\"Collects LLM response\"\"\"\n", + " query: Annotated[str, \"The user's query\"]\n", + " answer: Annotated[str, \"The LLM's response\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class MixtureOfAnswers(Workflow):\n", + " def __init__(\n", + " self, \n", + " *args: Any,\n", + " llm: Optional[LLM] = llm,\n", + " **kwargs: Any\n", + " ):\n", + " \"\"\"Class constructor. Takes in an llm instance and constructs \n", + " 1. A function calling agent with search tools\n", + " 2. A simple chat engine instance\n", + " 3. A common memory instance across the workflow\n", + "\n", + " Args:\n", + " llm (Optional[LLM], optional): LLM instance. Defaults to Settings.llm.\n", + " \"\"\"\n", + " super().__init__(*args, **kwargs)\n", + " self.llm = llm\n", + " self.search_agent_worker = FunctionCallingAgentWorker.from_tools(\n", + " tools = search_tools,\n", + " llm = self.llm\n", + " )\n", + " self.search_agent = self.search_agent_worker.as_agent()\n", + " self.answer_without_search_engine = SimpleChatEngine.from_defaults(\n", + " llm = self.llm\n", + " )\n", + " self.history: List[ChatMessage] = []\n", + " \n", + " @step()\n", + " async def route_to_llm(\n", + " self,\n", + " ev: StartEvent\n", + " ) -> SearchEvent | AnswerEvent:\n", + " \"\"\"Generates a search event and an answer event once given a start event\"\"\"\n", + " \n", + " ## Update memory\n", + " self.history.append(\n", + " ChatMessage(\n", + " role = MessageRole.USER,\n", + " content = ev.query\n", + " )\n", + " )\n", + " \n", + " ## Routes to both events. But you can also write a router component to decide \n", + " ## which event to route to.\n", + " self.send_event(SearchEvent(query = ev.query))\n", + " self.send_event(AnswerEvent(query = ev.query))\n", + " \n", + " @step()\n", + " async def search_and_answer(\n", + " self,\n", + " ev: SearchEvent\n", + " ) -> ResponseEvent:\n", + " \"\"\"Uses the tavily search tool to answer the question\"\"\"\n", + " \n", + " ## Synthesize response\n", + " response = await self.search_agent.achat(\n", + " ev.query, \n", + " chat_history = self.history\n", + " )\n", + " \n", + " ## [OPTIONAL] Show intermediate response in the frontend\n", + " # await cl.Message(content=\"ANSWER WITH SEARCH: \" + str(response)).send()\n", + " \n", + " ## Update memory\n", + " self.history.append(\n", + " ChatMessage(\n", + " role = MessageRole.ASSISTANT,\n", + " content = \"ANSWER WITH SEARCH: \" + str(response)\n", + " )\n", + " )\n", + " \n", + " return ResponseEvent(query = ev.query, answer = str(response))\n", + "\n", + " @step()\n", + " async def simply_answer(\n", + " self,\n", + " ev: AnswerEvent\n", + " ) -> ResponseEvent:\n", + " \"\"\"Uses the LLM to simple answer the question\"\"\"\n", + " \n", + " ## Synthesize response\n", + " response = await self.answer_without_search_engine.achat(\n", + " ev.query, \n", + " chat_history = self.history\n", + " )\n", + " \n", + " ## [OPTIONAL] Show intermediate response in the frontend\n", + " # await cl.Message(content=\"ANSWER WITHOUT SEARCH: \" + str(response)).send()\n", + " \n", + " ## Update memory\n", + " self.history.append(\n", + " ChatMessage(\n", + " role = MessageRole.ASSISTANT,\n", + " content = \"ANSWER WITHOUT SEARCH: \" + str(response)\n", + " )\n", + " )\n", + " \n", + " return ResponseEvent(query = ev.query, answer = str(response))\n", + " \n", + " @step()\n", + " async def compile(\n", + " self,\n", + " ctx: Context,\n", + " ev: ResponseEvent\n", + " ) -> StopEvent:\n", + " \"\"\"Compiles and summarizes answers from all response events\"\"\"\n", + " \n", + " ## There are 2 response events from routing to 2 different agents. This can\n", + " ## also be a dynamic number of events.\n", + " ready = ctx.collect_events(ev, [ResponseEvent] * 2) \n", + " \n", + " if ready is None:\n", + " return None\n", + " \n", + " response = await self.llm.acomplete(\n", + " f\"\"\"\n", + " A user has asked us a question and we have responded accordingly using a \n", + " search tool and without using a search tool. Your job is to decide which \n", + " response best answered the question and summarize the response into a crisp \n", + " reply. If both responses answered the question, summarize both responses\n", + " into a single answer.\n", + " \n", + " The user's query was: {ev.query}\n", + " \n", + " The responses are:\n", + " {ready[0].answer} &\n", + " {ready[1].answer}\n", + " \"\"\"\n", + " )\n", + " \n", + " ## Update memory\n", + " self.history.append(\n", + " ChatMessage(\n", + " role = MessageRole.ASSISTANT,\n", + " content = \"FINAL ANSWER: \" + str(response)\n", + " )\n", + " )\n", + " \n", + " return StopEvent(result = str(response))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "workflow.html\n" + ] + } + ], + "source": [ + "draw_all_possible_flows(MixtureOfAnswers, filename=\"workflow.html\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "llamaindex", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.5" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "4fb762c9777d81b920bdc4f2aaae00fd18dcefdd6f60d8272d61e705ce6e5d33" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/llamaindex-workflow/lib/bindings/utils.js b/llamaindex-workflow/lib/bindings/utils.js new file mode 100644 index 000000000..088effe20 --- /dev/null +++ b/llamaindex-workflow/lib/bindings/utils.js @@ -0,0 +1,189 @@ +function neighbourhoodHighlight(params) { + // console.log("in nieghbourhoodhighlight"); + allNodes = nodes.get({ returnType: "Object" }); + // originalNodes = JSON.parse(JSON.stringify(allNodes)); + // if something is selected: + if (params.nodes.length > 0) { + highlightActive = true; + var i, j; + var selectedNode = params.nodes[0]; + var degrees = 2; + + // mark all nodes as hard to read. + for (let nodeId in allNodes) { + // nodeColors[nodeId] = allNodes[nodeId].color; + allNodes[nodeId].color = "rgba(200,200,200,0.5)"; + if (allNodes[nodeId].hiddenLabel === undefined) { + allNodes[nodeId].hiddenLabel = allNodes[nodeId].label; + allNodes[nodeId].label = undefined; + } + } + var connectedNodes = network.getConnectedNodes(selectedNode); + var allConnectedNodes = []; + + // get the second degree nodes + for (i = 1; i < degrees; i++) { + for (j = 0; j < connectedNodes.length; j++) { + allConnectedNodes = allConnectedNodes.concat( + network.getConnectedNodes(connectedNodes[j]) + ); + } + } + + // all second degree nodes get a different color and their label back + for (i = 0; i < allConnectedNodes.length; i++) { + // allNodes[allConnectedNodes[i]].color = "pink"; + allNodes[allConnectedNodes[i]].color = "rgba(150,150,150,0.75)"; + if (allNodes[allConnectedNodes[i]].hiddenLabel !== undefined) { + allNodes[allConnectedNodes[i]].label = + allNodes[allConnectedNodes[i]].hiddenLabel; + allNodes[allConnectedNodes[i]].hiddenLabel = undefined; + } + } + + // all first degree nodes get their own color and their label back + for (i = 0; i < connectedNodes.length; i++) { + // allNodes[connectedNodes[i]].color = undefined; + allNodes[connectedNodes[i]].color = nodeColors[connectedNodes[i]]; + if (allNodes[connectedNodes[i]].hiddenLabel !== undefined) { + allNodes[connectedNodes[i]].label = + allNodes[connectedNodes[i]].hiddenLabel; + allNodes[connectedNodes[i]].hiddenLabel = undefined; + } + } + + // the main node gets its own color and its label back. + // allNodes[selectedNode].color = undefined; + allNodes[selectedNode].color = nodeColors[selectedNode]; + if (allNodes[selectedNode].hiddenLabel !== undefined) { + allNodes[selectedNode].label = allNodes[selectedNode].hiddenLabel; + allNodes[selectedNode].hiddenLabel = undefined; + } + } else if (highlightActive === true) { + // console.log("highlightActive was true"); + // reset all nodes + for (let nodeId in allNodes) { + // allNodes[nodeId].color = "purple"; + allNodes[nodeId].color = nodeColors[nodeId]; + // delete allNodes[nodeId].color; + if (allNodes[nodeId].hiddenLabel !== undefined) { + allNodes[nodeId].label = allNodes[nodeId].hiddenLabel; + allNodes[nodeId].hiddenLabel = undefined; + } + } + highlightActive = false; + } + + // transform the object into an array + var updateArray = []; + if (params.nodes.length > 0) { + for (let nodeId in allNodes) { + if (allNodes.hasOwnProperty(nodeId)) { + // console.log(allNodes[nodeId]); + updateArray.push(allNodes[nodeId]); + } + } + nodes.update(updateArray); + } else { + // console.log("Nothing was selected"); + for (let nodeId in allNodes) { + if (allNodes.hasOwnProperty(nodeId)) { + // console.log(allNodes[nodeId]); + // allNodes[nodeId].color = {}; + updateArray.push(allNodes[nodeId]); + } + } + nodes.update(updateArray); + } +} + +function filterHighlight(params) { + allNodes = nodes.get({ returnType: "Object" }); + // if something is selected: + if (params.nodes.length > 0) { + filterActive = true; + let selectedNodes = params.nodes; + + // hiding all nodes and saving the label + for (let nodeId in allNodes) { + allNodes[nodeId].hidden = true; + if (allNodes[nodeId].savedLabel === undefined) { + allNodes[nodeId].savedLabel = allNodes[nodeId].label; + allNodes[nodeId].label = undefined; + } + } + + for (let i=0; i < selectedNodes.length; i++) { + allNodes[selectedNodes[i]].hidden = false; + if (allNodes[selectedNodes[i]].savedLabel !== undefined) { + allNodes[selectedNodes[i]].label = allNodes[selectedNodes[i]].savedLabel; + allNodes[selectedNodes[i]].savedLabel = undefined; + } + } + + } else if (filterActive === true) { + // reset all nodes + for (let nodeId in allNodes) { + allNodes[nodeId].hidden = false; + if (allNodes[nodeId].savedLabel !== undefined) { + allNodes[nodeId].label = allNodes[nodeId].savedLabel; + allNodes[nodeId].savedLabel = undefined; + } + } + filterActive = false; + } + + // transform the object into an array + var updateArray = []; + if (params.nodes.length > 0) { + for (let nodeId in allNodes) { + if (allNodes.hasOwnProperty(nodeId)) { + updateArray.push(allNodes[nodeId]); + } + } + nodes.update(updateArray); + } else { + for (let nodeId in allNodes) { + if (allNodes.hasOwnProperty(nodeId)) { + updateArray.push(allNodes[nodeId]); + } + } + nodes.update(updateArray); + } +} + +function selectNode(nodes) { + network.selectNodes(nodes); + neighbourhoodHighlight({ nodes: nodes }); + return nodes; +} + +function selectNodes(nodes) { + network.selectNodes(nodes); + filterHighlight({nodes: nodes}); + return nodes; +} + +function highlightFilter(filter) { + let selectedNodes = [] + let selectedProp = filter['property'] + if (filter['item'] === 'node') { + let allNodes = nodes.get({ returnType: "Object" }); + for (let nodeId in allNodes) { + if (allNodes[nodeId][selectedProp] && filter['value'].includes((allNodes[nodeId][selectedProp]).toString())) { + selectedNodes.push(nodeId) + } + } + } + else if (filter['item'] === 'edge'){ + let allEdges = edges.get({returnType: 'object'}); + // check if the selected property exists for selected edge and select the nodes connected to the edge + for (let edge in allEdges) { + if (allEdges[edge][selectedProp] && filter['value'].includes((allEdges[edge][selectedProp]).toString())) { + selectedNodes.push(allEdges[edge]['from']) + selectedNodes.push(allEdges[edge]['to']) + } + } + } + selectNodes(selectedNodes) +} \ No newline at end of file diff --git a/llamaindex-workflow/lib/tom-select/tom-select.complete.min.js b/llamaindex-workflow/lib/tom-select/tom-select.complete.min.js new file mode 100644 index 000000000..e2e0211fe --- /dev/null +++ b/llamaindex-workflow/lib/tom-select/tom-select.complete.min.js @@ -0,0 +1,356 @@ +/** +* Tom Select v2.0.0-rc.4 +* Licensed under the Apache License, Version 2.0 (the "License"); +*/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TomSelect=t()}(this,(function(){"use strict" +function e(e,t){e.split(/\s+/).forEach((e=>{t(e)}))}class t{constructor(){this._events={}}on(t,i){e(t,(e=>{this._events[e]=this._events[e]||[],this._events[e].push(i)}))}off(t,i){var s=arguments.length +0!==s?e(t,(e=>{if(1===s)return delete this._events[e] +e in this._events!=!1&&this._events[e].splice(this._events[e].indexOf(i),1)})):this._events={}}trigger(t,...i){var s=this +e(t,(e=>{if(e in s._events!=!1)for(let t of s._events[e])t.apply(s,i)}))}}var i +const s="[̀-ͯ·ʾ]",n=new RegExp(s,"g") +var o +const r={"æ":"ae","ⱥ":"a","ø":"o"},l=new RegExp(Object.keys(r).join("|"),"g"),a=[[67,67],[160,160],[192,438],[452,652],[961,961],[1019,1019],[1083,1083],[1281,1289],[1984,1984],[5095,5095],[7429,7441],[7545,7549],[7680,7935],[8580,8580],[9398,9449],[11360,11391],[42792,42793],[42802,42851],[42873,42897],[42912,42922],[64256,64260],[65313,65338],[65345,65370]],c=e=>e.normalize("NFKD").replace(n,"").toLowerCase().replace(l,(function(e){return r[e]})),d=(e,t="|")=>{if(1==e.length)return e[0] +var i=1 +return e.forEach((e=>{i=Math.max(i,e.length)})),1==i?"["+e.join("")+"]":"(?:"+e.join(t)+")"},p=e=>{if(1===e.length)return[[e]] +var t=[] +return p(e.substring(1)).forEach((function(i){var s=i.slice(0) +s[0]=e.charAt(0)+s[0],t.push(s),(s=i.slice(0)).unshift(e.charAt(0)),t.push(s)})),t},u=e=>{void 0===o&&(o=(()=>{var e={} +a.forEach((t=>{for(let s=t[0];s<=t[1];s++){let t=String.fromCharCode(s),n=c(t) +if(n!=t.toLowerCase()){n in e||(e[n]=[n]) +var i=new RegExp(d(e[n]),"iu") +t.match(i)||e[n].push(t)}}})) +var t=Object.keys(e) +t=t.sort(((e,t)=>t.length-e.length)),i=new RegExp("("+d(t)+"[̀-ͯ·ʾ]*)","g") +var s={} +return t.sort(((e,t)=>e.length-t.length)).forEach((t=>{var i=p(t).map((t=>(t=t.map((t=>e.hasOwnProperty(t)?d(e[t]):t)),d(t,"")))) +s[t]=d(i)})),s})()) +return e.normalize("NFKD").toLowerCase().split(i).map((e=>{if(""==e)return"" +const t=c(e) +if(o.hasOwnProperty(t))return o[t] +const i=e.normalize("NFC") +return i!=e?d([e,i]):e})).join("")},h=(e,t)=>{if(e)return e[t]},g=(e,t)=>{if(e){for(var i,s=t.split(".");(i=s.shift())&&(e=e[i]););return e}},f=(e,t,i)=>{var s,n +return e?-1===(n=(e+="").search(t.regex))?0:(s=t.string.length/e.length,0===n&&(s+=.5),s*i):0},v=e=>(e+"").replace(/([\$\(-\+\.\?\[-\^\{-\}])/g,"\\$1"),m=(e,t)=>{var i=e[t] +if("function"==typeof i)return i +i&&!Array.isArray(i)&&(e[t]=[i])},y=(e,t)=>{if(Array.isArray(e))e.forEach(t) +else for(var i in e)e.hasOwnProperty(i)&&t(e[i],i)},O=(e,t)=>"number"==typeof e&&"number"==typeof t?e>t?1:e(t=c(t+"").toLowerCase())?1:t>e?-1:0 +class b{constructor(e,t){this.items=e,this.settings=t||{diacritics:!0}}tokenize(e,t,i){if(!e||!e.length)return[] +const s=[],n=e.split(/\s+/) +var o +return i&&(o=new RegExp("^("+Object.keys(i).map(v).join("|")+"):(.*)$")),n.forEach((e=>{let i,n=null,r=null +o&&(i=e.match(o))&&(n=i[1],e=i[2]),e.length>0&&(r=v(e),this.settings.diacritics&&(r=u(r)),t&&(r="\\b"+r)),s.push({string:e,regex:r?new RegExp(r,"iu"):null,field:n})})),s}getScoreFunction(e,t){var i=this.prepareSearch(e,t) +return this._getScoreFunction(i)}_getScoreFunction(e){const t=e.tokens,i=t.length +if(!i)return function(){return 0} +const s=e.options.fields,n=e.weights,o=s.length,r=e.getAttrFn +if(!o)return function(){return 1} +const l=1===o?function(e,t){const i=s[0].field +return f(r(t,i),e,n[i])}:function(e,t){var i=0 +if(e.field){const s=r(t,e.field) +!e.regex&&s?i+=1/o:i+=f(s,e,1)}else y(n,((s,n)=>{i+=f(r(t,n),e,s)})) +return i/o} +return 1===i?function(e){return l(t[0],e)}:"and"===e.options.conjunction?function(e){for(var s,n=0,o=0;n{s+=l(t,e)})),s/i}}getSortFunction(e,t){var i=this.prepareSearch(e,t) +return this._getSortFunction(i)}_getSortFunction(e){var t,i,s +const n=this,o=e.options,r=!e.query&&o.sort_empty?o.sort_empty:o.sort,l=[],a=[] +if("function"==typeof r)return r.bind(this) +const c=function(t,i){return"$score"===t?i.score:e.getAttrFn(n.items[i.id],t)} +if(r)for(t=0,i=r.length;t{"string"==typeof t&&(t={field:t,weight:1}),e.push(t),i[t.field]="weight"in t?t.weight:1})),s.fields=e}return{options:s,query:e.toLowerCase().trim(),tokens:this.tokenize(e,s.respect_word_boundaries,i),total:0,items:[],weights:i,getAttrFn:s.nesting?g:h}}search(e,t){var i,s,n=this +s=this.prepareSearch(e,t),t=s.options,e=s.query +const o=t.score||n._getScoreFunction(s) +e.length?y(n.items,((e,n)=>{i=o(e),(!1===t.filter||i>0)&&s.items.push({score:i,id:n})})):y(n.items,((e,t)=>{s.items.push({score:1,id:t})})) +const r=n._getSortFunction(s) +return r&&s.items.sort(r),s.total=s.items.length,"number"==typeof t.limit&&(s.items=s.items.slice(0,t.limit)),s}}const w=e=>{if(e.jquery)return e[0] +if(e instanceof HTMLElement)return e +if(e.indexOf("<")>-1){let t=document.createElement("div") +return t.innerHTML=e.trim(),t.firstChild}return document.querySelector(e)},_=(e,t)=>{var i=document.createEvent("HTMLEvents") +i.initEvent(t,!0,!1),e.dispatchEvent(i)},I=(e,t)=>{Object.assign(e.style,t)},C=(e,...t)=>{var i=A(t);(e=x(e)).map((e=>{i.map((t=>{e.classList.add(t)}))}))},S=(e,...t)=>{var i=A(t);(e=x(e)).map((e=>{i.map((t=>{e.classList.remove(t)}))}))},A=e=>{var t=[] +return y(e,(e=>{"string"==typeof e&&(e=e.trim().split(/[\11\12\14\15\40]/)),Array.isArray(e)&&(t=t.concat(e))})),t.filter(Boolean)},x=e=>(Array.isArray(e)||(e=[e]),e),k=(e,t,i)=>{if(!i||i.contains(e))for(;e&&e.matches;){if(e.matches(t))return e +e=e.parentNode}},F=(e,t=0)=>t>0?e[e.length-1]:e[0],L=(e,t)=>{if(!e)return-1 +t=t||e.nodeName +for(var i=0;e=e.previousElementSibling;)e.matches(t)&&i++ +return i},P=(e,t)=>{y(t,((t,i)=>{null==t?e.removeAttribute(i):e.setAttribute(i,""+t)}))},E=(e,t)=>{e.parentNode&&e.parentNode.replaceChild(t,e)},T=(e,t)=>{if(null===t)return +if("string"==typeof t){if(!t.length)return +t=new RegExp(t,"i")}const i=e=>3===e.nodeType?(e=>{var i=e.data.match(t) +if(i&&e.data.length>0){var s=document.createElement("span") +s.className="highlight" +var n=e.splitText(i.index) +n.splitText(i[0].length) +var o=n.cloneNode(!0) +return s.appendChild(o),E(n,s),1}return 0})(e):((e=>{if(1===e.nodeType&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&("highlight"!==e.className||"SPAN"!==e.tagName))for(var t=0;t0},render:{}} +const q=e=>null==e?null:D(e),D=e=>"boolean"==typeof e?e?"1":"0":e+"",N=e=>(e+"").replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),z=(e,t)=>{var i +return function(s,n){var o=this +i&&(o.loading=Math.max(o.loading-1,0),clearTimeout(i)),i=setTimeout((function(){i=null,o.loadedSearches[s]=!0,e.call(o,s,n)}),t)}},R=(e,t,i)=>{var s,n=e.trigger,o={} +for(s in e.trigger=function(){var i=arguments[0] +if(-1===t.indexOf(i))return n.apply(e,arguments) +o[i]=arguments},i.apply(e,[]),e.trigger=n,o)n.apply(e,o[s])},H=(e,t=!1)=>{e&&(e.preventDefault(),t&&e.stopPropagation())},B=(e,t,i,s)=>{e.addEventListener(t,i,s)},K=(e,t)=>!!t&&(!!t[e]&&1===(t.altKey?1:0)+(t.ctrlKey?1:0)+(t.shiftKey?1:0)+(t.metaKey?1:0)),M=(e,t)=>{const i=e.getAttribute("id") +return i||(e.setAttribute("id",t),t)},Q=e=>e.replace(/[\\"']/g,"\\$&"),G=(e,t)=>{t&&e.append(t)} +function U(e,t){var i=Object.assign({},j,t),s=i.dataAttr,n=i.labelField,o=i.valueField,r=i.disabledField,l=i.optgroupField,a=i.optgroupLabelField,c=i.optgroupValueField,d=e.tagName.toLowerCase(),p=e.getAttribute("placeholder")||e.getAttribute("data-placeholder") +if(!p&&!i.allowEmptyOption){let t=e.querySelector('option[value=""]') +t&&(p=t.textContent)}var u,h,g,f,v,m,O={placeholder:p,options:[],optgroups:[],items:[],maxItems:null} +return"select"===d?(h=O.options,g={},f=1,v=e=>{var t=Object.assign({},e.dataset),i=s&&t[s] +return"string"==typeof i&&i.length&&(t=Object.assign(t,JSON.parse(i))),t},m=(e,t)=>{var s=q(e.value) +if(null!=s&&(s||i.allowEmptyOption)){if(g.hasOwnProperty(s)){if(t){var a=g[s][l] +a?Array.isArray(a)?a.push(t):g[s][l]=[a,t]:g[s][l]=t}}else{var c=v(e) +c[n]=c[n]||e.textContent,c[o]=c[o]||s,c[r]=c[r]||e.disabled,c[l]=c[l]||t,c.$option=e,g[s]=c,h.push(c)}e.selected&&O.items.push(s)}},O.maxItems=e.hasAttribute("multiple")?null:1,y(e.children,(e=>{var t,i,s +"optgroup"===(u=e.tagName.toLowerCase())?((s=v(t=e))[a]=s[a]||t.getAttribute("label")||"",s[c]=s[c]||f++,s[r]=s[r]||t.disabled,O.optgroups.push(s),i=s[c],y(t.children,(e=>{m(e,i)}))):"option"===u&&m(e)}))):(()=>{const t=e.getAttribute(s) +if(t)O.options=JSON.parse(t),y(O.options,(e=>{O.items.push(e[o])})) +else{var r=e.value.trim()||"" +if(!i.allowEmptyOption&&!r.length)return +const t=r.split(i.delimiter) +y(t,(e=>{const t={} +t[n]=e,t[o]=e,O.options.push(t)})),O.items=t}})(),Object.assign({},j,O,t)}var W=0 +class J extends(function(e){return e.plugins={},class extends e{constructor(...e){super(...e),this.plugins={names:[],settings:{},requested:{},loaded:{}}}static define(t,i){e.plugins[t]={name:t,fn:i}}initializePlugins(e){var t,i +const s=this,n=[] +if(Array.isArray(e))e.forEach((e=>{"string"==typeof e?n.push(e):(s.plugins.settings[e.name]=e.options,n.push(e.name))})) +else if(e)for(t in e)e.hasOwnProperty(t)&&(s.plugins.settings[t]=e[t],n.push(t)) +for(;i=n.shift();)s.require(i)}loadPlugin(t){var i=this,s=i.plugins,n=e.plugins[t] +if(!e.plugins.hasOwnProperty(t))throw new Error('Unable to find "'+t+'" plugin') +s.requested[t]=!0,s.loaded[t]=n.fn.apply(i,[i.plugins.settings[t]||{}]),s.names.push(t)}require(e){var t=this,i=t.plugins +if(!t.plugins.loaded.hasOwnProperty(e)){if(i.requested[e])throw new Error('Plugin has circular dependency ("'+e+'")') +t.loadPlugin(e)}return i.loaded[e]}}}(t)){constructor(e,t){var i +super(),this.order=0,this.isOpen=!1,this.isDisabled=!1,this.isInvalid=!1,this.isValid=!0,this.isLocked=!1,this.isFocused=!1,this.isInputHidden=!1,this.isSetup=!1,this.ignoreFocus=!1,this.hasOptions=!1,this.lastValue="",this.caretPos=0,this.loading=0,this.loadedSearches={},this.activeOption=null,this.activeItems=[],this.optgroups={},this.options={},this.userOptions={},this.items=[],W++ +var s=w(e) +if(s.tomselect)throw new Error("Tom Select already initialized on this element") +s.tomselect=this,i=(window.getComputedStyle&&window.getComputedStyle(s,null)).getPropertyValue("direction") +const n=U(s,t) +this.settings=n,this.input=s,this.tabIndex=s.tabIndex||0,this.is_select_tag="select"===s.tagName.toLowerCase(),this.rtl=/rtl/i.test(i),this.inputId=M(s,"tomselect-"+W),this.isRequired=s.required,this.sifter=new b(this.options,{diacritics:n.diacritics}),n.mode=n.mode||(1===n.maxItems?"single":"multi"),"boolean"!=typeof n.hideSelected&&(n.hideSelected="multi"===n.mode),"boolean"!=typeof n.hidePlaceholder&&(n.hidePlaceholder="multi"!==n.mode) +var o=n.createFilter +"function"!=typeof o&&("string"==typeof o&&(o=new RegExp(o)),o instanceof RegExp?n.createFilter=e=>o.test(e):n.createFilter=()=>!0),this.initializePlugins(n.plugins),this.setupCallbacks(),this.setupTemplates() +const r=w("
"),l=w("
"),a=this._render("dropdown"),c=w('
'),d=this.input.getAttribute("class")||"",p=n.mode +var u +if(C(r,n.wrapperClass,d,p),C(l,n.controlClass),G(r,l),C(a,n.dropdownClass,p),n.copyClassesToDropdown&&C(a,d),C(c,n.dropdownContentClass),G(a,c),w(n.dropdownParent||r).appendChild(a),n.hasOwnProperty("controlInput"))n.controlInput?(u=w(n.controlInput),this.focus_node=u):(u=w(""),this.focus_node=l) +else{u=w('') +y(["autocorrect","autocapitalize","autocomplete"],(e=>{s.getAttribute(e)&&P(u,{[e]:s.getAttribute(e)})})),u.tabIndex=-1,l.appendChild(u),this.focus_node=u}this.wrapper=r,this.dropdown=a,this.dropdown_content=c,this.control=l,this.control_input=u,this.setup()}setup(){const e=this,t=e.settings,i=e.control_input,s=e.dropdown,n=e.dropdown_content,o=e.wrapper,r=e.control,l=e.input,a=e.focus_node,c={passive:!0},d=e.inputId+"-ts-dropdown" +P(n,{id:d}),P(a,{role:"combobox","aria-haspopup":"listbox","aria-expanded":"false","aria-controls":d}) +const p=M(a,e.inputId+"-ts-control"),u="label[for='"+(e=>e.replace(/['"\\]/g,"\\$&"))(e.inputId)+"']",h=document.querySelector(u),g=e.focus.bind(e) +if(h){B(h,"click",g),P(h,{for:p}) +const t=M(h,e.inputId+"-ts-label") +P(a,{"aria-labelledby":t}),P(n,{"aria-labelledby":t})}if(o.style.width=l.style.width,e.plugins.names.length){const t="plugin-"+e.plugins.names.join(" plugin-") +C([o,s],t)}(null===t.maxItems||t.maxItems>1)&&e.is_select_tag&&P(l,{multiple:"multiple"}),e.settings.placeholder&&P(i,{placeholder:t.placeholder}),!e.settings.splitOn&&e.settings.delimiter&&(e.settings.splitOn=new RegExp("\\s*"+v(e.settings.delimiter)+"+\\s*")),t.load&&t.loadThrottle&&(t.load=z(t.load,t.loadThrottle)),e.control_input.type=l.type,B(s,"click",(t=>{const i=k(t.target,"[data-selectable]") +i&&(e.onOptionSelect(t,i),H(t,!0))})),B(r,"click",(t=>{var s=k(t.target,"[data-ts-item]",r) +s&&e.onItemSelect(t,s)?H(t,!0):""==i.value&&(e.onClick(),H(t,!0))})),B(i,"mousedown",(e=>{""!==i.value&&e.stopPropagation()})),B(a,"keydown",(t=>e.onKeyDown(t))),B(i,"keypress",(t=>e.onKeyPress(t))),B(i,"input",(t=>e.onInput(t))),B(a,"resize",(()=>e.positionDropdown()),c),B(a,"blur",(t=>e.onBlur(t))),B(a,"focus",(t=>e.onFocus(t))),B(a,"paste",(t=>e.onPaste(t))) +const f=t=>{const i=t.composedPath()[0] +if(!o.contains(i)&&!s.contains(i))return e.isFocused&&e.blur(),void e.inputState() +H(t,!0)} +var m=()=>{e.isOpen&&e.positionDropdown()} +B(document,"mousedown",f),B(window,"scroll",m,c),B(window,"resize",m,c),this._destroy=()=>{document.removeEventListener("mousedown",f),window.removeEventListener("sroll",m),window.removeEventListener("resize",m),h&&h.removeEventListener("click",g)},this.revertSettings={innerHTML:l.innerHTML,tabIndex:l.tabIndex},l.tabIndex=-1,l.insertAdjacentElement("afterend",e.wrapper),e.sync(!1),t.items=[],delete t.optgroups,delete t.options,B(l,"invalid",(t=>{e.isValid&&(e.isValid=!1,e.isInvalid=!0,e.refreshState())})),e.updateOriginalInput(),e.refreshItems(),e.close(!1),e.inputState(),e.isSetup=!0,l.disabled?e.disable():e.enable(),e.on("change",this.onChange),C(l,"tomselected","ts-hidden-accessible"),e.trigger("initialize"),!0===t.preload&&e.preload()}setupOptions(e=[],t=[]){this.addOptions(e),y(t,(e=>{this.registerOptionGroup(e)}))}setupTemplates(){var e=this,t=e.settings.labelField,i=e.settings.optgroupLabelField,s={optgroup:e=>{let t=document.createElement("div") +return t.className="optgroup",t.appendChild(e.options),t},optgroup_header:(e,t)=>'
'+t(e[i])+"
",option:(e,i)=>"
"+i(e[t])+"
",item:(e,i)=>"
"+i(e[t])+"
",option_create:(e,t)=>'
Add '+t(e.input)+"
",no_results:()=>'
No results found
',loading:()=>'
',not_loading:()=>{},dropdown:()=>"
"} +e.settings.render=Object.assign({},s,e.settings.render)}setupCallbacks(){var e,t,i={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",item_select:"onItemSelect",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"} +for(e in i)(t=this.settings[i[e]])&&this.on(e,t)}sync(e=!0){const t=this,i=e?U(t.input,{delimiter:t.settings.delimiter}):t.settings +t.setupOptions(i.options,i.optgroups),t.setValue(i.items,!0),t.lastQuery=null}onClick(){var e=this +if(e.activeItems.length>0)return e.clearActiveItems(),void e.focus() +e.isFocused&&e.isOpen?e.blur():e.focus()}onMouseDown(){}onChange(){_(this.input,"input"),_(this.input,"change")}onPaste(e){var t=this +t.isFull()||t.isInputHidden||t.isLocked?H(e):t.settings.splitOn&&setTimeout((()=>{var e=t.inputValue() +if(e.match(t.settings.splitOn)){var i=e.trim().split(t.settings.splitOn) +y(i,(e=>{t.createItem(e)}))}}),0)}onKeyPress(e){var t=this +if(!t.isLocked){var i=String.fromCharCode(e.keyCode||e.which) +return t.settings.create&&"multi"===t.settings.mode&&i===t.settings.delimiter?(t.createItem(),void H(e)):void 0}H(e)}onKeyDown(e){var t=this +if(t.isLocked)9!==e.keyCode&&H(e) +else{switch(e.keyCode){case 65:if(K(V,e))return H(e),void t.selectAll() +break +case 27:return t.isOpen&&(H(e,!0),t.close()),void t.clearActiveItems() +case 40:if(!t.isOpen&&t.hasOptions)t.open() +else if(t.activeOption){let e=t.getAdjacent(t.activeOption,1) +e&&t.setActiveOption(e)}return void H(e) +case 38:if(t.activeOption){let e=t.getAdjacent(t.activeOption,-1) +e&&t.setActiveOption(e)}return void H(e) +case 13:return void(t.isOpen&&t.activeOption?(t.onOptionSelect(e,t.activeOption),H(e)):t.settings.create&&t.createItem()&&H(e)) +case 37:return void t.advanceSelection(-1,e) +case 39:return void t.advanceSelection(1,e) +case 9:return void(t.settings.selectOnTab&&(t.isOpen&&t.activeOption&&(t.onOptionSelect(e,t.activeOption),H(e)),t.settings.create&&t.createItem()&&H(e))) +case 8:case 46:return void t.deleteSelection(e)}t.isInputHidden&&!K(V,e)&&H(e)}}onInput(e){var t=this +if(!t.isLocked){var i=t.inputValue() +t.lastValue!==i&&(t.lastValue=i,t.settings.shouldLoad.call(t,i)&&t.load(i),t.refreshOptions(),t.trigger("type",i))}}onFocus(e){var t=this,i=t.isFocused +if(t.isDisabled)return t.blur(),void H(e) +t.ignoreFocus||(t.isFocused=!0,"focus"===t.settings.preload&&t.preload(),i||t.trigger("focus"),t.activeItems.length||(t.showInput(),t.refreshOptions(!!t.settings.openOnFocus)),t.refreshState())}onBlur(e){if(!1!==document.hasFocus()){var t=this +if(t.isFocused){t.isFocused=!1,t.ignoreFocus=!1 +var i=()=>{t.close(),t.setActiveItem(),t.setCaret(t.items.length),t.trigger("blur")} +t.settings.create&&t.settings.createOnBlur?t.createItem(null,!1,i):i()}}}onOptionSelect(e,t){var i,s=this +t&&(t.parentElement&&t.parentElement.matches("[data-disabled]")||(t.classList.contains("create")?s.createItem(null,!0,(()=>{s.settings.closeAfterSelect&&s.close()})):void 0!==(i=t.dataset.value)&&(s.lastQuery=null,s.addItem(i),s.settings.closeAfterSelect&&s.close(),!s.settings.hideSelected&&e.type&&/click/.test(e.type)&&s.setActiveOption(t))))}onItemSelect(e,t){var i=this +return!i.isLocked&&"multi"===i.settings.mode&&(H(e),i.setActiveItem(t,e),!0)}canLoad(e){return!!this.settings.load&&!this.loadedSearches.hasOwnProperty(e)}load(e){const t=this +if(!t.canLoad(e))return +C(t.wrapper,t.settings.loadingClass),t.loading++ +const i=t.loadCallback.bind(t) +t.settings.load.call(t,e,i)}loadCallback(e,t){const i=this +i.loading=Math.max(i.loading-1,0),i.lastQuery=null,i.clearActiveOption(),i.setupOptions(e,t),i.refreshOptions(i.isFocused&&!i.isInputHidden),i.loading||S(i.wrapper,i.settings.loadingClass),i.trigger("load",e,t)}preload(){var e=this.wrapper.classList +e.contains("preloaded")||(e.add("preloaded"),this.load(""))}setTextboxValue(e=""){var t=this.control_input +t.value!==e&&(t.value=e,_(t,"update"),this.lastValue=e)}getValue(){return this.is_select_tag&&this.input.hasAttribute("multiple")?this.items:this.items.join(this.settings.delimiter)}setValue(e,t){R(this,t?[]:["change"],(()=>{this.clear(t),this.addItems(e,t)}))}setMaxItems(e){0===e&&(e=null),this.settings.maxItems=e,this.refreshState()}setActiveItem(e,t){var i,s,n,o,r,l,a=this +if("single"!==a.settings.mode){if(!e)return a.clearActiveItems(),void(a.isFocused&&a.showInput()) +if("click"===(i=t&&t.type.toLowerCase())&&K("shiftKey",t)&&a.activeItems.length){for(l=a.getLastActive(),(n=Array.prototype.indexOf.call(a.control.children,l))>(o=Array.prototype.indexOf.call(a.control.children,e))&&(r=n,n=o,o=r),s=n;s<=o;s++)e=a.control.children[s],-1===a.activeItems.indexOf(e)&&a.setActiveItemClass(e) +H(t)}else"click"===i&&K(V,t)||"keydown"===i&&K("shiftKey",t)?e.classList.contains("active")?a.removeActiveItem(e):a.setActiveItemClass(e):(a.clearActiveItems(),a.setActiveItemClass(e)) +a.hideInput(),a.isFocused||a.focus()}}setActiveItemClass(e){const t=this,i=t.control.querySelector(".last-active") +i&&S(i,"last-active"),C(e,"active last-active"),t.trigger("item_select",e),-1==t.activeItems.indexOf(e)&&t.activeItems.push(e)}removeActiveItem(e){var t=this.activeItems.indexOf(e) +this.activeItems.splice(t,1),S(e,"active")}clearActiveItems(){S(this.activeItems,"active"),this.activeItems=[]}setActiveOption(e){e!==this.activeOption&&(this.clearActiveOption(),e&&(this.activeOption=e,P(this.focus_node,{"aria-activedescendant":e.getAttribute("id")}),P(e,{"aria-selected":"true"}),C(e,"active"),this.scrollToOption(e)))}scrollToOption(e,t){if(!e)return +const i=this.dropdown_content,s=i.clientHeight,n=i.scrollTop||0,o=e.offsetHeight,r=e.getBoundingClientRect().top-i.getBoundingClientRect().top+n +r+o>s+n?this.scroll(r-s+o,t):r0||!e.isFocused&&e.settings.hidePlaceholder&&e.items.length>0?(e.setTextboxValue(),e.isInputHidden=!0):(e.settings.hidePlaceholder&&e.items.length>0&&P(e.control_input,{placeholder:""}),e.isInputHidden=!1),e.wrapper.classList.toggle("input-hidden",e.isInputHidden))}hideInput(){this.inputState()}showInput(){this.inputState()}inputValue(){return this.control_input.value.trim()}focus(){var e=this +e.isDisabled||(e.ignoreFocus=!0,e.control_input.offsetWidth?e.control_input.focus():e.focus_node.focus(),setTimeout((()=>{e.ignoreFocus=!1,e.onFocus()}),0))}blur(){this.focus_node.blur(),this.onBlur()}getScoreFunction(e){return this.sifter.getScoreFunction(e,this.getSearchOptions())}getSearchOptions(){var e=this.settings,t=e.sortField +return"string"==typeof e.sortField&&(t=[{field:e.sortField}]),{fields:e.searchField,conjunction:e.searchConjunction,sort:t,nesting:e.nesting}}search(e){var t,i,s,n=this,o=this.getSearchOptions() +if(n.settings.score&&"function"!=typeof(s=n.settings.score.call(n,e)))throw new Error('Tom Select "score" setting must be a function that returns a function') +if(e!==n.lastQuery?(n.lastQuery=e,i=n.sifter.search(e,Object.assign(o,{score:s})),n.currentResults=i):i=Object.assign({},n.currentResults),n.settings.hideSelected)for(t=i.items.length-1;t>=0;t--){let e=q(i.items[t].id) +e&&-1!==n.items.indexOf(e)&&i.items.splice(t,1)}return i}refreshOptions(e=!0){var t,i,s,n,o,r,l,a,c,d,p +const u={},h=[] +var g,f=this,v=f.inputValue(),m=f.search(v),O=f.activeOption,b=f.settings.shouldOpen||!1,w=f.dropdown_content +for(O&&(c=O.dataset.value,d=O.closest("[data-group]")),n=m.items.length,"number"==typeof f.settings.maxOptions&&(n=Math.min(n,f.settings.maxOptions)),n>0&&(b=!0),t=0;t0&&(l=l.cloneNode(!0),P(l,{id:n.$id+"-clone-"+i,"aria-selected":null}),l.classList.add("ts-cloned"),S(l,"active")),c==e&&d&&d.dataset.group===o&&(O=l),u[o].appendChild(l)}this.settings.lockOptgroupOrder&&h.sort(((e,t)=>(f.optgroups[e]&&f.optgroups[e].$order||0)-(f.optgroups[t]&&f.optgroups[t].$order||0))),l=document.createDocumentFragment(),y(h,(e=>{if(f.optgroups.hasOwnProperty(e)&&u[e].children.length){let t=document.createDocumentFragment(),i=f.render("optgroup_header",f.optgroups[e]) +G(t,i),G(t,u[e]) +let s=f.render("optgroup",{group:f.optgroups[e],options:t}) +G(l,s)}else G(l,u[e])})),w.innerHTML="",G(w,l),f.settings.highlight&&(g=w.querySelectorAll("span.highlight"),Array.prototype.forEach.call(g,(function(e){var t=e.parentNode +t.replaceChild(e.firstChild,e),t.normalize()})),m.query.length&&m.tokens.length&&y(m.tokens,(e=>{T(w,e.regex)}))) +var _=e=>{let t=f.render(e,{input:v}) +return t&&(b=!0,w.insertBefore(t,w.firstChild)),t} +if(f.loading?_("loading"):f.settings.shouldLoad.call(f,v)?0===m.items.length&&_("no_results"):_("not_loading"),(a=f.canCreate(v))&&(p=_("option_create")),f.hasOptions=m.items.length>0||a,b){if(m.items.length>0){if(!w.contains(O)&&"single"===f.settings.mode&&f.items.length&&(O=f.getOption(f.items[0])),!w.contains(O)){let e=0 +p&&!f.settings.addPrecedence&&(e=1),O=f.selectable()[e]}}else p&&(O=p) +e&&!f.isOpen&&(f.open(),f.scrollToOption(O,"auto")),f.setActiveOption(O)}else f.clearActiveOption(),e&&f.isOpen&&f.close(!1)}selectable(){return this.dropdown_content.querySelectorAll("[data-selectable]")}addOption(e,t=!1){const i=this +if(Array.isArray(e))return i.addOptions(e,t),!1 +const s=q(e[i.settings.valueField]) +return null!==s&&!i.options.hasOwnProperty(s)&&(e.$order=e.$order||++i.order,e.$id=i.inputId+"-opt-"+e.$order,i.options[s]=e,i.lastQuery=null,t&&(i.userOptions[s]=t,i.trigger("option_add",s,e)),s)}addOptions(e,t=!1){y(e,(e=>{this.addOption(e,t)}))}registerOption(e){return this.addOption(e)}registerOptionGroup(e){var t=q(e[this.settings.optgroupValueField]) +return null!==t&&(e.$order=e.$order||++this.order,this.optgroups[t]=e,t)}addOptionGroup(e,t){var i +t[this.settings.optgroupValueField]=e,(i=this.registerOptionGroup(t))&&this.trigger("optgroup_add",i,t)}removeOptionGroup(e){this.optgroups.hasOwnProperty(e)&&(delete this.optgroups[e],this.clearCache(),this.trigger("optgroup_remove",e))}clearOptionGroups(){this.optgroups={},this.clearCache(),this.trigger("optgroup_clear")}updateOption(e,t){const i=this +var s,n +const o=q(e),r=q(t[i.settings.valueField]) +if(null===o)return +if(!i.options.hasOwnProperty(o))return +if("string"!=typeof r)throw new Error("Value must be set in option data") +const l=i.getOption(o),a=i.getItem(o) +if(t.$order=t.$order||i.options[o].$order,delete i.options[o],i.uncacheValue(r),i.options[r]=t,l){if(i.dropdown_content.contains(l)){const e=i._render("option",t) +E(l,e),i.activeOption===l&&i.setActiveOption(e)}l.remove()}a&&(-1!==(n=i.items.indexOf(o))&&i.items.splice(n,1,r),s=i._render("item",t),a.classList.contains("active")&&C(s,"active"),E(a,s)),i.lastQuery=null}removeOption(e,t){const i=this +e=D(e),i.uncacheValue(e),delete i.userOptions[e],delete i.options[e],i.lastQuery=null,i.trigger("option_remove",e),i.removeItem(e,t)}clearOptions(){this.loadedSearches={},this.userOptions={},this.clearCache() +var e={} +y(this.options,((t,i)=>{this.items.indexOf(i)>=0&&(e[i]=this.options[i])})),this.options=this.sifter.items=e,this.lastQuery=null,this.trigger("option_clear")}getOption(e,t=!1){const i=q(e) +if(null!==i&&this.options.hasOwnProperty(i)){const e=this.options[i] +if(e.$div)return e.$div +if(t)return this._render("option",e)}return null}getAdjacent(e,t,i="option"){var s +if(!e)return null +s="item"==i?this.controlChildren():this.dropdown_content.querySelectorAll("[data-selectable]") +for(let i=0;i0?s[i+1]:s[i-1] +return null}getItem(e){if("object"==typeof e)return e +var t=q(e) +return null!==t?this.control.querySelector(`[data-value="${Q(t)}"]`):null}addItems(e,t){var i=this,s=Array.isArray(e)?e:[e] +for(let e=0,n=(s=s.filter((e=>-1===i.items.indexOf(e)))).length;e{var i,s +const n=this,o=n.settings.mode,r=q(e) +if((!r||-1===n.items.indexOf(r)||("single"===o&&n.close(),"single"!==o&&n.settings.duplicates))&&null!==r&&n.options.hasOwnProperty(r)&&("single"===o&&n.clear(t),"multi"!==o||!n.isFull())){if(i=n._render("item",n.options[r]),n.control.contains(i)&&(i=i.cloneNode(!0)),s=n.isFull(),n.items.splice(n.caretPos,0,r),n.insertAtCaret(i),n.isSetup){if(!n.isPending&&n.settings.hideSelected){let e=n.getOption(r),t=n.getAdjacent(e,1) +t&&n.setActiveOption(t)}n.isPending||n.refreshOptions(n.isFocused&&"single"!==o),0!=n.settings.closeAfterSelect&&n.isFull()?n.close():n.isPending||n.positionDropdown(),n.trigger("item_add",r,i),n.isPending||n.updateOriginalInput({silent:t})}(!n.isPending||!s&&n.isFull())&&(n.inputState(),n.refreshState())}}))}removeItem(e=null,t){const i=this +if(!(e=i.getItem(e)))return +var s,n +const o=e.dataset.value +s=L(e),e.remove(),e.classList.contains("active")&&(n=i.activeItems.indexOf(e),i.activeItems.splice(n,1),S(e,"active")),i.items.splice(s,1),i.lastQuery=null,!i.settings.persist&&i.userOptions.hasOwnProperty(o)&&i.removeOption(o,t),s{})){var s,n=this,o=n.caretPos +if(e=e||n.inputValue(),!n.canCreate(e))return i(),!1 +n.lock() +var r=!1,l=e=>{if(n.unlock(),!e||"object"!=typeof e)return i() +var s=q(e[n.settings.valueField]) +if("string"!=typeof s)return i() +n.setTextboxValue(),n.addOption(e,!0),n.setCaret(o),n.addItem(s),n.refreshOptions(t&&"single"!==n.settings.mode),i(e),r=!0} +return s="function"==typeof n.settings.create?n.settings.create.call(this,e,l):{[n.settings.labelField]:e,[n.settings.valueField]:e},r||l(s),!0}refreshItems(){var e=this +e.lastQuery=null,e.isSetup&&e.addItems(e.items),e.updateOriginalInput(),e.refreshState()}refreshState(){const e=this +e.refreshValidityState() +const t=e.isFull(),i=e.isLocked +e.wrapper.classList.toggle("rtl",e.rtl) +const s=e.wrapper.classList +var n +s.toggle("focus",e.isFocused),s.toggle("disabled",e.isDisabled),s.toggle("required",e.isRequired),s.toggle("invalid",!e.isValid),s.toggle("locked",i),s.toggle("full",t),s.toggle("input-active",e.isFocused&&!e.isInputHidden),s.toggle("dropdown-active",e.isOpen),s.toggle("has-options",(n=e.options,0===Object.keys(n).length)),s.toggle("has-items",e.items.length>0)}refreshValidityState(){var e=this +e.input.checkValidity&&(e.isValid=e.input.checkValidity(),e.isInvalid=!e.isValid)}isFull(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems}updateOriginalInput(e={}){const t=this +var i,s +const n=t.input.querySelector('option[value=""]') +if(t.is_select_tag){const e=[] +function o(i,s,o){return i||(i=w('")),i!=n&&t.input.append(i),e.push(i),i.selected=!0,i}t.input.querySelectorAll("option:checked").forEach((e=>{e.selected=!1})),0==t.items.length&&"single"==t.settings.mode?o(n,"",""):t.items.forEach((n=>{if(i=t.options[n],s=i[t.settings.labelField]||"",e.includes(i.$option)){o(t.input.querySelector(`option[value="${Q(n)}"]:not(:checked)`),n,s)}else i.$option=o(i.$option,n,s)}))}else t.input.value=t.getValue() +t.isSetup&&(e.silent||t.trigger("change",t.getValue()))}open(){var e=this +e.isLocked||e.isOpen||"multi"===e.settings.mode&&e.isFull()||(e.isOpen=!0,P(e.focus_node,{"aria-expanded":"true"}),e.refreshState(),I(e.dropdown,{visibility:"hidden",display:"block"}),e.positionDropdown(),I(e.dropdown,{visibility:"visible",display:"block"}),e.focus(),e.trigger("dropdown_open",e.dropdown))}close(e=!0){var t=this,i=t.isOpen +e&&(t.setTextboxValue(),"single"===t.settings.mode&&t.items.length&&t.hideInput()),t.isOpen=!1,P(t.focus_node,{"aria-expanded":"false"}),I(t.dropdown,{display:"none"}),t.settings.hideSelected&&t.clearActiveOption(),t.refreshState(),i&&t.trigger("dropdown_close",t.dropdown)}positionDropdown(){if("body"===this.settings.dropdownParent){var e=this.control,t=e.getBoundingClientRect(),i=e.offsetHeight+t.top+window.scrollY,s=t.left+window.scrollX +I(this.dropdown,{width:t.width+"px",top:i+"px",left:s+"px"})}}clear(e){var t=this +if(t.items.length){var i=t.controlChildren() +y(i,(e=>{t.removeItem(e,!0)})),t.showInput(),e||t.updateOriginalInput(),t.trigger("clear")}}insertAtCaret(e){const t=this,i=t.caretPos,s=t.control +s.insertBefore(e,s.children[i]),t.setCaret(i+1)}deleteSelection(e){var t,i,s,n,o,r=this +t=e&&8===e.keyCode?-1:1,i={start:(o=r.control_input).selectionStart||0,length:(o.selectionEnd||0)-(o.selectionStart||0)} +const l=[] +if(r.activeItems.length)n=F(r.activeItems,t),s=L(n),t>0&&s++,y(r.activeItems,(e=>l.push(e))) +else if((r.isFocused||"single"===r.settings.mode)&&r.items.length){const e=r.controlChildren() +t<0&&0===i.start&&0===i.length?l.push(e[r.caretPos-1]):t>0&&i.start===r.inputValue().length&&l.push(e[r.caretPos])}const a=l.map((e=>e.dataset.value)) +if(!a.length||"function"==typeof r.settings.onDelete&&!1===r.settings.onDelete.call(r,a,e))return!1 +for(H(e,!0),void 0!==s&&r.setCaret(s);l.length;)r.removeItem(l.pop()) +return r.showInput(),r.positionDropdown(),r.refreshOptions(!1),!0}advanceSelection(e,t){var i,s,n=this +n.rtl&&(e*=-1),n.inputValue().length||(K(V,t)||K("shiftKey",t)?(s=(i=n.getLastActive(e))?i.classList.contains("active")?n.getAdjacent(i,e,"item"):i:e>0?n.control_input.nextElementSibling:n.control_input.previousElementSibling)&&(s.classList.contains("active")&&n.removeActiveItem(i),n.setActiveItemClass(s)):n.moveCaret(e))}moveCaret(e){}getLastActive(e){let t=this.control.querySelector(".last-active") +if(t)return t +var i=this.control.querySelectorAll(".active") +return i?F(i,e):void 0}setCaret(e){this.caretPos=this.items.length}controlChildren(){return Array.from(this.control.querySelectorAll("[data-ts-item]"))}lock(){this.close(),this.isLocked=!0,this.refreshState()}unlock(){this.isLocked=!1,this.refreshState()}disable(){var e=this +e.input.disabled=!0,e.control_input.disabled=!0,e.focus_node.tabIndex=-1,e.isDisabled=!0,e.lock()}enable(){var e=this +e.input.disabled=!1,e.control_input.disabled=!1,e.focus_node.tabIndex=e.tabIndex,e.isDisabled=!1,e.unlock()}destroy(){var e=this,t=e.revertSettings +e.trigger("destroy"),e.off(),e.wrapper.remove(),e.dropdown.remove(),e.input.innerHTML=t.innerHTML,e.input.tabIndex=t.tabIndex,S(e.input,"tomselected","ts-hidden-accessible"),e._destroy(),delete e.input.tomselect}render(e,t){return"function"!=typeof this.settings.render[e]?null:this._render(e,t)}_render(e,t){var i,s,n="" +const o=this +return"option"!==e&&"item"!=e||(n=D(t[o.settings.valueField])),null==(s=o.settings.render[e].call(this,t,N))||(s=w(s),"option"===e||"option_create"===e?t[o.settings.disabledField]?P(s,{"aria-disabled":"true"}):P(s,{"data-selectable":""}):"optgroup"===e&&(i=t.group[o.settings.optgroupValueField],P(s,{"data-group":i}),t.group[o.settings.disabledField]&&P(s,{"data-disabled":""})),"option"!==e&&"item"!==e||(P(s,{"data-value":n}),"item"===e?(C(s,o.settings.itemClass),P(s,{"data-ts-item":""})):(C(s,o.settings.optionClass),P(s,{role:"option",id:t.$id}),o.options[n].$div=s))),s}clearCache(){y(this.options,((e,t)=>{e.$div&&(e.$div.remove(),delete e.$div)}))}uncacheValue(e){const t=this.getOption(e) +t&&t.remove()}canCreate(e){return this.settings.create&&e.length>0&&this.settings.createFilter.call(this,e)}hook(e,t,i){var s=this,n=s[t] +s[t]=function(){var t,o +return"after"===e&&(t=n.apply(s,arguments)),o=i.apply(s,arguments),"instead"===e?o:("before"===e&&(t=n.apply(s,arguments)),t)}}}return J.define("change_listener",(function(){B(this.input,"change",(()=>{this.sync()}))})),J.define("checkbox_options",(function(){var e=this,t=e.onOptionSelect +e.settings.hideSelected=!1 +var i=function(e){setTimeout((()=>{var t=e.querySelector("input") +e.classList.contains("selected")?t.checked=!0:t.checked=!1}),1)} +e.hook("after","setupTemplates",(()=>{var t=e.settings.render.option +e.settings.render.option=(i,s)=>{var n=w(t.call(e,i,s)),o=document.createElement("input") +o.addEventListener("click",(function(e){H(e)})),o.type="checkbox" +const r=q(i[e.settings.valueField]) +return r&&e.items.indexOf(r)>-1&&(o.checked=!0),n.prepend(o),n}})),e.on("item_remove",(t=>{var s=e.getOption(t) +s&&(s.classList.remove("selected"),i(s))})),e.hook("instead","onOptionSelect",((s,n)=>{if(n.classList.contains("selected"))return n.classList.remove("selected"),e.removeItem(n.dataset.value),e.refreshOptions(),void H(s,!0) +t.call(e,s,n),i(n)}))})),J.define("clear_button",(function(e){const t=this,i=Object.assign({className:"clear-button",title:"Clear All",html:e=>`
×
`},e) +t.on("initialize",(()=>{var e=w(i.html(i)) +e.addEventListener("click",(e=>{t.clear(),"single"===t.settings.mode&&t.settings.allowEmptyOption&&t.addItem(""),e.preventDefault(),e.stopPropagation()})),t.control.appendChild(e)}))})),J.define("drag_drop",(function(){var e=this +if(!$.fn.sortable)throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".') +if("multi"===e.settings.mode){var t=e.lock,i=e.unlock +e.hook("instead","lock",(()=>{var i=$(e.control).data("sortable") +return i&&i.disable(),t.call(e)})),e.hook("instead","unlock",(()=>{var t=$(e.control).data("sortable") +return t&&t.enable(),i.call(e)})),e.on("initialize",(()=>{var t=$(e.control).sortable({items:"[data-value]",forcePlaceholderSize:!0,disabled:e.isLocked,start:(e,i)=>{i.placeholder.css("width",i.helper.css("width")),t.css({overflow:"visible"})},stop:()=>{t.css({overflow:"hidden"}) +var i=[] +t.children("[data-value]").each((function(){this.dataset.value&&i.push(this.dataset.value)})),e.setValue(i)}})}))}})),J.define("dropdown_header",(function(e){const t=this,i=Object.assign({title:"Untitled",headerClass:"dropdown-header",titleRowClass:"dropdown-header-title",labelClass:"dropdown-header-label",closeClass:"dropdown-header-close",html:e=>'
'+e.title+'×
'},e) +t.on("initialize",(()=>{var e=w(i.html(i)),s=e.querySelector("."+i.closeClass) +s&&s.addEventListener("click",(e=>{H(e,!0),t.close()})),t.dropdown.insertBefore(e,t.dropdown.firstChild)}))})),J.define("caret_position",(function(){var e=this +e.hook("instead","setCaret",(t=>{"single"!==e.settings.mode&&e.control.contains(e.control_input)?(t=Math.max(0,Math.min(e.items.length,t)))==e.caretPos||e.isPending||e.controlChildren().forEach(((i,s)=>{s{if(!e.isFocused)return +const i=e.getLastActive(t) +if(i){const s=L(i) +e.setCaret(t>0?s+1:s),e.setActiveItem()}else e.setCaret(e.caretPos+t)}))})),J.define("dropdown_input",(function(){var e=this +e.settings.shouldOpen=!0,e.hook("before","setup",(()=>{e.focus_node=e.control,C(e.control_input,"dropdown-input") +const t=w('