diff --git a/src/.quarto/_freeze/notebooks/advanced_rag/execute-results/html.json b/src/.quarto/_freeze/notebooks/advanced_rag/execute-results/html.json new file mode 100644 index 0000000000000000000000000000000000000000..571c858b2647bbaf531de9fac0e261fdde0a3c02 --- /dev/null +++ b/src/.quarto/_freeze/notebooks/advanced_rag/execute-results/html.json @@ -0,0 +1,12 @@ +{ + "hash": "203f7c8e7bfe8fa1357b5554be09d30a", + "result": { + "engine": "jupyter", + "markdown": "---\ntitle: Advanced RAG\njupyter: python3\neval: false\ncode-annotations: hover\n---\n\n\n\n\n\nThis notebook demonstrates how you can build an advanced RAG (Retrieval Augmented Generation) for answering a user's question about a specific knowledge base (here, the HuggingFace documentation), using LangChain.\n\nFor an introduction to RAG, you can check [this other cookbook](rag_zephyr_langchain.qmd)!\n\nRAG systems are complex, with many moving parts: here a RAG diagram, where we noted in blue all possibilities for system enhancement:\n\n\n\n::: callout-note\nπŸ’‘ As you can see, there are many steps to tune in this architecture: tuning the system properly will yield significant performance gains.\n:::\n\nIn this notebook, we will take a look into many of these blue notes to see how to tune your RAG system and get the best performance.\n\n__Let's dig into the model building!__ First, we install the required model dependancies.\n\n::: {#7d75abe1 .cell execution_count=1}\n``` {.python .cell-code}\n!pip install -q torch transformers transformers accelerate bitsandbytes langchain sentence-transformers faiss-gpu openpyxl pacmap\n```\n:::\n\n\n::: {#2a33957c .cell execution_count=2}\n``` {.python .cell-code}\n%reload_ext dotenv\n%dotenv\n```\n:::\n\n\n::: {#d3507708 .cell execution_count=3}\n``` {.python .cell-code}\nfrom tqdm.notebook import tqdm\nimport pandas as pd\nfrom typing import Optional, List, Tuple\nfrom datasets import Dataset\nimport matplotlib.pyplot as plt\n\npd.set_option(\n \"display.max_colwidth\", None # <1>\n) \n```\n:::\n\n\n1. This will be helpful when visualizing retriever outputs\n\n### Load your knowledge base\n\n::: {#acfee5b8 .cell execution_count=4}\n``` {.python .cell-code}\nimport datasets\n\nds = datasets.load_dataset(\"m-ric/huggingface_doc\", split=\"train\")\n```\n:::\n\n\n::: {#40339fbc .cell execution_count=5}\n``` {.python .cell-code}\nfrom langchain.docstore.document import Document as LangchainDocument\n\nRAW_KNOWLEDGE_BASE = [\n LangchainDocument(page_content=doc[\"text\"], metadata={\"source\": doc[\"source\"]})\n for doc in tqdm(ds)\n]\n```\n:::\n\n\n# 1. Retriever - embeddings πŸ—‚οΈ\nThe __retriever acts like an internal search engine__: given the user query, it returns a few relevant snippets from your knowledge base.\n\nThese snippets will then be fed to the Reader Model to help it generate its answer.\n\nSo __our objective here is, given a user question, to find the most snippets from our knowledge base to answer that question.__\n\nThis is a wide objective, it leaves open some questions. How many snippets should we retrieve? This parameter will be named `top_k`.\n\nHow long should these snippets be? This is called the `chunk size`. There's no one-size-fits-all answers, but here are a few elements:\n- πŸ”€ Your `chunk size` is allowed to vary from one snippet to the other.\n- Since there will always be some noise in your retrieval, increasing the `top_k` increases the chance to get relevant elements in your retrieved snippets. 🎯 Shooting more arrows increases your probability to hit your target.\n- Meanwhile, the summed length of your retrieved documents should not be too high: for instance, for most current models 16k tokens will probably drown your Reader model in information due to [Lost-in-the-middle phenomenon](https://huggingface.co/papers/2307.03172). 🎯 Give your reader model only the most relevant insights, not a huge pile of books!\n\n::: callout-note\nIn this notebook, we use Langchain library since __it offers a huge variety of options for vector databases and allows us to keep document metadata throughout the processing__.\n:::\n\n### 1.1 Split the documents into chunks\n\n- In this part, __we split the documents from our knowledge base into smaller chunks__ which will be the snippets on which the reader LLM will base its answer.\n- The goal is to prepare a collection of **semantically relevant snippets**. So their size should be adapted to precise ideas: too small will truncate ideas, too large will dilute them.\n\n::: callout-tip\nπŸ’‘ Many options exist for text splitting: splitting on words, on sentence boundaries, recursive chunking that processes documents in a tree-like way to preserve structure information... To learn more about chunking, I recommend you read [this great notebook](https://github.com/FullStackRetrieval-com/RetrievalTutorials/blob/main/5_Levels_Of_Text_Splitting.ipynb) by Greg Kamradt.\n:::\n\n\n- **Recursive chunking** breaks down the text into smaller parts step by step using a given list of separators sorted from the most important to the least important separator. If the first split doesn't give the right size or shape chunks, the method repeats itself on the new chunks using a different separator. For instance with the list of separators `[\"\\n\\n\", \"\\n\", \".\", \"\"]`:\n - The method will first break down the document wherever there is a double line break `\"\\n\\n\"`.\n - Resulting documents will be split again on simple line breaks `\"\\n\"`, then on sentence ends `\".\"`.\n - And finally, if some chunks are still too big, they will be split whenever they overflow the maximum size.\n\n- With this method, the global structure is well preserved, at the expense of getting slight variations in chunk size.\n\n> [This space](https://huggingface.co/spaces/A-Roucher/chunk_visualizer) lets you visualize how different splitting options affect the chunks you get.\n\nπŸ”¬ Let's experiment a bit with chunk sizes, beginning with an arbitrary size, and see how splits work. We use Langchain's implementation of recursive chunking with `RecursiveCharacterTextSplitter`.\n- Parameter `chunk_size` controls the length of individual chunks: this length is counted by default as the number of characters in the chunk.\n- Parameter `chunk_overlap` lets adjacent chunks get a bit of overlap on each other. This reduces the probability that an idea could be cut in half by the split between two adjacent chunks. We ~arbitrarily set this to 1/10th of the chunk size, you could try different values!\n\n::: {#7a7ac95d .cell execution_count=6}\n``` {.python .cell-code}\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\n\n# We use a hierarchical list of separators specifically tailored for splitting Markdown documents\n# This list is taken from LangChain's MarkdownTextSplitter class.\nMARKDOWN_SEPARATORS = [\n \"\\n#{1,6} \",\n \"```\\n\",\n \"\\n\\\\*\\\\*\\\\*+\\n\",\n \"\\n---+\\n\",\n \"\\n___+\\n\",\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\",\n]\n\ntext_splitter = RecursiveCharacterTextSplitter(\n chunk_size=1000, # <1>\n chunk_overlap=100, # <2>\n add_start_index=True, # <3>\n strip_whitespace=True, # <4>\n separators=MARKDOWN_SEPARATORS,\n)\n\ndocs_processed = []\nfor doc in RAW_KNOWLEDGE_BASE:\n docs_processed += text_splitter.split_documents([doc])\n```\n:::\n\n\n1. The maximum number of characters in a chunk: we selected this value arbitrally\n2. The number of characters to overlap between chunks\n3. If `True`, includes chunk's start index in metadata\n4. If `True`, strips whitespace from the start and end of every document\n\n\nWe also have to keep in mind that when embedding documents, we will use an embedding model that has accepts a certain maximum sequence length `max_seq_length`.\n\nSo we should make sure that our chunk sizes are below this limit, because any longer chunk will be truncated before processing, thus losing relevancy.\n\n::: {#174a0a05 .cell execution_count=7}\n``` {.python .cell-code}\nfrom sentence_transformers import SentenceTransformer\n\n# To get the value of the max sequence_length, we will query the underlying `SentenceTransformer` object used in the RecursiveCharacterTextSplitter.\nprint(\n f\"Model's maximum sequence length: {SentenceTransformer('thenlper/gte-small').max_seq_length}\"\n)\n\nfrom transformers import AutoTokenizer\n\ntokenizer = AutoTokenizer.from_pretrained(\"thenlper/gte-small\")\nlengths = [len(tokenizer.encode(doc.page_content)) for doc in tqdm(docs_processed)]\n\n# Plot the distrubution of document lengths, counted as the number of tokens\nfig = pd.Series(lengths).hist()\nplt.title(\"Distribution of document lengths in the knowledge base (in count of tokens)\")\nplt.show()\n```\n:::\n\n\nπŸ‘€ As you can see, __the chunk lengths are not aligned with our limit of 512 tokens__, and some documents are above the limit, thus some part of them will be lost in truncation!\n - So we should change the `RecursiveCharacterTextSplitter` class to count length in number of tokens instead of number of characters.\n - Then we can choose a specific chunk size, here we would choose a lower threshold than 512:\n - smaller documents could allow the split to focus more on specific ideas.\n - But too small chunks would split sentences in half, thus losing meaning again: the proper tuning is a matter of balance.\n\n::: {#2fa8715e .cell execution_count=8}\n``` {.python .cell-code}\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom transformers import AutoTokenizer\n\nEMBEDDING_MODEL_NAME = \"thenlper/gte-small\"\n\n\ndef split_documents(\n chunk_size: int,\n knowledge_base: List[LangchainDocument],\n tokenizer_name: Optional[str] = EMBEDDING_MODEL_NAME,\n) -> List[LangchainDocument]:\n \"\"\"\n Split documents into chunks of maximum size `chunk_size` tokens and return a list of documents.\n \"\"\"\n text_splitter = RecursiveCharacterTextSplitter.from_huggingface_tokenizer(\n AutoTokenizer.from_pretrained(tokenizer_name),\n chunk_size=chunk_size,\n chunk_overlap=int(chunk_size / 10),\n add_start_index=True,\n strip_whitespace=True,\n separators=MARKDOWN_SEPARATORS,\n )\n\n docs_processed = []\n for doc in knowledge_base:\n docs_processed += text_splitter.split_documents([doc])\n\n # Remove duplicates\n unique_texts = {}\n docs_processed_unique = []\n for doc in docs_processed:\n if doc.page_content not in unique_texts:\n unique_texts[doc.page_content] = True\n docs_processed_unique.append(doc)\n\n return docs_processed_unique\n\n\ndocs_processed = split_documents(\n 512, # We choose a chunk size adapted to our model\n RAW_KNOWLEDGE_BASE,\n tokenizer_name=EMBEDDING_MODEL_NAME,\n)\n\n# Let's visualize the chunk sizes we would have in tokens from a common model\nfrom transformers import AutoTokenizer\n\ntokenizer = AutoTokenizer.from_pretrained(EMBEDDING_MODEL_NAME)\nlengths = [len(tokenizer.encode(doc.page_content)) for doc in tqdm(docs_processed)]\nfig = pd.Series(lengths).hist()\nplt.title(\"Distribution of document lengths in the knowledge base (in count of tokens)\")\nplt.show()\n```\n:::\n\n\n➑️ Now the chunk length distribution looks better!\n\n### 1.2 Building the vector database\n\nWe want to compute the embeddings for all the chunks of our knowledge base: to learn more on sentence embeddings, we recommend reading [this guide](https://osanseviero.github.io/hackerllama/blog/posts/sentence_embeddings/).\n\n#### How does retrieval work ?\n\nOnce the chunks are all embedded, we store them into a vector database. When the user types in a query, it gets embedded by the same model previously used, and a similarity search returns the closest documents from the vector database.\n\nThe technical challenge is thus, given a query vector, to quickly find the nearest neighbours of this vector in the vector database. To do this, we need to choose two things: a distance, and a search algorithm to find the nearest neighbors quickly within a database of thousands of records.\n\n##### Nearest Neighbor search algorithm\n\nThere are plentiful choices for the nearest neighbor search algorithm: we go with Facebook's [FAISS](https://github.com/facebookresearch/faiss), since FAISS is performant enough for most use cases, and it is well known thus widely implemented.\n\n##### Distances\n\nRegarding distances, you can find a good guide [here](https://osanseviero.github.io/hackerllama/blog/posts/sentence_embeddings/#distance-between-embeddings). In short:\n\n- **Cosine similarity** computes similarity between two vectors as the cosinus of their relative angle: it allows us to compare vector directions are regardless of their magnitude. Using it requires to normalize all vectors, to rescale them into unit norm.\n- **Dot product** takes into account magnitude, with the sometimes undesirable effect that increasing a vector's length will make it more similar to all others.\n- **Euclidean distance** is the distance between the ends of vectors.\n\nYou can try [this small exercise](https://developers.google.com/machine-learning/clustering/similarity/check-your-understanding) to check your understanding of these concepts. But once vectors are normalized, [the choice of a specific distance does not matter much](https://platform.openai.com/docs/guides/embeddings/which-distance-function-should-i-use).\n\nOur particular model works well with cosine similarity, so choose this distance, and we set it up both in the Embedding model, and in the `distance_strategy` argument of our FAISS index. With cosine similarity, we have to normalize our embeddings.\n\n::: {.callout-warning}\nπŸš¨πŸ‘‡ The cell below takes a few minutes to run on A10G!\n:::\n\n::: {#938830be .cell execution_count=9}\n``` {.python .cell-code}\nfrom langchain.vectorstores import FAISS\nfrom langchain_community.embeddings import HuggingFaceEmbeddings\nfrom langchain_community.vectorstores.utils import DistanceStrategy\n\nembedding_model = HuggingFaceEmbeddings(\n model_name=EMBEDDING_MODEL_NAME,\n multi_process=True,\n model_kwargs={\"device\": \"cuda\"},\n encode_kwargs={\"normalize_embeddings\": True}, # set True for cosine similarity\n)\n\nKNOWLEDGE_VECTOR_DATABASE = FAISS.from_documents(\n docs_processed, embedding_model, distance_strategy=DistanceStrategy.COSINE\n)\n```\n:::\n\n\nπŸ‘€ To visualize the search for the closest documents, let's project our embeddings from 384 dimensions down to 2 dimensions using PaCMAP.\n\n::: {.callout-note}\nπŸ’‘ We chose PaCMAP rather than other techniques such as t-SNE or UMAP, since [it is efficient (preserves local and global structure), robust to initialization parameters and fast](https://www.nature.com/articles/s42003-022-03628-x#Abs1).\n:::\n\n::: {#5ed5a830 .cell execution_count=10}\n``` {.python .cell-code}\n# embed a user query in the same space\nuser_query = \"How to create a pipeline object?\"\nquery_vector = embedding_model.embed_query(user_query)\n```\n:::\n\n\n::: {#206f15a0 .cell execution_count=11}\n``` {.python .cell-code}\nimport pacmap\nimport numpy as np\nimport plotly.express as px\n\nembedding_projector = pacmap.PaCMAP(\n n_components=2, n_neighbors=None, MN_ratio=0.5, FP_ratio=2.0, random_state=1\n)\n\nembeddings_2d = [\n list(KNOWLEDGE_VECTOR_DATABASE.index.reconstruct_n(idx, 1)[0])\n for idx in range(len(docs_processed))\n] + [query_vector]\n\n# fit the data (The index of transformed data corresponds to the index of the original data)\ndocuments_projected = embedding_projector.fit_transform(np.array(embeddings_2d), init=\"pca\")\n```\n:::\n\n\n::: {#249c7a6b .cell execution_count=12}\n``` {.python .cell-code}\ndf = pd.DataFrame.from_dict(\n [\n {\n \"x\": documents_projected[i, 0],\n \"y\": documents_projected[i, 1],\n \"source\": docs_processed[i].metadata[\"source\"].split(\"/\")[1],\n \"extract\": docs_processed[i].page_content[:100] + \"...\",\n \"symbol\": \"circle\",\n \"size_col\": 4,\n }\n for i in range(len(docs_processed))\n ]\n + [\n {\n \"x\": documents_projected[-1, 0],\n \"y\": documents_projected[-1, 1],\n \"source\": \"User query\",\n \"extract\": user_query,\n \"size_col\": 100,\n \"symbol\": \"star\",\n }\n ]\n)\n\n# visualize the embedding\nfig = px.scatter(\n df,\n x=\"x\",\n y=\"y\",\n color=\"source\",\n hover_data=\"extract\",\n size=\"size_col\",\n symbol=\"symbol\",\n color_discrete_map={\"User query\": \"black\"},\n width=1000,\n height=700,\n)\nfig.update_traces(\n marker=dict(opacity=1, line=dict(width=0, color=\"DarkSlateGrey\")), selector=dict(mode=\"markers\")\n)\nfig.update_layout(\n legend_title_text=\"Chunk source\",\n title=\"2D Projection of Chunk Embeddings via PaCMAP\",\n)\nfig.show()\n```\n:::\n\n\n\n\n\n➑️ On the graph above, you can see a spatial representation of the kowledge base documents. As the vector embeddings represent the document's meaning, their closeness in meaning should be reflected in their embedding's closeness.\n\nThe user query's embedding is also shown : we want to find the `k` document that have the closest meaning, thus we pick the `k` closest vectors.\n\nIn the LangChain vector database implementation, this search operation is performed by the method `vector_database.similarity_search(query)`.\n\nHere is the result:\n\n::: {#47319e72 .cell execution_count=13}\n``` {.python .cell-code}\nprint(f\"\\nStarting retrieval for {user_query=}...\")\nretrieved_docs = KNOWLEDGE_VECTOR_DATABASE.similarity_search(query=user_query, k=5)\nprint(\"\\n==================================Top document==================================\")\nprint(retrieved_docs[0].page_content)\nprint(\"==================================Metadata==================================\")\nprint(retrieved_docs[0].metadata)\n```\n:::\n\n\n# 2. Reader - LLM πŸ’¬\n\nIn this part, the __LLM Reader reads the retrieved context to formulate its answer.__\n\nThere are actually substeps that can all be tuned:\n1. The content of the retrieved documents is aggregated together into the \"context\", with many processing options like _prompt compression_.\n2. The context and the user query are aggregated into a prompt then given to the LLM to generate its answer.\n\n### 2.1. Reader model\n\nThe choice of a reader model is important on a few aspects:\n- the reader model's `max_seq_length` must accomodate our prompt, which includes the context output by the retriever call: the context consists in 5 documents of 512 tokens each, so we aim for a context length of 4k tokens at least.\n- the reader model\n\nFor this example, we chose [`HuggingFaceH4/zephyr-7b-beta`](https://huggingface.co/HuggingFaceH4/zephyr-7b-beta), a small but powerful model.\n\n::: callout-note\nWith many models being released every week, you may want to substitute this model to the latest and greatest. The best way to keep track of open source LLMs is to check the [Open-source LLM leaderboard](https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard).\n:::\n\nTo make inference faster, we will load the quantized version of the model:\n\n::: {#eaf2beef .cell execution_count=14}\n``` {.python .cell-code}\nfrom transformers import pipeline\nimport torch\nfrom transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig\n\nREADER_MODEL_NAME = \"HuggingFaceH4/zephyr-7b-beta\"\n\nbnb_config = BitsAndBytesConfig(\n load_in_4bit=True,\n bnb_4bit_use_double_quant=True,\n bnb_4bit_quant_type=\"nf4\",\n bnb_4bit_compute_dtype=torch.bfloat16,\n)\nmodel = AutoModelForCausalLM.from_pretrained(READER_MODEL_NAME, quantization_config=bnb_config)\ntokenizer = AutoTokenizer.from_pretrained(READER_MODEL_NAME)\n\nREADER_LLM = pipeline(\n model=model,\n tokenizer=tokenizer,\n task=\"text-generation\",\n do_sample=True,\n temperature=0.2,\n repetition_penalty=1.1,\n return_full_text=False,\n max_new_tokens=500,\n)\n```\n:::\n\n\n::: {#f6f72fd3 .cell execution_count=15}\n``` {.python .cell-code}\nREADER_LLM(\"What is 4+4? Answer:\")\n```\n:::\n\n\n### 2.2. Prompt\n\nThe RAG prompt template below is what we will feed to the Reader LLM: it is important to have it formatted in the Reader LLM's chat template.\n\nWe give it our context and the user's question.\n\n::: {#e28bd1d6 .cell execution_count=16}\n``` {.python .cell-code}\nprompt_in_chat_format = [\n {\n \"role\": \"system\",\n \"content\": \"\"\"Using the information contained in the context,\ngive a comprehensive answer to the question.\nRespond only to the question asked, response should be concise and relevant to the question.\nProvide the number of the source document when relevant.\nIf the answer cannot be deduced from the context, do not give an answer.\"\"\",\n },\n {\n \"role\": \"user\",\n \"content\": \"\"\"Context:\n{context}\n---\nNow here is the question you need to answer.\n\nQuestion: {question}\"\"\",\n },\n]\nRAG_PROMPT_TEMPLATE = tokenizer.apply_chat_template(\n prompt_in_chat_format, tokenize=False, add_generation_prompt=True\n)\nprint(RAG_PROMPT_TEMPLATE)\n```\n:::\n\n\nLet's test our Reader on our previously retrieved documents!\n\n::: {#96c062d3 .cell execution_count=17}\n``` {.python .cell-code}\nretrieved_docs_text = [\n doc.page_content for doc in retrieved_docs\n] # we only need the text of the documents\ncontext = \"\\nExtracted documents:\\n\"\ncontext += \"\".join([f\"Document {str(i)}:::\\n\" + doc for i, doc in enumerate(retrieved_docs_text)])\n\nfinal_prompt = RAG_PROMPT_TEMPLATE.format(\n question=\"How to create a pipeline object?\", context=context\n)\n\n# Redact an answer\nanswer = READER_LLM(final_prompt)[0][\"generated_text\"]\nprint(answer)\n```\n:::\n\n\n### 2.3. Reranking\n\nA good option for RAG is to retrieve more documents than you want in the end, then rerank the results with a more powerful retrieval model before keeping only the `top_k`.\n\nFor this, [Colbertv2](https://arxiv.org/abs/2112.01488) is a great choice: instead of a bi-encoder like our classical embedding models, it is a cross-encoder that computes more fine-grained interactions between the query tokens and each document's tokens.\n\nIt is easily usable thanks to [the RAGatouille library](https://github.com/bclavie/RAGatouille).\n\n::: {#c0298337 .cell execution_count=18}\n``` {.python .cell-code}\nfrom ragatouille import RAGPretrainedModel\n\nRERANKER = RAGPretrainedModel.from_pretrained(\"colbert-ir/colbertv2.0\")\n```\n:::\n\n\n# 3. Assembling it all!\n\n::: {#f7d2b29a .cell execution_count=19}\n``` {.python .cell-code}\nfrom transformers import Pipeline\n\n\ndef answer_with_rag(\n question: str,\n llm: Pipeline,\n knowledge_index: FAISS,\n reranker: Optional[RAGPretrainedModel] = None,\n num_retrieved_docs: int = 30,\n num_docs_final: int = 5,\n) -> Tuple[str, List[LangchainDocument]]:\n # Gather documents with retriever\n print(\"=> Retrieving documents...\")\n relevant_docs = knowledge_index.similarity_search(query=question, k=num_retrieved_docs)\n relevant_docs = [doc.page_content for doc in relevant_docs] # keep only the text\n\n # Optionally rerank results\n if reranker:\n print(\"=> Reranking documents...\")\n relevant_docs = reranker.rerank(question, relevant_docs, k=num_docs_final)\n relevant_docs = [doc[\"content\"] for doc in relevant_docs]\n\n relevant_docs = relevant_docs[:num_docs_final]\n\n # Build the final prompt\n context = \"\\nExtracted documents:\\n\"\n context += \"\".join([f\"Document {str(i)}:::\\n\" + doc for i, doc in enumerate(relevant_docs)])\n\n final_prompt = RAG_PROMPT_TEMPLATE.format(question=question, context=context)\n\n # Redact an answer\n print(\"=> Generating answer...\")\n answer = llm(final_prompt)[0][\"generated_text\"]\n\n return answer, relevant_docs\n```\n:::\n\n\nLet's see how our RAG pipeline answers a user query.\n\n::: {#06ccd294 .cell execution_count=20}\n``` {.python .cell-code}\nquestion = \"how to create a pipeline object?\"\n\nanswer, relevant_docs = answer_with_rag(\n question, READER_LLM, KNOWLEDGE_VECTOR_DATABASE, reranker=RERANKER\n)\n```\n:::\n\n\n::: {#f6ece0d2 .cell execution_count=21}\n``` {.python .cell-code}\nprint(\"==================================Answer==================================\")\nprint(f\"{answer}\")\nprint(\"==================================Source docs==================================\")\nfor i, doc in enumerate(relevant_docs):\n print(f\"Document {i}------------------------------------------------------------\")\n print(doc)\n```\n:::\n\n\nβœ… We now have a fully functional, performant RAG sytem. That's it for today! Congratulations for making it to the end πŸ₯³\n\n\n# To go further πŸ—ΊοΈ\n\nThis is not the end of the journey! You can try many steps to improve your RAG system. We recommend doing so in an iterative way: bring small changes to the system and see what improves performance.\n\n### Setting up an evaluation pipeline\n\n- πŸ’¬ \"You cannot improve the model performance that you do not measure\", said Gandhi... or at least Llama2 told me he said it. Anyway, you should absolutely start by measuring performance: this means building a small evaluation dataset, then monitor the performance of your RAG system on this evaluation dataset.\n\n### Improving the retriever\n\nπŸ› οΈ __You can use these options to tune the results:__\n\n- Tune the chunking method:\n - Size of the chunks\n - Method: split on different separators, use [semantic chunking](https://python.langchain.com/docs/modules/data_connection/document_transformers/semantic-chunker)...\n- Change the embedding model\n\nπŸ‘·β€β™€οΈ __More could be considered:__\n- Try another chunking method, like semantic chunking\n- Change the index used (here, FAISS)\n- Query expansion: reformulate the user query in slightly different ways to retrieve more documents.\n\n### Improving the reader\n\nπŸ› οΈ __Here you can try the following options to improve results:__\n- Tune the prompt\n- Switch reranking on/off\n- Choose a more powerful reader model\n\nπŸ’‘ __Many options could be considered here to further improve the results:__\n- Compress the retrieved context to keep only the most relevant parts to answer the query.\n- Extend the RAG system to make it more user-friendly:\n - cite source\n - make conversational\n\n", + "supporting": [ + "advanced_rag_files" + ], + "filters": [], + "includes": {} + } +} \ No newline at end of file diff --git a/src/.quarto/_freeze/notebooks/rag_evaluation/execute-results/html.json b/src/.quarto/_freeze/notebooks/rag_evaluation/execute-results/html.json new file mode 100644 index 0000000000000000000000000000000000000000..a2a38823d36e6527e41095c664bd5ef8811e67a4 --- /dev/null +++ b/src/.quarto/_freeze/notebooks/rag_evaluation/execute-results/html.json @@ -0,0 +1,12 @@ +{ + "hash": "f2080bfc2a1669f9b0cb07f290b98431", + "result": { + "engine": "jupyter", + "markdown": "---\ntitle: RAG Evaluation\njupyter: python3\neval: false\n---\n\n::: {#22784221 .cell execution_count=1}\n``` {.python .cell-code}\n!pip install -q torch transformers transformers langchain sentence-transformers faiss-gpu openpyxl openai\n```\n:::\n\n\n::: {#01b9dea1 .cell execution_count=2}\n``` {.python .cell-code}\n%reload_ext autoreload\n%autoreload 2\n%reload_ext dotenv\n%dotenv\n```\n:::\n\n\n::: {#b2fdd362 .cell execution_count=3}\n``` {.python .cell-code}\nfrom tqdm.notebook import tqdm\nimport pandas as pd\nfrom typing import Optional, List, Tuple\nfrom langchain_core.language_models import BaseChatModel\nimport json\nimport datasets\n\npd.set_option(\"display.max_colwidth\", None)\n```\n:::\n\n\n### Load your knowledge base\n\n::: {#359836ac .cell execution_count=4}\n``` {.python .cell-code}\nds = datasets.load_dataset(\"m-ric/huggingface_doc\", split=\"train\")\n```\n:::\n\n\n# 1. Build a synthetic dataset for evaluation\nWe first build a synthetic dataset of questions and associated contexts. The method is to get elements from our knowledge base, and ask an LLM to generate questions based on these documents.\n\nThen we setup other LLM agents to act as quality filters for the generated QA couples: each of them will act as the filter for a specific flaw.\n\n### 1.1. Prepare source documents\n\n::: {#36f649ad .cell execution_count=5}\n``` {.python .cell-code}\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain.docstore.document import Document as LangchainDocument\n\nlangchain_docs = [\n LangchainDocument(page_content=doc[\"text\"], metadata={\"source\": doc[\"source\"]})\n for doc in tqdm(ds)\n]\n\n\ntext_splitter = RecursiveCharacterTextSplitter(\n chunk_size=2000,\n chunk_overlap=200,\n add_start_index=True,\n separators=[\"\\n\\n\", \"\\n\", \".\", \" \", \"\"],\n)\n\ndocs_processed = []\nfor doc in langchain_docs:\n docs_processed += text_splitter.split_documents([doc])\n```\n:::\n\n\n### 1.2. Setup agents for question generation\n\nWe use [Mixtral](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1) for QA couple generation because it it has excellent performance in leaderboards such as [Chatbot Arena](https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard).\n\n::: {#03878328 .cell execution_count=6}\n``` {.python .cell-code}\nfrom langchain_community.llms import HuggingFaceHub\n\nrepo_id = \"mistralai/Mixtral-8x7B-Instruct-v0.1\"\n\nllm = HuggingFaceHub(\n repo_id=repo_id,\n task=\"text-generation\",\n model_kwargs={\n \"max_new_tokens\": 512,\n \"top_k\": 30,\n \"temperature\": 0.1,\n \"repetition_penalty\": 1.03,\n },\n)\n```\n:::\n\n\n::: {#67115f45 .cell execution_count=7}\n``` {.python .cell-code}\nfrom langchain_community.chat_models import ChatHuggingFace\n\nchat_model = ChatHuggingFace(llm=llm)\n```\n:::\n\n\n::: {#8a75ff72 .cell execution_count=8}\n``` {.python .cell-code}\nfrom langchain.prompts import ChatPromptTemplate\n\nQA_generation_prompt = \"\"\"\nYour task is to write a factoid question and an answer given a context.\nYour factoid question should be answerable with a specific, concise piece of factual information from the context.\nYour factoid question should be formulated in the same style as questions users could ask in a search engine.\nThis means that your factoid question MUST NOT mention something like \"according to the passage\" or \"context\".\n\nProvide your answer as follows:\n\nOutput:::\nFactoid question: (your factoid question)\nAnswer: (your answer to the factoid question)\n\nNow here is the context.\n\nContext: {context}\\n\nOutput:::\"\"\"\n\nQA_generation_prompt = ChatPromptTemplate.from_template(QA_generation_prompt)\nQA_generation_agent = QA_generation_prompt | chat_model\n```\n:::\n\n\nNow let's generate our QA couples.\nFor this example, we generate only 10 QA couples and will load the rest from the Hub.\n\nBut for your specific knowledge base, given that you want to get at least ~100 test samples, and accounting for the fact that we will filter out around half of these with our critique agents later on, you should generate much more, in the >200 samples.\n\n::: {#66448027 .cell execution_count=9}\n``` {.python .cell-code}\nimport random\n\nN_GENERATIONS = (\n 10 # We intentionally generate only 10 QA couples here for cost and time considerations\n)\n\nprint(f\"Generating {N_GENERATIONS} QA couples...\")\noutputs = []\nfor context in tqdm(random.sample(langchain_docs, N_GENERATIONS)):\n # Generate QA couple\n output_QA_couple = QA_generation_agent.invoke({\"context\": context.page_content}).content\n try:\n question = output_QA_couple.split(\"Factoid question: \")[1].split(\"Answer: \")[0]\n answer = output_QA_couple.split(\"Answer: \")[1]\n outputs.append(\n {\n \"context\": context.page_content,\n \"question\": question,\n \"answer\": answer,\n \"source_doc\": context.metadata[\"source\"],\n }\n )\n except:\n continue\n```\n:::\n\n\n::: {#38586e3e .cell execution_count=10}\n``` {.python .cell-code}\ndisplay(pd.DataFrame(outputs).head(1))\n```\n:::\n\n\n### 1.3. Setup critique agents\n\nThe questions generated by the previous agent can have many flaws: we should do a quality check before validating these questions.\n\nWe thus build critique agents that will rate each question on several criteria, given in [this paper](https://huggingface.co/papers/2312.10003):\n- **Groundedness:** can the question be answered from the given context?\n- **Relevance:** is the question relevant to users? For instance, `\"What is the date when transformers 4.29.1 was released?\"` is not relevant for ML practicioners.\n\nOne last failure case we've noticed is when a function is tailored for the particular setting where the question was generated, but undecipherable by itself, like `\"What is the name of the function used in this guide?\"`.\nWe also build a critique agent for this criteria:\n- **Stand-alone**: is the question understandable free of any context, for someone with domain knowledge/Internet access? The opposite of this would be `What is the function used in this article?` for a question generated from a specific blog article.\n\nWe systematically score functions with all these agents, and whenever the score is too low for any one of the agents, we eliminate the question from our eval dataset.\n\nπŸ’‘ ___When asking the agents to output a score, we first ask them to produce its rationale. This will help us verify scores, but most importantly, asking it to first output rationale gives the model more tokens to think and elaborate an answer before summarizing it into a single score token.___\n\nWe now build and run these critique agents.\n\n::: {#36f64eeb .cell execution_count=11}\n``` {.python .cell-code}\nquestion_groundedness_critique_prompt = \"\"\"\nYou will be given a context and a question.\nYour task is to provide a 'total rating' scoring how well one can answer the given question unambiguously with the given context.\nGive your answer on a scale of 1 to 5, where 1 means that the question is not answerable at all given the context, and 5 means that the question is clearly and unambiguously answerable with the context.\n\nProvide your answer as follows:\n\nAnswer:::\nEvaluation: (your rationale for the rating)\nTotal rating: (your rating)\n\nNow here are the question and context.\n\nQuestion: {question}\\n\nContext: {context}\\n\nAnswer::: \"\"\"\n\nquestion_relevance_critique_prompt = \"\"\"\nYou will be given a question.\nYour task is to provide a 'total rating' representing how useful this question can be to machine learning developers building NLP applications with the Hugging Face ecosystem.\nGive your answer on a scale of 1 to 5, where 1 means that the question is not useful at all, and 5 means that the question is extremely useful.\n\nProvide your answer as follows:\n\nAnswer:::\nEvaluation: (your rationale for the rating)\nTotal rating: (your rating)\n\nNow here is the question.\n\nQuestion: {question}\\n\nAnswer::: \"\"\"\n\nquestion_standalone_critique_prompt = \"\"\"\nYou will be given a question.\nYour task is to provide a 'total rating' representing how context-independant this question is.\nGive your answer on a scale of 1 to 5, where 1 means that the question only makes sense in a specific context, and 5 means that the question makes sense by itself.\nFor instance, if the question refers to a particular setting, like 'in the context' or 'in the document', the rating must be 1.\nThe questions can contain obscure technical nouns or acronyms like Gradio, Hub, Hugging Face or Space and still be a 5: it must simply be clear to an operator with access to documentation what the question is about.\n\nProvide your answer as follows:\n\nAnswer:::\nEvaluation: (your rationale for the rating)\nTotal rating: (your rating)\n\nNow here is the question.\n\nQuestion: {question}\\n\nAnswer::: \"\"\"\n\nquestion_groundedness_critique_prompt = ChatPromptTemplate.from_template(\n question_groundedness_critique_prompt\n)\nquestion_groundedness_critique_agent = question_groundedness_critique_prompt | chat_model\n\nquestion_relevance_critique_prompt = ChatPromptTemplate.from_template(\n question_relevance_critique_prompt\n)\nquestion_relevance_critique_agent = question_relevance_critique_prompt | chat_model\n\nquestion_standalone_critique_prompt = ChatPromptTemplate.from_template(\n question_standalone_critique_prompt\n)\nquestion_standalone_critique_agent = question_standalone_critique_prompt | chat_model\n```\n:::\n\n\n::: {#36a9f0a0 .cell execution_count=12}\n``` {.python .cell-code}\nprint(\"Generating critique for each QA couple...\")\nfor output in tqdm(outputs):\n # Critique the generated QA couple\n question_groundedness_evaluation = question_groundedness_critique_agent.invoke(\n {\"context\": output[\"context\"], \"question\": output[\"question\"]}\n ).content\n question_relevance_evaluation = question_relevance_critique_agent.invoke(\n {\"question\": output[\"question\"]}\n ).content\n question_standalone_evaluation = question_standalone_critique_agent.invoke(\n {\"question\": output[\"question\"]}\n ).content\n\n try:\n groundedness_score = int(question_groundedness_evaluation.split(\"Total rating: \")[1][0])\n groundedness_eval = question_groundedness_evaluation.split(\"Total rating: \")[0].split(\n \"Evaluation: \"\n )[1]\n relevance_score = int(question_relevance_evaluation.split(\"Total rating: \")[1][0])\n relevance_eval = question_relevance_evaluation.split(\"Total rating: \")[0].split(\n \"Evaluation: \"\n )[1]\n standalone_score = int(question_standalone_evaluation.split(\"Total rating: \")[1][0])\n standalone_eval = question_standalone_evaluation.split(\"Total rating: \")[0].split(\n \"Evaluation: \"\n )[1]\n output.update(\n {\n \"groundedness_score\": groundedness_score,\n \"groundedness_eval\": groundedness_eval,\n \"relevance_score\": relevance_score,\n \"relevance_eval\": relevance_eval,\n \"standalone_score\": standalone_score,\n \"standalone_eval\": standalone_eval,\n }\n )\n except:\n continue\n```\n:::\n\n\nNow let us filter out bad questions based on our critique agent scores:\n\n::: {#244dd1b5 .cell execution_count=13}\n``` {.python .cell-code}\nimport pandas as pd\n\npd.set_option(\"display.max_colwidth\", None)\n\ngenerated_questions = pd.DataFrame.from_dict(outputs)\n\nprint(\"Evaluation dataset before filtering:\")\ndisplay(\n generated_questions[\n [\"question\", \"answer\", \"groundedness_score\", \"relevance_score\", \"standalone_score\"]\n ]\n)\ngenerated_questions = generated_questions.loc[\n (generated_questions[\"groundedness_score\"] >= 4)\n & (generated_questions[\"relevance_score\"] >= 4)\n & (generated_questions[\"standalone_score\"] >= 4)\n]\nprint(\"============================================\")\nprint(\"Final evaluation dataset:\")\ndisplay(\n generated_questions[\n [\"question\", \"answer\", \"groundedness_score\", \"relevance_score\", \"standalone_score\"]\n ]\n)\n\neval_dataset = datasets.Dataset.from_pandas(\n generated_questions, split=\"train\", preserve_index=False\n)\n```\n:::\n\n\nNow our synthetic evaluation dataset is complete! We can evaluate different RAG systems on this evaluation dataset.\n\nWe have generated only a few QA couples here to reduce time and cost. But let's kick start the next part by loading a pre-generated dataset:\n\n::: {#9e9d2a9f .cell execution_count=14}\n``` {.python .cell-code}\neval_dataset = datasets.load_dataset(\"m-ric/huggingface_doc_qa_eval\", split=\"train\")\n```\n:::\n\n\n# 2. Build our RAG System\n\n### 2.1. Preprocessing documents to build our vector database\n\n- In this part, __we split the documents from our knowledge base into smaller chunks__: these will be the snippets that are picked by the Retriever, to then be ingested by the Reader LLM as supporting elements for its answer.\n- The goal is to build semantically relevant snippets: not too small to be sufficient for supporting an answer, and not too large too avoid diluting individual ideas.\n\nMany options exist for text splitting:\n- split every `n` words / characters, but this has the risk of cutting in half paragraphs or even sentences\n- split after `n` words / character, but only on sentence boundaries\n- **recursive split** tries to preserve even more of the document structure, by processing it tree-like way, splitting first on the largest units (chapters) then recursively splitting on smaller units (paragraphs, sentences).\n\nTo learn more about chunking, I recommend you read [this great notebook](https://github.com/FullStackRetrieval-com/RetrievalTutorials/blob/main/5_Levels_Of_Text_Splitting.ipynb) by Greg Kamradt.\n\n[This space](https://huggingface.co/spaces/m-ric/chunk_visualizer) lets you visualize how different splitting options affect the chunks you get.\n\n> In the following, we use Langchain's `RecursiveCharacterTextSplitter`.\n\nπŸ’‘ _To measure chunk length in our Text Splitter, our length function will not be the count of characters, but the count of tokens in the tokenized text: indeed, for subsequent embedder that processes token, measuring length in tokens is more relevant and empirically performs better._\n\n::: {#94af026d .cell execution_count=15}\n``` {.python .cell-code}\nfrom langchain.docstore.document import Document as LangchainDocument\n\nRAW_KNOWLEDGE_BASE = [\n LangchainDocument(page_content=doc[\"text\"], metadata={\"source\": doc[\"source\"]})\n for doc in tqdm(ds)\n]\n```\n:::\n\n\n::: {#13cebd63 .cell execution_count=16}\n``` {.python .cell-code}\nfrom langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom transformers import AutoTokenizer\n\n\ndef split_documents(\n chunk_size: int,\n knowledge_base: List[LangchainDocument],\n tokenizer_name: str,\n) -> List[LangchainDocument]:\n \"\"\"\n Split documents into chunks of size `chunk_size` characters and return a list of documents.\n \"\"\"\n text_splitter = RecursiveCharacterTextSplitter.from_huggingface_tokenizer(\n AutoTokenizer.from_pretrained(tokenizer_name),\n chunk_size=chunk_size,\n chunk_overlap=int(chunk_size / 10),\n add_start_index=True,\n strip_whitespace=True,\n separators=[\"\\n\\n\", \"\\n\", \".\", \" \", \"\"],\n )\n\n docs_processed = []\n for doc in knowledge_base:\n docs_processed += text_splitter.split_documents([doc])\n\n # Remove duplicates\n unique_texts = {}\n docs_processed_unique = []\n for doc in docs_processed:\n if doc.page_content not in unique_texts:\n unique_texts[doc.page_content] = True\n docs_processed_unique.append(doc)\n\n return docs_processed_unique\n```\n:::\n\n\n### 2.2. Retriever - embeddings πŸ—‚οΈ\nThe __retriever acts like an internal search engine__: given the user query, it returns the most relevant documents from your knowledge base.\n\n> For the knowledge base, we use Langchain vector databases since __it offers a convenient [FAISS](https://github.com/facebookresearch/faiss) index and allows us to keep document metadata throughout the processing__.\n\nπŸ› οΈ __Options included:__\n\n- Tune the chunking method:\n - Size of the chunks\n - Method: split on different separators, use [semantic chunking](https://python.langchain.com/docs/modules/data_connection/document_transformers/semantic-chunker)...\n- Change the embedding model\n\n::: {#8f7371a4 .cell execution_count=17}\n``` {.python .cell-code}\nfrom langchain.vectorstores import FAISS\nfrom langchain_community.embeddings import HuggingFaceEmbeddings\nfrom langchain_community.vectorstores.utils import DistanceStrategy\nimport os\n\n\ndef load_embeddings(\n langchain_docs: List[LangchainDocument],\n chunk_size: int,\n embedding_model_name: Optional[str] = \"thenlper/gte-small\",\n) -> FAISS:\n \"\"\"\n Creates a FAISS index from the given embedding model and documents. Loads the index directly if it already exists.\n\n Args:\n langchain_docs: list of documents\n chunk_size: size of the chunks to split the documents into\n embedding_model_name: name of the embedding model to use\n\n Returns:\n FAISS index\n \"\"\"\n # load embedding_model\n embedding_model = HuggingFaceEmbeddings(\n model_name=embedding_model_name,\n multi_process=True,\n model_kwargs={\"device\": \"cuda\"},\n encode_kwargs={\"normalize_embeddings\": True}, # set True to compute cosine similarity\n )\n\n # Check if embeddings already exist on disk\n index_name = f\"index_chunk:{chunk_size}_embeddings:{embedding_model_name.replace('/', '~')}\"\n index_folder_path = f\"./data/indexes/{index_name}/\"\n if os.path.isdir(index_folder_path):\n return FAISS.load_local(\n index_folder_path,\n embedding_model,\n distance_strategy=DistanceStrategy.COSINE,\n )\n\n else:\n print(\"Index not found, generating it...\")\n docs_processed = split_documents(\n chunk_size,\n langchain_docs,\n embedding_model_name,\n )\n knowledge_index = FAISS.from_documents(\n docs_processed, embedding_model, distance_strategy=DistanceStrategy.COSINE\n )\n knowledge_index.save_local(index_folder_path)\n return knowledge_index\n```\n:::\n\n\n### 2.3. Reader - LLM πŸ’¬\n\nIn this part, the __LLM Reader reads the retrieved documents to formulate its answer.__\n\nπŸ› οΈ Here we tried the following options to improve results:\n- Switch reranking on/off\n- Change the reader model\n\n::: {#843d7987 .cell execution_count=18}\n``` {.python .cell-code}\nRAG_PROMPT_TEMPLATE = \"\"\"\n<|system|>\nUsing the information contained in the context,\ngive a comprehensive answer to the question.\nRespond only to the question asked, response should be concise and relevant to the question.\nProvide the number of the source document when relevant.\nIf the answer cannot be deduced from the context, do not give an answer.\n<|user|>\nContext:\n{context}\n---\nNow here is the question you need to answer.\n\nQuestion: {question}\n\n<|assistant|>\n\"\"\"\n```\n:::\n\n\n::: {#6884550b .cell execution_count=19}\n``` {.python .cell-code}\nfrom langchain_community.llms import HuggingFaceHub\n\nrepo_id = \"HuggingFaceH4/zephyr-7b-beta\"\nREADER_MODEL_NAME = \"zephyr-7b-beta\"\n\nREADER_LLM = HuggingFaceHub(\n repo_id=repo_id,\n task=\"text-generation\",\n model_kwargs={\n \"max_new_tokens\": 512,\n \"top_k\": 30,\n \"temperature\": 0.1,\n \"repetition_penalty\": 1.03,\n },\n)\n```\n:::\n\n\n::: {#143d4d0b .cell execution_count=20}\n``` {.python .cell-code}\nfrom ragatouille import RAGPretrainedModel\nfrom langchain_core.vectorstores import VectorStore\nfrom langchain_core.language_models.llms import LLM\n\n\ndef answer_with_rag(\n question: str,\n llm: LLM,\n knowledge_index: VectorStore,\n reranker: Optional[RAGPretrainedModel] = None,\n num_retrieved_docs: int = 30,\n num_docs_final: int = 7,\n) -> Tuple[str, List[LangchainDocument]]:\n \"\"\"Answer a question using RAG with the given knowledge index.\"\"\"\n # Gather documents with retriever\n relevant_docs = knowledge_index.similarity_search(query=question, k=num_retrieved_docs)\n relevant_docs = [doc.page_content for doc in relevant_docs] # keep only the text\n\n # Optionally rerank results\n if reranker:\n relevant_docs = reranker.rerank(question, relevant_docs, k=num_docs_final)\n relevant_docs = [doc[\"content\"] for doc in relevant_docs]\n\n relevant_docs = relevant_docs[:num_docs_final]\n\n # Build the final prompt\n context = \"\\nExtracted documents:\\n\"\n context += \"\".join([f\"Document {str(i)}:::\\n\" + doc for i, doc in enumerate(relevant_docs)])\n\n final_prompt = RAG_PROMPT_TEMPLATE.format(question=question, context=context)\n\n # Redact an answer\n answer = llm(final_prompt)\n\n return answer, relevant_docs\n```\n:::\n\n\n# 3. Benchmarking the RAG system\n\nThe RAG system and the evaluation datasets are now ready. The last step is to judge the RAG system's output on this evlauation dataset.\n\nTo this end, __we setup a judge agent__. βš–οΈπŸ€–\n\nOut of [the different RAG evaluation metrics](https://docs.ragas.io/en/latest/concepts/metrics/index.html), we choose to focus only on faithfulness since it the best end-to-end metric of our system's performance.\n\n> We use GPT4 as a judge for its empirically good performance, but you could try with other models such as [kaist-ai/prometheus-13b-v1.0](https://huggingface.co/kaist-ai/prometheus-13b-v1.0) or [BAAI/JudgeLM-33B-v1.0](https://huggingface.co/BAAI/JudgeLM-33B-v1.0).\n\nπŸ’‘ _In the evaluation prompt, we give a detailed description each metric on the scale 1-5, as is done in [Prometheus's prompt template](https://huggingface.co/kaist-ai/prometheus-13b-v1.0): this helps the model ground its metric precisely. If instead you give the judge LLM a vague scale to work with, the outputs will not be consistent enough between different examples._\n\nπŸ’‘ _Again, prompting the LLM to output rationale before giving its final score gives it more tokens to help it formalize and elaborate a judgement._\n\n::: {#ce53b5ec .cell execution_count=21}\n``` {.python .cell-code}\ndef run_rag_tests(\n eval_dataset: datasets.Dataset,\n llm: BaseChatModel,\n knowledge_index: VectorStore,\n output_file: str,\n reranker: Optional[RAGPretrainedModel] = None,\n verbose: Optional[bool] = True,\n test_settings: Optional[str] = None, # To document the test settings used\n):\n \"\"\"Runs RAG tests on the given dataset and saves the results to the given output file.\"\"\"\n try: # load previous generations if they exist\n with open(output_file, \"r\") as f:\n outputs = json.load(f)\n except:\n outputs = []\n\n for example in tqdm(eval_dataset):\n question = example[\"question\"]\n if question in [output[\"question\"] for output in outputs]:\n continue\n\n answer, relevant_docs = answer_with_rag(question, llm, knowledge_index, reranker=reranker)\n if verbose:\n print(\"=======================================================\")\n print(f\"Question: {question}\")\n print(f\"Answer: {answer}\")\n print(f'True answer: {example[\"answer\"]}')\n result = {\n \"question\": question,\n \"true_answer\": example[\"answer\"],\n \"source_doc\": example[\"source_doc\"],\n \"generated_answer\": answer,\n \"retrieved_docs\": [doc for doc in relevant_docs],\n }\n if test_settings:\n result[\"test_settings\"] = test_settings\n outputs.append(result)\n\n with open(output_file, \"w\") as f:\n json.dump(outputs, f)\n```\n:::\n\n\n::: {#49f7ef44 .cell execution_count=22}\n``` {.python .cell-code}\nEVALUATION_PROMPT = \"\"\"###Task Description:\nAn instruction (might include an Input inside it), a response to evaluate, a reference answer that gets a score of 5, and a score rubric representing a evaluation criteria are given.\n1. Write a detailed feedback that assess the quality of the response strictly based on the given score rubric, not evaluating in general.\n2. After writing a feedback, write a score that is an integer between 1 and 5. You should refer to the score rubric.\n3. The output format should look as follows: \\\"Feedback: {{write a feedback for criteria}} [RESULT] {{an integer number between 1 and 5}}\\\"\n4. Please do not generate any other opening, closing, and explanations. Be sure to include [RESULT] in your output.\n\n###The instruction to evaluate:\n{instruction}\n\n###Response to evaluate:\n{response}\n\n###Reference Answer (Score 5):\n{reference_answer}\n\n###Score Rubrics:\n[Is the response correct, accurate, and factual based on the reference answer?]\nScore 1: The response is completely incorrect, inaccurate, and/or not factual.\nScore 2: The response is mostly incorrect, inaccurate, and/or not factual.\nScore 3: The response is somewhat correct, accurate, and/or factual.\nScore 4: The response is mostly correct, accurate, and factual.\nScore 5: The response is completely correct, accurate, and factual.\n\n###Feedback:\"\"\"\n\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n)\nfrom langchain.schema import SystemMessage\n\n\nevaluation_prompt_template = ChatPromptTemplate.from_messages(\n [\n SystemMessage(content=\"You are a fair evaluator language model.\"),\n HumanMessagePromptTemplate.from_template(EVALUATION_PROMPT),\n ]\n)\n```\n:::\n\n\n::: {#b678d8fe .cell execution_count=23}\n``` {.python .cell-code}\nfrom langchain.chat_models import ChatOpenAI\n\neval_chat_model = ChatOpenAI(model=\"gpt-4-1106-preview\", temperature=0)\nevaluator_name = \"GPT4\"\n\n\ndef evaluate_answers(\n answer_path: str,\n eval_chat_model: BaseChatModel,\n evaluator_name: str,\n evaluation_prompt_template: ChatPromptTemplate,\n) -> None:\n \"\"\"Evaluates generated answers. Modifies the given answer file in place for better checkpointing.\"\"\"\n answers = []\n if os.path.isfile(answer_path): # load previous generations if they exist\n answers = json.load(open(answer_path, \"r\"))\n\n for experiment in tqdm(answers):\n if f\"eval_score_{evaluator_name}\" in experiment:\n continue\n\n eval_prompt = evaluation_prompt_template.format_messages(\n instruction=experiment[\"question\"],\n response=experiment[\"generated_answer\"],\n reference_answer=experiment[\"true_answer\"],\n )\n eval_result = eval_chat_model.invoke(eval_prompt)\n feedback, score = [item.strip() for item in eval_result.content.split(\"[RESULT]\")]\n experiment[f\"eval_score_{evaluator_name}\"] = score\n experiment[f\"eval_feedback_{evaluator_name}\"] = feedback\n\n with open(answer_path, \"w\") as f:\n json.dump(answers, f)\n```\n:::\n\n\nπŸš€ Let's run the tests and evaluate answers!πŸ‘‡\n\n::: {#55f9f502 .cell execution_count=24}\n``` {.python .cell-code}\nif not os.path.exists(\"./output\"):\n os.mkdir(\"./output\")\n\nfor chunk_size in [200]: # Add other chunk sizes (in tokens) as needed\n for embeddings in [\"thenlper/gte-small\"]: # Add other embeddings as needed\n for rerank in [True, False]:\n settings_name = f\"chunk:{chunk_size}_embeddings:{embeddings.replace('/', '~')}_rerank:{rerank}_reader-model:{READER_MODEL_NAME}\"\n output_file_name = f\"./output/rag_{settings_name}.json\"\n\n print(f\"Running evaluation for {settings_name}:\")\n\n print(\"Loading knowledge base embeddings...\")\n knowledge_index = load_embeddings(\n RAW_KNOWLEDGE_BASE,\n chunk_size=chunk_size,\n embedding_model_name=embeddings,\n )\n\n print(\"Running RAG...\")\n reranker = (\n RAGPretrainedModel.from_pretrained(\"colbert-ir/colbertv2.0\") if rerank else None\n )\n run_rag_tests(\n eval_dataset=eval_dataset,\n llm=READER_LLM,\n knowledge_index=knowledge_index,\n output_file=output_file_name,\n reranker=reranker,\n verbose=False,\n test_settings=settings_name,\n )\n\n print(\"Running evaluation...\")\n evaluate_answers(\n output_file_name,\n eval_chat_model,\n evaluator_name,\n evaluation_prompt_template,\n )\n```\n:::\n\n\n### Inspect results\n\n::: {#9fbbe7e3 .cell execution_count=25}\n``` {.python .cell-code}\nimport glob\n\noutputs = []\nfor file in glob.glob(\"./output/*.json\"):\n output = pd.DataFrame(json.load(open(file, \"r\")))\n output[\"settings\"] = file\n outputs.append(output)\nresult = pd.concat(outputs)\n```\n:::\n\n\n::: {#8e32ac07 .cell execution_count=26}\n``` {.python .cell-code}\nresult[\"eval_score_GPT4\"] = result[\"eval_score_GPT4\"].apply(\n lambda x: int(x) if isinstance(x, str) else 1\n)\nresult[\"eval_score_GPT4\"] = (result[\"eval_score_GPT4\"] - 1) / 4\n```\n:::\n\n\n::: {#14c33cb4 .cell execution_count=27}\n``` {.python .cell-code}\naverage_scores = result.groupby(\"settings\")[\"eval_score_GPT4\"].mean()\naverage_scores.sort_values()\n```\n:::\n\n\n## Example results\n\nLet us load the results that I obtained by tweaking the different options available in this notebook.\nFor more detail on why these options could work on not, see the notebook on [advanced_RAG](advanced_rag).\n\nAs you can see in the graph below, some tweaks do not bring any improvement, some give huge performance boosts.\n\n➑️ ___There is no single good recipe: you should try several different directions when tuning your RAG systems.___\n\n::: {#0f27b105 .cell execution_count=28}\n``` {.python .cell-code}\nimport plotly.express as px\n\nscores = datasets.load_dataset(\"m-ric/rag_scores_cookbook\", split=\"train\")\nscores = pd.Series(scores[\"score\"], index=scores[\"settings\"])\n```\n:::\n\n\n::: {#abdb166c .cell execution_count=29}\n``` {.python .cell-code}\nfig = px.bar(\n scores,\n color=scores,\n labels={\n \"value\": \"Accuracy\",\n \"settings\": \"Configuration\",\n },\n color_continuous_scale=\"bluered\",\n)\nfig.update_layout(w\n width=1000,\n height=600,\n barmode=\"group\",\n yaxis_range=[0, 100],\n title=\"Accuracy of different RAG configurations\",\n xaxis_title=\"RAG settings\",\n font=dict(size=15),\n)\nfig.layout.yaxis.ticksuffix = \"%\"\nfig.update_coloraxes(showscale=False)\nfig.update_traces(texttemplate=\"%{y:.1f}\", textposition=\"outside\")\nfig.show()\n```\n:::\n\n\n\n\nAs you can see, these had varying impact on performance. In particular, tuning the chunk size is both easy and very impactful.\n\nBut this is our case: your results could be very different: now that you have a robust evaluation pipeline, you can set on to explore other options! πŸ—ΊοΈ\n\n", + "supporting": [ + "rag_evaluation_files" + ], + "filters": [], + "includes": {} + } +} \ No newline at end of file diff --git a/src/.quarto/_freeze/notebooks/rag_zephyr_langchain/execute-results/html.json b/src/.quarto/_freeze/notebooks/rag_zephyr_langchain/execute-results/html.json new file mode 100644 index 0000000000000000000000000000000000000000..6b03a0c1912a117f4e41ced87026a927439bdbd8 --- /dev/null +++ b/src/.quarto/_freeze/notebooks/rag_zephyr_langchain/execute-results/html.json @@ -0,0 +1,12 @@ +{ + "hash": "664c66ecb115cb866ac245da08fed5e9", + "result": { + "engine": "jupyter", + "markdown": "---\ntitle: Simple RAG\njupyter: python3\neval: false\ncode-annotations: hover\n\n---\n\n::: {#78116675 .cell execution_count=1}\n``` {.python .cell-code}\n!pip install -q torch transformers accelerate bitsandbytes transformers sentence-transformers faiss-gpu\n```\n:::\n\n\n::: {#be6b2c06 .cell execution_count=2}\n``` {.python .cell-code}\n!pip install -q langchain\n```\n:::\n\n\n::: callout-note\nIf running in Google Colab, you may need to run this cell to make sure you're using UTF-8 locale to install LangChain\n\n::: {#4dc3a73a .cell execution_count=3}\n``` {.python .cell-code}\nimport locale\nlocale.getpreferredencoding = lambda: \"UTF-8\"\n```\n:::\n\n\n:::\n\n\n## Prepare the data\n\nIn this example, we'll load all of the issues (both open and closed) from [PEFT library's repo](https://github.com/huggingface/peft).\n\nFirst, you need to acquire a [GitHub personal access token](https://github.com/settings/tokens?type=beta) to access the GitHub API.\n\n::: {#99d8d506 .cell execution_count=4}\n``` {.python .cell-code}\nfrom getpass import getpass\n\nACCESS_TOKEN = getpass(\"YOUR_GITHUB_PERSONAL_TOKEN\") # <1>\n```\n:::\n\n\n1. You can also use an environment variable to store your token.\n\nNext, we'll load all of the issues in the [huggingface/peft](https://github.com/huggingface/peft) repo:\n- By default, pull requests are considered issues as well, here we chose to exclude them from data with by setting `include_prs=False`\n- Setting `state = \"all\"` means we will load both open and closed issues.\n\n::: {#4aba18cd .cell execution_count=5}\n``` {.python .cell-code}\nfrom langchain.document_loaders import GitHubIssuesLoader\n\nloader = GitHubIssuesLoader(\n repo=\"huggingface/peft\",\n access_token=ACCESS_TOKEN,\n include_prs=False,\n state=\"all\"\n)\n\ndocs = loader.load()\n```\n:::\n\n\nThe content of individual GitHub issues may be longer than what an embedding model can take as input. If we want to embed all of the available content, we need to chunk the documents into appropriately sized pieces.\n\nThe most common and straightforward approach to chunking is to define a fixed size of chunks and whether there should be any overlap between them. Keeping some overlap between chunks allows us to preserve some semantic context between the chunks.\n\nOther approaches are typically more involved and take into account the documents' structure and context. For example, one may want to split a document based on sentences or paragraphs, or create chunks based on the\n\nThe fixed-size chunking, however, works well for most common cases, so that is what we'll do here.\n\n::: {#1ee02e26 .cell execution_count=6}\n``` {.python .cell-code}\nfrom langchain.text_splitter import CharacterTextSplitter\n\nsplitter = CharacterTextSplitter(chunk_size=512, chunk_overlap=30)\n\nchunked_docs = splitter.split_documents(docs)\n```\n:::\n\n\n## Create the embeddings + retriever\n\nNow that the docs are all of the appropriate size, we can create a database with their embeddings.\n\nTo create document chunk embeddings we'll use the `HuggingFaceEmbeddings` and the [`BAAI/bge-base-en-v1.5`](https://huggingface.co/BAAI/bge-base-en-v1.5) embeddings model. To create the vector database, we'll use `FAISS`, a library developed by Facebook AI. This library offers efficient similarity search and clustering of dense vectors, which is what we need here. FAISS is currently one of the most used libraries for NN search in massive datasets. \n\n::: callout-tip\nThere are many other embeddings models available on the Hub, and you can keep an eye on the best performing ones by checking the [Massive Text Embedding Benchmark (MTEB) Leaderboard](https://huggingface.co/spaces/mteb/leaderboard).\n:::\n\nWe'll access both the embeddings model and FAISS via LangChain API.\n\n::: {#3342a691 .cell execution_count=7}\n``` {.python .cell-code}\nfrom langchain.vectorstores import FAISS\nfrom langchain.embeddings import HuggingFaceEmbeddings\n\ndb = FAISS.from_documents(chunked_docs,\n HuggingFaceEmbeddings(model_name='BAAI/bge-base-en-v1.5'))\n```\n:::\n\n\nWe need a way to return(retrieve) the documents given an unstructured query. For that, we'll use the `as_retriever` method using the `db` as a backbone:\n- `search_type=\"similarity\"` means we want to perform similarity search between the query and documents\n- `search_kwargs={'k': 4}` instructs the retriever to return top 4 results.\n\n::: {#28bd25f2 .cell execution_count=8}\n``` {.python .cell-code}\nretriever = db.as_retriever(\n search_type=\"similarity\", # <1>\n search_kwargs={'k': 4} # <1>\n)\n```\n:::\n\n\n1. The ideal search type is context dependent, and you should experiment to find the best one for your data.\n\nThe vector database and retriever are now set up, next we need to set up the next piece of the chain - the model.\n\n## Load quantized model\n\nFor this example, we chose [`HuggingFaceH4/zephyr-7b-beta`](https://huggingface.co/HuggingFaceH4/zephyr-7b-beta), a small but powerful model.\nTo make inference faster, we will load the quantized version of the model:\n\n:::::: {.callout-tip}\nWith many models being released every week, you may want to substitute this model to the latest and greatest. The best way to keep track of open source LLMs is to check the [Open-source LLM leaderboard](https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard).\n:::\n\n::: {#e5288d87 .cell execution_count=9}\n``` {.python .cell-code}\nimport torch\nfrom transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig\n\nmodel_name = 'HuggingFaceH4/zephyr-7b-beta'\n\nbnb_config = BitsAndBytesConfig(\n load_in_4bit=True,\n bnb_4bit_use_double_quant=True,\n bnb_4bit_quant_type=\"nf4\",\n bnb_4bit_compute_dtype=torch.bfloat16\n)\n\nmodel = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=bnb_config)\ntokenizer = AutoTokenizer.from_pretrained(model_name)\n```\n:::\n\n\n## Setup the LLM chain\n\nFinally, we have all the pieces we need to set up the LLM chain.\n\nFirst, create a text_generation pipeline using the loaded model and its tokenizer.\n\nNext, create a prompt template - this should follow the format of the model, so if you substitute the model checkpoint, make sure to use the appropriate formatting.\n\n::: {#389798fe .cell execution_count=10}\n``` {.python .cell-code}\nfrom langchain.llms import HuggingFacePipeline\nfrom langchain.prompts import PromptTemplate\nfrom transformers import pipeline\nfrom langchain_core.output_parsers import StrOutputParser\n\ntext_generation_pipeline = pipeline(\n model=model, # <1> \n tokenizer=tokenizer, # <2> \n task=\"text-generation\", # <3> \n temperature=0.2, # <4> \n do_sample=True, # <5> \n repetition_penalty=1.1, # <6> \n return_full_text=True, # <7> \n max_new_tokens=400, # <8> \n)\n\nllm = HuggingFacePipeline(pipeline=text_generation_pipeline)\n\nprompt_template = \"\"\"\n<|system|>\nAnswer the question based on your knowledge. Use the following context to help:\n\n{context}\n\n\n<|user|>\n{question}\n\n<|assistant|>\n\n \"\"\"\n\nprompt = PromptTemplate(\n input_variables=[\"context\", \"question\"],\n template=prompt_template,\n)\n\nllm_chain = prompt | llm | StrOutputParser()\n```\n:::\n\n\n1. The pre-trained model for text generation.\n2. Tokenizer to preprocess input text and postprocess generated output.\n3. Specifies the task as text generation.\n4. Controls the randomness in the output generation. Lower values make the output more deterministic.\n5. Enables sampling to introduce randomness in the output generation.\n6. Penalizes repetition in the output to encourage diversity.\n7. Returns the full generated text including the input prompt.\n8. Limits the maximum number of new tokens generated.\n\nNote: _You can also use `tokenizer.apply_chat_template` to convert a list of messages (as dicts: `{'role': 'user', 'content': '(...)'}`) into a string with the appropriate chat format._\n\n\nFinally, we need to combine the `llm_chain` with the retriever to create a RAG chain. We pass the original question through to the final generation step, as well as the retrieved context docs:\n\n::: {#2ad1978e .cell execution_count=11}\n``` {.python .cell-code}\nfrom langchain_core.runnables import RunnablePassthrough\n\nretriever = db.as_retriever()\n\nrag_chain = (\n {\"context\": retriever, \"question\": RunnablePassthrough()}\n | llm_chain\n)\n```\n:::\n\n\n## Compare the results\n\nLet's see the difference RAG makes in generating answers to the library-specific questions.\n\n::: {#aa570a95 .cell execution_count=12}\n``` {.python .cell-code}\nquestion = \"How do you combine multiple adapters?\"\n```\n:::\n\n\nFirst, let's see what kind of answer we can get with just the model itself, no context added:\n\n::: {#3c1688aa .cell execution_count=13}\n``` {.python .cell-code}\nllm_chain.invoke({\"context\":\"\", \"question\": question})\n```\n:::\n\n\nAs you can see, the model interpreted the question as one about physical computer adapters, while in the context of PEFT, \"adapters\" refer to LoRA adapters.\nLet's see if adding context from GitHub issues helps the model give a more relevant answer:\n\n::: {#57388c24 .cell execution_count=14}\n``` {.python .cell-code}\nrag_chain.invoke(question)\n```\n:::\n\n\nAs we can see, the added context, really helps the exact same model, provide a much more relevant and informed answer to the library-specific question.\n\nNotably, combining multiple adapters for inference has been added to the library, and one can find this information in the documentation, so for the next iteration of this RAG it may be worth including documentation embeddings.\n\n", + "supporting": [ + "rag_zephyr_langchain_files" + ], + "filters": [], + "includes": {} + } +} \ No newline at end of file diff --git a/src/.quarto/_freeze/site_libs/clipboard/clipboard.min.js b/src/.quarto/_freeze/site_libs/clipboard/clipboard.min.js new file mode 100644 index 0000000000000000000000000000000000000000..1103f811ed80f17985ecf61e0d50e3359484244f --- /dev/null +++ b/src/.quarto/_freeze/site_libs/clipboard/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT Β© Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1*{grid-area:1/1;margin:auto}.reveal .r-hstack,.reveal .r-vstack{display:flex}.reveal .r-hstack img,.reveal .r-hstack video,.reveal .r-vstack img,.reveal .r-vstack video{min-width:0;min-height:0;object-fit:contain}.reveal .r-vstack{flex-direction:column;align-items:center;justify-content:center}.reveal .r-hstack{flex-direction:row;align-items:center;justify-content:center}.reveal .items-stretch{align-items:stretch}.reveal .items-start{align-items:flex-start}.reveal .items-center{align-items:center}.reveal .items-end{align-items:flex-end}.reveal .justify-between{justify-content:space-between}.reveal .justify-around{justify-content:space-around}.reveal .justify-start{justify-content:flex-start}.reveal .justify-center{justify-content:center}.reveal .justify-end{justify-content:flex-end}html.reveal-full-page{width:100%;height:100%;height:100vh;height:calc(var(--vh,1vh) * 100);height:100svh;overflow:hidden}.reveal-viewport{height:100%;overflow:hidden;position:relative;line-height:1;margin:0;background-color:#fff;color:#000;--r-controls-spacing:12px}.reveal-viewport:fullscreen{top:0!important;left:0!important;width:100%!important;height:100%!important;transform:none!important}.reveal .fragment{transition:all .2s ease}.reveal .fragment:not(.custom){opacity:0;visibility:hidden;will-change:opacity}.reveal .fragment.visible{opacity:1;visibility:inherit}.reveal .fragment.disabled{transition:none}.reveal .fragment.grow{opacity:1;visibility:inherit}.reveal .fragment.grow.visible{transform:scale(1.3)}.reveal .fragment.shrink{opacity:1;visibility:inherit}.reveal .fragment.shrink.visible{transform:scale(.7)}.reveal .fragment.zoom-in{transform:scale(.1)}.reveal .fragment.zoom-in.visible{transform:none}.reveal .fragment.fade-out{opacity:1;visibility:inherit}.reveal .fragment.fade-out.visible{opacity:0;visibility:hidden}.reveal .fragment.semi-fade-out{opacity:1;visibility:inherit}.reveal .fragment.semi-fade-out.visible{opacity:.5;visibility:inherit}.reveal .fragment.strike{opacity:1;visibility:inherit}.reveal .fragment.strike.visible{text-decoration:line-through}.reveal .fragment.fade-up{transform:translate(0,40px)}.reveal .fragment.fade-up.visible{transform:translate(0,0)}.reveal .fragment.fade-down{transform:translate(0,-40px)}.reveal .fragment.fade-down.visible{transform:translate(0,0)}.reveal .fragment.fade-right{transform:translate(-40px,0)}.reveal .fragment.fade-right.visible{transform:translate(0,0)}.reveal .fragment.fade-left{transform:translate(40px,0)}.reveal .fragment.fade-left.visible{transform:translate(0,0)}.reveal .fragment.current-visible,.reveal .fragment.fade-in-then-out{opacity:0;visibility:hidden}.reveal .fragment.current-visible.current-fragment,.reveal .fragment.fade-in-then-out.current-fragment{opacity:1;visibility:inherit}.reveal .fragment.fade-in-then-semi-out{opacity:0;visibility:hidden}.reveal .fragment.fade-in-then-semi-out.visible{opacity:.5;visibility:inherit}.reveal .fragment.fade-in-then-semi-out.current-fragment{opacity:1;visibility:inherit}.reveal .fragment.highlight-blue,.reveal .fragment.highlight-current-blue,.reveal .fragment.highlight-current-green,.reveal .fragment.highlight-current-red,.reveal .fragment.highlight-green,.reveal .fragment.highlight-red{opacity:1;visibility:inherit}.reveal .fragment.highlight-red.visible{color:#ff2c2d}.reveal .fragment.highlight-green.visible{color:#17ff2e}.reveal .fragment.highlight-blue.visible{color:#1b91ff}.reveal .fragment.highlight-current-red.current-fragment{color:#ff2c2d}.reveal .fragment.highlight-current-green.current-fragment{color:#17ff2e}.reveal .fragment.highlight-current-blue.current-fragment{color:#1b91ff}.reveal:after{content:"";font-style:italic}.reveal iframe{z-index:1}.reveal a{position:relative}@keyframes bounce-right{0%,10%,25%,40%,50%{transform:translateX(0)}20%{transform:translateX(10px)}30%{transform:translateX(-5px)}}@keyframes bounce-left{0%,10%,25%,40%,50%{transform:translateX(0)}20%{transform:translateX(-10px)}30%{transform:translateX(5px)}}@keyframes bounce-down{0%,10%,25%,40%,50%{transform:translateY(0)}20%{transform:translateY(10px)}30%{transform:translateY(-5px)}}.reveal .controls{display:none;position:absolute;top:auto;bottom:var(--r-controls-spacing);right:var(--r-controls-spacing);left:auto;z-index:11;color:#000;pointer-events:none;font-size:10px}.reveal .controls button{position:absolute;padding:0;background-color:transparent;border:0;outline:0;cursor:pointer;color:currentColor;transform:scale(.9999);transition:color .2s ease,opacity .2s ease,transform .2s ease;z-index:2;pointer-events:auto;font-size:inherit;visibility:hidden;opacity:0;-webkit-appearance:none;-webkit-tap-highlight-color:transparent}.reveal .controls .controls-arrow:after,.reveal .controls .controls-arrow:before{content:"";position:absolute;top:0;left:0;width:2.6em;height:.5em;border-radius:.25em;background-color:currentColor;transition:all .15s ease,background-color .8s ease;transform-origin:.2em 50%;will-change:transform}.reveal .controls .controls-arrow{position:relative;width:3.6em;height:3.6em}.reveal .controls .controls-arrow:before{transform:translateX(.5em) translateY(1.55em) rotate(45deg)}.reveal .controls .controls-arrow:after{transform:translateX(.5em) translateY(1.55em) rotate(-45deg)}.reveal .controls .controls-arrow:hover:before{transform:translateX(.5em) translateY(1.55em) rotate(40deg)}.reveal .controls .controls-arrow:hover:after{transform:translateX(.5em) translateY(1.55em) rotate(-40deg)}.reveal .controls .controls-arrow:active:before{transform:translateX(.5em) translateY(1.55em) rotate(36deg)}.reveal .controls .controls-arrow:active:after{transform:translateX(.5em) translateY(1.55em) rotate(-36deg)}.reveal .controls .navigate-left{right:6.4em;bottom:3.2em;transform:translateX(-10px)}.reveal .controls .navigate-left.highlight{animation:bounce-left 2s 50 both ease-out}.reveal .controls .navigate-right{right:0;bottom:3.2em;transform:translateX(10px)}.reveal .controls .navigate-right .controls-arrow{transform:rotate(180deg)}.reveal .controls .navigate-right.highlight{animation:bounce-right 2s 50 both ease-out}.reveal .controls .navigate-up{right:3.2em;bottom:6.4em;transform:translateY(-10px)}.reveal .controls .navigate-up .controls-arrow{transform:rotate(90deg)}.reveal .controls .navigate-down{right:3.2em;bottom:-1.4em;padding-bottom:1.4em;transform:translateY(10px)}.reveal .controls .navigate-down .controls-arrow{transform:rotate(-90deg)}.reveal .controls .navigate-down.highlight{animation:bounce-down 2s 50 both ease-out}.reveal .controls[data-controls-back-arrows=faded] .navigate-up.enabled{opacity:.3}.reveal .controls[data-controls-back-arrows=faded] .navigate-up.enabled:hover{opacity:1}.reveal .controls[data-controls-back-arrows=hidden] .navigate-up.enabled{opacity:0;visibility:hidden}.reveal .controls .enabled{visibility:visible;opacity:.9;cursor:pointer;transform:none}.reveal .controls .enabled.fragmented{opacity:.5}.reveal .controls .enabled.fragmented:hover,.reveal .controls .enabled:hover{opacity:1}.reveal:not(.rtl) .controls[data-controls-back-arrows=faded] .navigate-left.enabled{opacity:.3}.reveal:not(.rtl) .controls[data-controls-back-arrows=faded] .navigate-left.enabled:hover{opacity:1}.reveal:not(.rtl) .controls[data-controls-back-arrows=hidden] .navigate-left.enabled{opacity:0;visibility:hidden}.reveal.rtl .controls[data-controls-back-arrows=faded] .navigate-right.enabled{opacity:.3}.reveal.rtl .controls[data-controls-back-arrows=faded] .navigate-right.enabled:hover{opacity:1}.reveal.rtl .controls[data-controls-back-arrows=hidden] .navigate-right.enabled{opacity:0;visibility:hidden}.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-down,.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-up{display:none}.reveal:not(.has-vertical-slides) .controls .navigate-left,.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-left{bottom:1.4em;right:5.5em}.reveal:not(.has-vertical-slides) .controls .navigate-right,.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-right{bottom:1.4em;right:.5em}.reveal:not(.has-horizontal-slides) .controls .navigate-up{right:1.4em;bottom:5em}.reveal:not(.has-horizontal-slides) .controls .navigate-down{right:1.4em;bottom:.5em}.reveal.has-dark-background .controls{color:#fff}.reveal.has-light-background .controls{color:#000}.reveal.no-hover .controls .controls-arrow:active:before,.reveal.no-hover .controls .controls-arrow:hover:before{transform:translateX(.5em) translateY(1.55em) rotate(45deg)}.reveal.no-hover .controls .controls-arrow:active:after,.reveal.no-hover .controls .controls-arrow:hover:after{transform:translateX(.5em) translateY(1.55em) rotate(-45deg)}@media screen and (min-width:500px){.reveal-viewport{--r-controls-spacing:0.8em}.reveal .controls[data-controls-layout=edges]{top:0;right:0;bottom:0;left:0}.reveal .controls[data-controls-layout=edges] .navigate-down,.reveal .controls[data-controls-layout=edges] .navigate-left,.reveal .controls[data-controls-layout=edges] .navigate-right,.reveal .controls[data-controls-layout=edges] .navigate-up{bottom:auto;right:auto}.reveal .controls[data-controls-layout=edges] .navigate-left{top:50%;left:var(--r-controls-spacing);margin-top:-1.8em}.reveal .controls[data-controls-layout=edges] .navigate-right{top:50%;right:var(--r-controls-spacing);margin-top:-1.8em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:var(--r-controls-spacing);left:50%;margin-left:-1.8em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:calc(var(--r-controls-spacing) - 1.4em + .3em);left:50%;margin-left:-1.8em}}.reveal .progress{position:absolute;display:none;height:3px;width:100%;bottom:0;left:0;z-index:10;background-color:rgba(0,0,0,.2);color:#fff}.reveal .progress:after{content:"";display:block;position:absolute;height:10px;width:100%;top:-10px}.reveal .progress span{display:block;height:100%;width:100%;background-color:currentColor;transition:transform .8s cubic-bezier(.26,.86,.44,.985);transform-origin:0 0;transform:scaleX(0)}.reveal .slide-number{position:absolute;display:block;right:8px;bottom:8px;z-index:31;font-family:Helvetica,sans-serif;font-size:12px;line-height:1;color:#fff;background-color:rgba(0,0,0,.4);padding:5px}.reveal .slide-number a{color:currentColor}.reveal .slide-number-delimiter{margin:0 3px}.reveal{position:relative;width:100%;height:100%;overflow:hidden;touch-action:pinch-zoom}.reveal.embedded{touch-action:pan-y}.reveal.embedded.is-vertical-slide{touch-action:none}.reveal .slides{position:absolute;width:100%;height:100%;top:0;right:0;bottom:0;left:0;margin:auto;pointer-events:none;overflow:visible;z-index:1;text-align:center;perspective:600px;perspective-origin:50% 40%}.reveal .slides>section{perspective:600px}.reveal .slides>section,.reveal .slides>section>section{display:none;position:absolute;width:100%;pointer-events:auto;z-index:10;transform-style:flat;transition:transform-origin .8s cubic-bezier(.26,.86,.44,.985),transform .8s cubic-bezier(.26,.86,.44,.985),visibility .8s cubic-bezier(.26,.86,.44,.985),opacity .8s cubic-bezier(.26,.86,.44,.985)}.reveal[data-transition-speed=fast] .slides section{transition-duration:.4s}.reveal[data-transition-speed=slow] .slides section{transition-duration:1.2s}.reveal .slides section[data-transition-speed=fast]{transition-duration:.4s}.reveal .slides section[data-transition-speed=slow]{transition-duration:1.2s}.reveal .slides>section.stack{padding-top:0;padding-bottom:0;pointer-events:none;height:100%}.reveal .slides>section.present,.reveal .slides>section>section.present{display:block;z-index:11;opacity:1}.reveal .slides>section:empty,.reveal .slides>section>section:empty,.reveal .slides>section>section[data-background-interactive],.reveal .slides>section[data-background-interactive]{pointer-events:none}.reveal.center,.reveal.center .slides,.reveal.center .slides section{min-height:0!important}.reveal .slides>section:not(.present),.reveal .slides>section>section:not(.present){pointer-events:none}.reveal.overview .slides>section,.reveal.overview .slides>section>section{pointer-events:auto}.reveal .slides>section.future,.reveal .slides>section.future>section,.reveal .slides>section.past,.reveal .slides>section.past>section,.reveal .slides>section>section.future,.reveal .slides>section>section.past{opacity:0}.reveal .slides>section[data-transition=slide].past,.reveal .slides>section[data-transition~=slide-out].past,.reveal.slide .slides>section:not([data-transition]).past{transform:translate(-150%,0)}.reveal .slides>section[data-transition=slide].future,.reveal .slides>section[data-transition~=slide-in].future,.reveal.slide .slides>section:not([data-transition]).future{transform:translate(150%,0)}.reveal .slides>section>section[data-transition=slide].past,.reveal .slides>section>section[data-transition~=slide-out].past,.reveal.slide .slides>section>section:not([data-transition]).past{transform:translate(0,-150%)}.reveal .slides>section>section[data-transition=slide].future,.reveal .slides>section>section[data-transition~=slide-in].future,.reveal.slide .slides>section>section:not([data-transition]).future{transform:translate(0,150%)}.reveal .slides>section[data-transition=linear].past,.reveal .slides>section[data-transition~=linear-out].past,.reveal.linear .slides>section:not([data-transition]).past{transform:translate(-150%,0)}.reveal .slides>section[data-transition=linear].future,.reveal .slides>section[data-transition~=linear-in].future,.reveal.linear .slides>section:not([data-transition]).future{transform:translate(150%,0)}.reveal .slides>section>section[data-transition=linear].past,.reveal .slides>section>section[data-transition~=linear-out].past,.reveal.linear .slides>section>section:not([data-transition]).past{transform:translate(0,-150%)}.reveal .slides>section>section[data-transition=linear].future,.reveal .slides>section>section[data-transition~=linear-in].future,.reveal.linear .slides>section>section:not([data-transition]).future{transform:translate(0,150%)}.reveal .slides section[data-transition=default].stack,.reveal.default .slides section.stack{transform-style:preserve-3d}.reveal .slides>section[data-transition=default].past,.reveal .slides>section[data-transition~=default-out].past,.reveal.default .slides>section:not([data-transition]).past{transform:translate3d(-100%,0,0) rotateY(-90deg) translate3d(-100%,0,0)}.reveal .slides>section[data-transition=default].future,.reveal .slides>section[data-transition~=default-in].future,.reveal.default .slides>section:not([data-transition]).future{transform:translate3d(100%,0,0) rotateY(90deg) translate3d(100%,0,0)}.reveal .slides>section>section[data-transition=default].past,.reveal .slides>section>section[data-transition~=default-out].past,.reveal.default .slides>section>section:not([data-transition]).past{transform:translate3d(0,-300px,0) rotateX(70deg) translate3d(0,-300px,0)}.reveal .slides>section>section[data-transition=default].future,.reveal .slides>section>section[data-transition~=default-in].future,.reveal.default .slides>section>section:not([data-transition]).future{transform:translate3d(0,300px,0) rotateX(-70deg) translate3d(0,300px,0)}.reveal .slides section[data-transition=convex].stack,.reveal.convex .slides section.stack{transform-style:preserve-3d}.reveal .slides>section[data-transition=convex].past,.reveal .slides>section[data-transition~=convex-out].past,.reveal.convex .slides>section:not([data-transition]).past{transform:translate3d(-100%,0,0) rotateY(-90deg) translate3d(-100%,0,0)}.reveal .slides>section[data-transition=convex].future,.reveal .slides>section[data-transition~=convex-in].future,.reveal.convex .slides>section:not([data-transition]).future{transform:translate3d(100%,0,0) rotateY(90deg) translate3d(100%,0,0)}.reveal .slides>section>section[data-transition=convex].past,.reveal .slides>section>section[data-transition~=convex-out].past,.reveal.convex .slides>section>section:not([data-transition]).past{transform:translate3d(0,-300px,0) rotateX(70deg) translate3d(0,-300px,0)}.reveal .slides>section>section[data-transition=convex].future,.reveal .slides>section>section[data-transition~=convex-in].future,.reveal.convex .slides>section>section:not([data-transition]).future{transform:translate3d(0,300px,0) rotateX(-70deg) translate3d(0,300px,0)}.reveal .slides section[data-transition=concave].stack,.reveal.concave .slides section.stack{transform-style:preserve-3d}.reveal .slides>section[data-transition=concave].past,.reveal .slides>section[data-transition~=concave-out].past,.reveal.concave .slides>section:not([data-transition]).past{transform:translate3d(-100%,0,0) rotateY(90deg) translate3d(-100%,0,0)}.reveal .slides>section[data-transition=concave].future,.reveal .slides>section[data-transition~=concave-in].future,.reveal.concave .slides>section:not([data-transition]).future{transform:translate3d(100%,0,0) rotateY(-90deg) translate3d(100%,0,0)}.reveal .slides>section>section[data-transition=concave].past,.reveal .slides>section>section[data-transition~=concave-out].past,.reveal.concave .slides>section>section:not([data-transition]).past{transform:translate3d(0,-80%,0) rotateX(-70deg) translate3d(0,-80%,0)}.reveal .slides>section>section[data-transition=concave].future,.reveal .slides>section>section[data-transition~=concave-in].future,.reveal.concave .slides>section>section:not([data-transition]).future{transform:translate3d(0,80%,0) rotateX(70deg) translate3d(0,80%,0)}.reveal .slides section[data-transition=zoom],.reveal.zoom .slides section:not([data-transition]){transition-timing-function:ease}.reveal .slides>section[data-transition=zoom].past,.reveal .slides>section[data-transition~=zoom-out].past,.reveal.zoom .slides>section:not([data-transition]).past{visibility:hidden;transform:scale(16)}.reveal .slides>section[data-transition=zoom].future,.reveal .slides>section[data-transition~=zoom-in].future,.reveal.zoom .slides>section:not([data-transition]).future{visibility:hidden;transform:scale(.2)}.reveal .slides>section>section[data-transition=zoom].past,.reveal .slides>section>section[data-transition~=zoom-out].past,.reveal.zoom .slides>section>section:not([data-transition]).past{transform:scale(16)}.reveal .slides>section>section[data-transition=zoom].future,.reveal .slides>section>section[data-transition~=zoom-in].future,.reveal.zoom .slides>section>section:not([data-transition]).future{transform:scale(.2)}.reveal.cube .slides{perspective:1300px}.reveal.cube .slides section{padding:30px;min-height:700px;backface-visibility:hidden;box-sizing:border-box;transform-style:preserve-3d}.reveal.center.cube .slides section{min-height:0}.reveal.cube .slides section:not(.stack):before{content:"";position:absolute;display:block;width:100%;height:100%;left:0;top:0;background:rgba(0,0,0,.1);border-radius:4px;transform:translateZ(-20px)}.reveal.cube .slides section:not(.stack):after{content:"";position:absolute;display:block;width:90%;height:30px;left:5%;bottom:0;background:0 0;z-index:1;border-radius:4px;box-shadow:0 95px 25px rgba(0,0,0,.2);transform:translateZ(-90px) rotateX(65deg)}.reveal.cube .slides>section.stack{padding:0;background:0 0}.reveal.cube .slides>section.past{transform-origin:100% 0;transform:translate3d(-100%,0,0) rotateY(-90deg)}.reveal.cube .slides>section.future{transform-origin:0 0;transform:translate3d(100%,0,0) rotateY(90deg)}.reveal.cube .slides>section>section.past{transform-origin:0 100%;transform:translate3d(0,-100%,0) rotateX(90deg)}.reveal.cube .slides>section>section.future{transform-origin:0 0;transform:translate3d(0,100%,0) rotateX(-90deg)}.reveal.page .slides{perspective-origin:0 50%;perspective:3000px}.reveal.page .slides section{padding:30px;min-height:700px;box-sizing:border-box;transform-style:preserve-3d}.reveal.page .slides section.past{z-index:12}.reveal.page .slides section:not(.stack):before{content:"";position:absolute;display:block;width:100%;height:100%;left:0;top:0;background:rgba(0,0,0,.1);transform:translateZ(-20px)}.reveal.page .slides section:not(.stack):after{content:"";position:absolute;display:block;width:90%;height:30px;left:5%;bottom:0;background:0 0;z-index:1;border-radius:4px;box-shadow:0 95px 25px rgba(0,0,0,.2);-webkit-transform:translateZ(-90px) rotateX(65deg)}.reveal.page .slides>section.stack{padding:0;background:0 0}.reveal.page .slides>section.past{transform-origin:0 0;transform:translate3d(-40%,0,0) rotateY(-80deg)}.reveal.page .slides>section.future{transform-origin:100% 0;transform:translate3d(0,0,0)}.reveal.page .slides>section>section.past{transform-origin:0 0;transform:translate3d(0,-40%,0) rotateX(80deg)}.reveal.page .slides>section>section.future{transform-origin:0 100%;transform:translate3d(0,0,0)}.reveal .slides section[data-transition=fade],.reveal.fade .slides section:not([data-transition]),.reveal.fade .slides>section>section:not([data-transition]){transform:none;transition:opacity .5s}.reveal.fade.overview .slides section,.reveal.fade.overview .slides>section>section{transition:none}.reveal .slides section[data-transition=none],.reveal.none .slides section:not([data-transition]){transform:none;transition:none}.reveal .pause-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:#000;visibility:hidden;opacity:0;z-index:100;transition:all 1s ease}.reveal .pause-overlay .resume-button{position:absolute;bottom:20px;right:20px;color:#ccc;border-radius:2px;padding:6px 14px;border:2px solid #ccc;font-size:16px;background:0 0;cursor:pointer}.reveal .pause-overlay .resume-button:hover{color:#fff;border-color:#fff}.reveal.paused .pause-overlay{visibility:visible;opacity:1}.reveal .no-transition,.reveal .no-transition *,.reveal .slides.disable-slide-transitions section{transition:none!important}.reveal .slides.disable-slide-transitions section{transform:none!important}.reveal .backgrounds{position:absolute;width:100%;height:100%;top:0;left:0;perspective:600px}.reveal .slide-background{display:none;position:absolute;width:100%;height:100%;opacity:0;visibility:hidden;overflow:hidden;background-color:rgba(0,0,0,0);transition:all .8s cubic-bezier(.26,.86,.44,.985)}.reveal .slide-background-content{position:absolute;width:100%;height:100%;background-position:50% 50%;background-repeat:no-repeat;background-size:cover}.reveal .slide-background.stack{display:block}.reveal .slide-background.present{opacity:1;visibility:visible;z-index:2}.print-pdf .reveal .slide-background{opacity:1!important;visibility:visible!important}.reveal .slide-background video{position:absolute;width:100%;height:100%;max-width:none;max-height:none;top:0;left:0;object-fit:cover}.reveal .slide-background[data-background-size=contain] video{object-fit:contain}.reveal>.backgrounds .slide-background[data-background-transition=none],.reveal[data-background-transition=none]>.backgrounds .slide-background:not([data-background-transition]){transition:none}.reveal>.backgrounds .slide-background[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background:not([data-background-transition]){opacity:1}.reveal>.backgrounds .slide-background.past[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background.past:not([data-background-transition]){transform:translate(-100%,0)}.reveal>.backgrounds .slide-background.future[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background.future:not([data-background-transition]){transform:translate(100%,0)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){transform:translate(0,-100%)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){transform:translate(0,100%)}.reveal>.backgrounds .slide-background.past[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(-100%,0,0) rotateY(-90deg) translate3d(-100%,0,0)}.reveal>.backgrounds .slide-background.future[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(100%,0,0) rotateY(90deg) translate3d(100%,0,0)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(0,-100%,0) rotateX(90deg) translate3d(0,-100%,0)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(0,100%,0) rotateX(-90deg) translate3d(0,100%,0)}.reveal>.backgrounds .slide-background.past[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(-100%,0,0) rotateY(90deg) translate3d(-100%,0,0)}.reveal>.backgrounds .slide-background.future[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(100%,0,0) rotateY(-90deg) translate3d(100%,0,0)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(0,-100%,0) rotateX(-90deg) translate3d(0,-100%,0)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(0,100%,0) rotateX(90deg) translate3d(0,100%,0)}.reveal>.backgrounds .slide-background[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background:not([data-background-transition]){transition-timing-function:ease}.reveal>.backgrounds .slide-background.past[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background.past:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(16)}.reveal>.backgrounds .slide-background.future[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background.future:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(.2)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(16)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(.2)}.reveal[data-transition-speed=fast]>.backgrounds .slide-background{transition-duration:.4s}.reveal[data-transition-speed=slow]>.backgrounds .slide-background{transition-duration:1.2s}.reveal [data-auto-animate-target^=unmatched]{will-change:opacity}.reveal section[data-auto-animate]:not(.stack):not([data-auto-animate=running]) [data-auto-animate-target^=unmatched]{opacity:0}.reveal.overview{perspective-origin:50% 50%;perspective:700px}.reveal.overview .slides{-moz-transform-style:preserve-3d}.reveal.overview .slides section{height:100%;top:0!important;opacity:1!important;overflow:hidden;visibility:visible!important;cursor:pointer;box-sizing:border-box}.reveal.overview .slides section.present,.reveal.overview .slides section:hover{outline:10px solid rgba(150,150,150,.4);outline-offset:10px}.reveal.overview .slides section .fragment{opacity:1;transition:none}.reveal.overview .slides section:after,.reveal.overview .slides section:before{display:none!important}.reveal.overview .slides>section.stack{padding:0;top:0!important;background:0 0;outline:0;overflow:visible}.reveal.overview .backgrounds{perspective:inherit;-moz-transform-style:preserve-3d}.reveal.overview .backgrounds .slide-background{opacity:1;visibility:visible;outline:10px solid rgba(150,150,150,.1);outline-offset:10px}.reveal.overview .backgrounds .slide-background.stack{overflow:visible}.reveal.overview .slides section,.reveal.overview-deactivating .slides section{transition:none}.reveal.overview .backgrounds .slide-background,.reveal.overview-deactivating .backgrounds .slide-background{transition:none}.reveal.rtl .slides,.reveal.rtl .slides h1,.reveal.rtl .slides h2,.reveal.rtl .slides h3,.reveal.rtl .slides h4,.reveal.rtl .slides h5,.reveal.rtl .slides h6{direction:rtl;font-family:sans-serif}.reveal.rtl code,.reveal.rtl pre{direction:ltr}.reveal.rtl ol,.reveal.rtl ul{text-align:right}.reveal.rtl .progress span{transform-origin:100% 0}.reveal.has-parallax-background .backgrounds{transition:all .8s ease}.reveal.has-parallax-background[data-transition-speed=fast] .backgrounds{transition-duration:.4s}.reveal.has-parallax-background[data-transition-speed=slow] .backgrounds{transition-duration:1.2s}.reveal>.overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1000;background:rgba(0,0,0,.95);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);transition:all .3s ease}.reveal>.overlay .spinner{position:absolute;display:block;top:50%;left:50%;width:32px;height:32px;margin:-16px 0 0 -16px;z-index:10;background-image:url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D);visibility:visible;opacity:.6;transition:all .3s ease}.reveal>.overlay header{position:absolute;left:0;top:0;width:100%;padding:5px;z-index:2;box-sizing:border-box}.reveal>.overlay header a{display:inline-block;width:40px;height:40px;line-height:36px;padding:0 10px;float:right;opacity:.6;box-sizing:border-box}.reveal>.overlay header a:hover{opacity:1}.reveal>.overlay header a .icon{display:inline-block;width:20px;height:20px;background-position:50% 50%;background-size:100%;background-repeat:no-repeat}.reveal>.overlay header a.close .icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC)}.reveal>.overlay header a.external .icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==)}.reveal>.overlay .viewport{position:absolute;display:flex;top:50px;right:0;bottom:0;left:0}.reveal>.overlay.overlay-preview .viewport iframe{width:100%;height:100%;max-width:100%;max-height:100%;border:0;opacity:0;visibility:hidden;transition:all .3s ease}.reveal>.overlay.overlay-preview.loaded .viewport iframe{opacity:1;visibility:visible}.reveal>.overlay.overlay-preview.loaded .viewport-inner{position:absolute;z-index:-1;left:0;top:45%;width:100%;text-align:center;letter-spacing:normal}.reveal>.overlay.overlay-preview .x-frame-error{opacity:0;transition:opacity .3s ease .3s}.reveal>.overlay.overlay-preview.loaded .x-frame-error{opacity:1}.reveal>.overlay.overlay-preview.loaded .spinner{opacity:0;visibility:hidden;transform:scale(.2)}.reveal>.overlay.overlay-help .viewport{overflow:auto;color:#fff}.reveal>.overlay.overlay-help .viewport .viewport-inner{width:600px;margin:auto;padding:20px 20px 80px 20px;text-align:center;letter-spacing:normal}.reveal>.overlay.overlay-help .viewport .viewport-inner .title{font-size:20px}.reveal>.overlay.overlay-help .viewport .viewport-inner table{border:1px solid #fff;border-collapse:collapse;font-size:16px}.reveal>.overlay.overlay-help .viewport .viewport-inner table td,.reveal>.overlay.overlay-help .viewport .viewport-inner table th{width:200px;padding:14px;border:1px solid #fff;vertical-align:middle}.reveal>.overlay.overlay-help .viewport .viewport-inner table th{padding-top:20px;padding-bottom:20px}.reveal .playback{position:absolute;left:15px;bottom:20px;z-index:30;cursor:pointer;transition:all .4s ease;-webkit-tap-highlight-color:transparent}.reveal.overview .playback{opacity:0;visibility:hidden}.reveal .hljs{min-height:100%}.reveal .hljs table{margin:initial}.reveal .hljs-ln-code,.reveal .hljs-ln-numbers{padding:0;border:0}.reveal .hljs-ln-numbers{opacity:.6;padding-right:.75em;text-align:right;vertical-align:top}.reveal .hljs.has-highlights tr:not(.highlight-line){opacity:.4}.reveal .hljs.has-highlights.fragment{transition:all .2s ease}.reveal .hljs:not(:first-child).fragment{position:absolute;top:0;left:0;width:100%;box-sizing:border-box}.reveal pre[data-auto-animate-target]{overflow:hidden}.reveal pre[data-auto-animate-target] code{height:100%}.reveal .roll{display:inline-block;line-height:1.2;overflow:hidden;vertical-align:top;perspective:400px;perspective-origin:50% 50%}.reveal .roll:hover{background:0 0;text-shadow:none}.reveal .roll span{display:block;position:relative;padding:0 2px;pointer-events:none;transition:all .4s ease;transform-origin:50% 0;transform-style:preserve-3d;backface-visibility:hidden}.reveal .roll:hover span{background:rgba(0,0,0,.5);transform:translate3d(0,0,-45px) rotateX(90deg)}.reveal .roll span:after{content:attr(data-title);display:block;position:absolute;left:0;top:0;padding:0 2px;backface-visibility:hidden;transform-origin:50% 0;transform:translate3d(0,110%,0) rotateX(-90deg)}.reveal aside.notes{display:none}.reveal .speaker-notes{display:none;position:absolute;width:33.3333333333%;height:100%;top:0;left:100%;padding:14px 18px 14px 18px;z-index:1;font-size:18px;line-height:1.4;border:1px solid rgba(0,0,0,.05);color:#222;background-color:#f5f5f5;overflow:auto;box-sizing:border-box;text-align:left;font-family:Helvetica,sans-serif;-webkit-overflow-scrolling:touch}.reveal .speaker-notes .notes-placeholder{color:#ccc;font-style:italic}.reveal .speaker-notes:focus{outline:0}.reveal .speaker-notes:before{content:"Speaker notes";display:block;margin-bottom:10px;opacity:.5}.reveal.show-notes{max-width:75%;overflow:visible}.reveal.show-notes .speaker-notes{display:block}@media screen and (min-width:1600px){.reveal .speaker-notes{font-size:20px}}@media screen and (max-width:1024px){.reveal.show-notes{border-left:0;max-width:none;max-height:70%;max-height:70vh;overflow:visible}.reveal.show-notes .speaker-notes{top:100%;left:0;width:100%;height:30vh;border:0}}@media screen and (max-width:600px){.reveal.show-notes{max-height:60%;max-height:60vh}.reveal.show-notes .speaker-notes{top:100%;height:40vh}.reveal .speaker-notes{font-size:14px}}.reveal .jump-to-slide{position:absolute;top:15px;left:15px;z-index:30;font-size:32px;-webkit-tap-highlight-color:transparent}.reveal .jump-to-slide-input{background:0 0;padding:8px;font-size:inherit;color:currentColor;border:0}.reveal .jump-to-slide-input::placeholder{color:currentColor;opacity:.5}.reveal.has-dark-background .jump-to-slide-input{color:#fff}.reveal.has-light-background .jump-to-slide-input{color:#222}.reveal .jump-to-slide-input:focus{outline:0}.zoomed .reveal *,.zoomed .reveal :after,.zoomed .reveal :before{backface-visibility:visible!important}.zoomed .reveal .controls,.zoomed .reveal .progress{opacity:0}.zoomed .reveal .roll span{background:0 0}.zoomed .reveal .roll span:after{visibility:hidden}.reveal-viewport.loading-scroll-mode{visibility:hidden}.reveal-viewport.reveal-scroll{margin:0 auto;overflow:auto;overflow-x:hidden;overflow-y:auto;z-index:1;--r-scrollbar-width:7px;--r-scrollbar-trigger-size:5px;--r-controls-spacing:8px}@media screen and (max-width:500px){.reveal-viewport.reveal-scroll{--r-scrollbar-width:3px;--r-scrollbar-trigger-size:3px}}.reveal-viewport.reveal-scroll .backgrounds,.reveal-viewport.reveal-scroll .controls,.reveal-viewport.reveal-scroll .playback,.reveal-viewport.reveal-scroll .progress,.reveal-viewport.reveal-scroll .slide-number,.reveal-viewport.reveal-scroll .speaker-notes{display:none!important}.reveal-viewport.reveal-scroll .overlay,.reveal-viewport.reveal-scroll .pause-overlay{position:fixed}.reveal-viewport.reveal-scroll .reveal{overflow:visible;touch-action:manipulation}.reveal-viewport.reveal-scroll .slides{position:static;pointer-events:initial;left:auto;top:auto;width:100%!important;margin:0;padding:0;overflow:visible;display:block;perspective:none;perspective-origin:50% 50%}.reveal-viewport.reveal-scroll .scroll-page{position:relative;width:100%;height:calc(var(--page-height) + var(--page-scroll-padding));z-index:1;overflow:visible}.reveal-viewport.reveal-scroll .scroll-page-sticky{position:sticky;height:var(--page-height);top:0}.reveal-viewport.reveal-scroll .scroll-page-content{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden}.reveal-viewport.reveal-scroll .scroll-page section{visibility:visible!important;display:block!important;position:absolute!important;width:var(--slide-width)!important;height:var(--slide-height)!important;top:50%!important;left:50%!important;opacity:1!important;transform:scale(var(--slide-scale)) translate(-50%,-50%)!important;transform-style:flat!important;transform-origin:0 0!important}.reveal-viewport.reveal-scroll .slide-background{display:block!important;position:absolute;top:0;left:0;width:100%;height:100%;z-index:auto!important;visibility:visible;opacity:1;touch-action:manipulation}.reveal-viewport.reveal-scroll[data-scrollbar=auto]::-webkit-scrollbar,.reveal-viewport.reveal-scroll[data-scrollbar=true]::-webkit-scrollbar{display:none}.reveal-viewport.reveal-scroll[data-scrollbar=auto],.reveal-viewport.reveal-scroll[data-scrollbar=true]{scrollbar-width:none}.reveal-viewport.has-dark-background,.reveal.has-dark-background{--r-overlay-element-bg-color:240,240,240;--r-overlay-element-fg-color:0,0,0}.reveal-viewport.has-light-background,.reveal.has-light-background{--r-overlay-element-bg-color:0,0,0;--r-overlay-element-fg-color:240,240,240}.reveal-viewport.reveal-scroll .scrollbar{position:sticky;top:50%;z-index:20;opacity:0;transition:all .3s ease}.reveal-viewport.reveal-scroll .scrollbar.visible,.reveal-viewport.reveal-scroll .scrollbar:hover{opacity:1}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-inner{position:absolute;width:var(--r-scrollbar-width);height:calc(var(--viewport-height) - var(--r-controls-spacing) * 2);right:var(--r-controls-spacing);top:0;transform:translateY(-50%);border-radius:var(--r-scrollbar-width);z-index:10}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-playhead{position:absolute;width:var(--r-scrollbar-width);height:var(--r-scrollbar-width);top:0;left:0;border-radius:var(--r-scrollbar-width);background-color:rgba(var(--r-overlay-element-bg-color),1);z-index:11;transition:background-color .2s ease}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide{position:absolute;width:100%;background-color:rgba(var(--r-overlay-element-bg-color),.2);box-shadow:0 0 0 1px rgba(var(--r-overlay-element-fg-color),.1);border-radius:var(--r-scrollbar-width);transition:background-color .2s ease}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide:after{content:"";position:absolute;width:200%;height:100%;top:0;left:-50%;background:rgba(0,0,0,0);z-index:-1}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide.active,.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide:hover{background-color:rgba(var(--r-overlay-element-bg-color),.4)}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-trigger{position:absolute;width:100%;transition:background-color .2s ease}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide.active.has-triggers{background-color:rgba(var(--r-overlay-element-bg-color),.4);z-index:10}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide.active .scrollbar-trigger:after{content:"";position:absolute;width:var(--r-scrollbar-trigger-size);height:var(--r-scrollbar-trigger-size);border-radius:20px;top:50%;left:50%;transform:translate(-50%,-50%);background-color:rgba(var(--r-overlay-element-bg-color),1);transition:transform .2s ease,opacity .2s ease;opacity:.4}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide.active .scrollbar-trigger.active:after,.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide.active .scrollbar-trigger.active~.scrollbar-trigger:after{opacity:1}.reveal-viewport.reveal-scroll .scrollbar .scrollbar-slide.active .scrollbar-trigger~.scrollbar-trigger.active:after{transform:translate(calc(var(--r-scrollbar-width) * -2),0);background-color:rgba(var(--r-overlay-element-bg-color),1)}html.reveal-print *{-webkit-print-color-adjust:exact}html.reveal-print{width:100%;height:100%;overflow:visible}html.reveal-print body{margin:0 auto!important;border:0;padding:0;float:none!important;overflow:visible}html.reveal-print .nestedarrow,html.reveal-print .reveal .controls,html.reveal-print .reveal .playback,html.reveal-print .reveal .progress,html.reveal-print .reveal.overview,html.reveal-print .state-background{display:none!important}html.reveal-print .reveal pre code{overflow:hidden!important}html.reveal-print .reveal{width:auto!important;height:auto!important;overflow:hidden!important}html.reveal-print .reveal .slides{position:static;width:100%!important;height:auto!important;zoom:1!important;pointer-events:initial;left:auto;top:auto;margin:0!important;padding:0!important;overflow:visible;display:block;perspective:none;perspective-origin:50% 50%}html.reveal-print .reveal .slides .pdf-page{position:relative;overflow:hidden;z-index:1;page-break-after:always}html.reveal-print .reveal .slides .pdf-page:last-of-type{page-break-after:avoid}html.reveal-print .reveal .slides section{visibility:visible!important;display:block!important;position:absolute!important;margin:0!important;padding:0!important;box-sizing:border-box!important;min-height:1px;opacity:1!important;transform-style:flat!important;transform:none!important}html.reveal-print .reveal section.stack{position:relative!important;margin:0!important;padding:0!important;page-break-after:avoid!important;height:auto!important;min-height:auto!important}html.reveal-print .reveal img{box-shadow:none}html.reveal-print .reveal .backgrounds{display:none}html.reveal-print .reveal .slide-background{display:block!important;position:absolute;top:0;left:0;width:100%;height:100%;z-index:auto!important}html.reveal-print .reveal.show-notes{max-width:none;max-height:none}html.reveal-print .reveal .speaker-notes-pdf{display:block;width:100%;height:auto;max-height:none;top:auto;right:auto;bottom:auto;left:auto;z-index:100}html.reveal-print .reveal .speaker-notes-pdf[data-layout=separate-page]{position:relative;color:inherit;background-color:transparent;padding:20px;page-break-after:always;border:0}html.reveal-print .reveal .slide-number-pdf{display:block;position:absolute;font-size:14px;visibility:visible}html.reveal-print .aria-status{display:none}@media print{html:not(.print-pdf){overflow:visible;width:auto;height:auto}html:not(.print-pdf) body{margin:0;padding:0;overflow:visible}html:not(.print-pdf) .reveal{background:#fff;font-size:20pt}html:not(.print-pdf) .reveal .backgrounds,html:not(.print-pdf) .reveal .controls,html:not(.print-pdf) .reveal .progress,html:not(.print-pdf) .reveal .slide-number,html:not(.print-pdf) .reveal .state-background{display:none!important}html:not(.print-pdf) .reveal li,html:not(.print-pdf) .reveal p,html:not(.print-pdf) .reveal td{font-size:20pt!important;color:#000}html:not(.print-pdf) .reveal h1,html:not(.print-pdf) .reveal h2,html:not(.print-pdf) .reveal h3,html:not(.print-pdf) .reveal h4,html:not(.print-pdf) .reveal h5,html:not(.print-pdf) .reveal h6{color:#000!important;height:auto;line-height:normal;text-align:left;letter-spacing:normal}html:not(.print-pdf) .reveal h1{font-size:28pt!important}html:not(.print-pdf) .reveal h2{font-size:24pt!important}html:not(.print-pdf) .reveal h3{font-size:22pt!important}html:not(.print-pdf) .reveal h4{font-size:22pt!important;font-variant:small-caps}html:not(.print-pdf) .reveal h5{font-size:21pt!important}html:not(.print-pdf) .reveal h6{font-size:20pt!important;font-style:italic}html:not(.print-pdf) .reveal a:link,html:not(.print-pdf) .reveal a:visited{color:#000!important;font-weight:700;text-decoration:underline}html:not(.print-pdf) .reveal div,html:not(.print-pdf) .reveal ol,html:not(.print-pdf) .reveal p,html:not(.print-pdf) .reveal ul{visibility:visible;position:static;width:auto;height:auto;display:block;overflow:visible;margin:0;text-align:left!important}html:not(.print-pdf) .reveal pre,html:not(.print-pdf) .reveal table{margin-left:0;margin-right:0}html:not(.print-pdf) .reveal pre code{padding:20px}html:not(.print-pdf) .reveal blockquote{margin:20px 0}html:not(.print-pdf) .reveal .slides{position:static!important;width:auto!important;height:auto!important;left:0!important;top:0!important;margin-left:0!important;margin-top:0!important;padding:0!important;zoom:1!important;transform:none!important;overflow:visible!important;display:block!important;text-align:left!important;perspective:none;perspective-origin:50% 50%}html:not(.print-pdf) .reveal .slides section{visibility:visible!important;position:static!important;width:auto!important;height:auto!important;display:block!important;overflow:visible!important;left:0!important;top:0!important;margin-left:0!important;margin-top:0!important;padding:60px 20px!important;z-index:auto!important;opacity:1!important;page-break-after:always!important;transform-style:flat!important;transform:none!important;transition:none!important}html:not(.print-pdf) .reveal .slides section.stack{padding:0!important}html:not(.print-pdf) .reveal .slides section:last-of-type{page-break-after:avoid!important}html:not(.print-pdf) .reveal .slides section .fragment{opacity:1!important;visibility:visible!important;transform:none!important}html:not(.print-pdf) .reveal .r-fit-text{white-space:normal!important}html:not(.print-pdf) .reveal section img{display:block;margin:15px 0;background:#fff;border:1px solid #666;box-shadow:none}html:not(.print-pdf) .reveal section small{font-size:.8em}html:not(.print-pdf) .reveal .hljs{max-height:100%;white-space:pre-wrap;word-wrap:break-word;word-break:break-word;font-size:15pt}html:not(.print-pdf) .reveal .hljs .hljs-ln-numbers{white-space:nowrap}html:not(.print-pdf) .reveal .hljs td{font-size:inherit!important;color:inherit!important}} \ No newline at end of file diff --git a/src/.quarto/_freeze/site_libs/revealjs/dist/reveal.esm.js b/src/.quarto/_freeze/site_libs/revealjs/dist/reveal.esm.js new file mode 100644 index 0000000000000000000000000000000000000000..60411ac123109fa0a96b3607e68fc579a9c33fbc --- /dev/null +++ b/src/.quarto/_freeze/site_libs/revealjs/dist/reveal.esm.js @@ -0,0 +1,9 @@ +/*! +* reveal.js 5.1.0 +* https://revealjs.com +* MIT licensed +* +* Copyright (C) 2011-2024 Hakim El Hattab, https://hakim.se +*/ +const e=(e,t)=>{for(let i in t)e[i]=t[i];return e},t=(e,t)=>Array.from(e.querySelectorAll(t)),i=(e,t,i)=>{i?e.classList.add(t):e.classList.remove(t)},s=e=>{if("string"==typeof e){if("null"===e)return null;if("true"===e)return!0;if("false"===e)return!1;if(e.match(/^-?[\d\.]+$/))return parseFloat(e)}return e},a=(e,t)=>{e.style.transform=t},n=(e,t)=>{let i=e.matches||e.matchesSelector||e.msMatchesSelector;return!(!i||!i.call(e,t))},r=(e,t)=>{if("function"==typeof e.closest)return e.closest(t);for(;e;){if(n(e,t))return e;e=e.parentNode}return null},o=e=>{let t=(e=e||document.documentElement).requestFullscreen||e.webkitRequestFullscreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullscreen;t&&t.apply(e)},l=e=>{let t=document.createElement("style");return t.type="text/css",e&&e.length>0&&(t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document.createTextNode(e))),document.head.appendChild(t),t},d=()=>{let e={};location.search.replace(/[A-Z0-9]+?=([\w\.%-]*)/gi,(t=>{e[t.split("=").shift()]=t.split("=").pop()}));for(let t in e){let i=e[t];e[t]=s(unescape(i))}return void 0!==e.dependencies&&delete e.dependencies,e},c={mp4:"video/mp4",m4a:"video/mp4",ogv:"video/ogg",mpeg:"video/mpeg",webm:"video/webm"},h=navigator.userAgent,u=/(iphone|ipod|ipad|android)/gi.test(h)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1,g=/android/gi.test(h);var p=function(e){if(e){var t=function(e){return[].slice.call(e)},i=3,s=[],a=null,n="requestAnimationFrame"in e?function(){e.cancelAnimationFrame(a),a=e.requestAnimationFrame((function(){return o(s.filter((function(e){return e.dirty&&e.active})))}))}:function(){},r=function(e){return function(){s.forEach((function(t){return t.dirty=e})),n()}},o=function(e){e.filter((function(e){return!e.styleComputed})).forEach((function(e){e.styleComputed=h(e)})),e.filter(u).forEach(g);var t=e.filter(c);t.forEach(d),t.forEach((function(e){g(e),l(e)})),t.forEach(p)},l=function(e){return e.dirty=0},d=function(e){e.availableWidth=e.element.parentNode.clientWidth,e.currentWidth=e.element.scrollWidth,e.previousFontSize=e.currentFontSize,e.currentFontSize=Math.min(Math.max(e.minSize,e.availableWidth/e.currentWidth*e.previousFontSize),e.maxSize),e.whiteSpace=e.multiLine&&e.currentFontSize===e.minSize?"normal":"nowrap"},c=function(e){return 2!==e.dirty||2===e.dirty&&e.element.parentNode.clientWidth!==e.availableWidth},h=function(t){var i=e.getComputedStyle(t.element,null);return t.currentFontSize=parseFloat(i.getPropertyValue("font-size")),t.display=i.getPropertyValue("display"),t.whiteSpace=i.getPropertyValue("white-space"),!0},u=function(e){var t=!1;return!e.preStyleTestCompleted&&(/inline-/.test(e.display)||(t=!0,e.display="inline-block"),"nowrap"!==e.whiteSpace&&(t=!0,e.whiteSpace="nowrap"),e.preStyleTestCompleted=!0,t)},g=function(e){e.element.style.whiteSpace=e.whiteSpace,e.element.style.display=e.display,e.element.style.fontSize=e.currentFontSize+"px"},p=function(e){e.element.dispatchEvent(new CustomEvent("fit",{detail:{oldValue:e.previousFontSize,newValue:e.currentFontSize,scaleFactor:e.currentFontSize/e.previousFontSize}}))},v=function(e,t){return function(){e.dirty=t,e.active&&n()}},m=function(e){return function(){s=s.filter((function(t){return t.element!==e.element})),e.observeMutations&&e.observer.disconnect(),e.element.style.whiteSpace=e.originalStyle.whiteSpace,e.element.style.display=e.originalStyle.display,e.element.style.fontSize=e.originalStyle.fontSize}},f=function(e){return function(){e.active||(e.active=!0,n())}},y=function(e){return function(){return e.active=!1}},b=function(e){e.observeMutations&&(e.observer=new MutationObserver(v(e,1)),e.observer.observe(e.element,e.observeMutations))},w={minSize:16,maxSize:512,multiLine:!0,observeMutations:"MutationObserver"in e&&{subtree:!0,childList:!0,characterData:!0}},E=null,S=function(){e.clearTimeout(E),E=e.setTimeout(r(2),k.observeWindowDelay)},A=["resize","orientationchange"];return Object.defineProperty(k,"observeWindow",{set:function(t){var i="".concat(t?"add":"remove","EventListener");A.forEach((function(t){e[i](t,S)}))}}),k.observeWindow=!0,k.observeWindowDelay=100,k.fitAll=r(i),k}function R(e,t){var a=Object.assign({},w,t),r=e.map((function(e){var t=Object.assign({},a,{element:e,active:!0});return function(e){e.originalStyle={whiteSpace:e.element.style.whiteSpace,display:e.element.style.display,fontSize:e.element.style.fontSize},b(e),e.newbie=!0,e.dirty=!0,s.push(e)}(t),{element:e,fit:v(t,i),unfreeze:f(t),freeze:y(t),unsubscribe:m(t)}}));return n(),r}function k(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"string"==typeof e?R(t(document.querySelectorAll(e)),i):R([e],i)[0]}}("undefined"==typeof window?null:window);class v{constructor(e){this.Reveal=e,this.startEmbeddedIframe=this.startEmbeddedIframe.bind(this)}shouldPreload(e){if(this.Reveal.isScrollView())return!0;let t=this.Reveal.getConfig().preloadIframes;return"boolean"!=typeof t&&(t=e.hasAttribute("data-preload")),t}load(e,i={}){e.style.display=this.Reveal.getConfig().display,t(e,"img[data-src], video[data-src], audio[data-src], iframe[data-src]").forEach((e=>{("IFRAME"!==e.tagName||this.shouldPreload(e))&&(e.setAttribute("src",e.getAttribute("data-src")),e.setAttribute("data-lazy-loaded",""),e.removeAttribute("data-src"))})),t(e,"video, audio").forEach((e=>{let i=0;t(e,"source[data-src]").forEach((e=>{e.setAttribute("src",e.getAttribute("data-src")),e.removeAttribute("data-src"),e.setAttribute("data-lazy-loaded",""),i+=1})),u&&"VIDEO"===e.tagName&&e.setAttribute("playsinline",""),i>0&&e.load()}));let s=e.slideBackgroundElement;if(s){s.style.display="block";let t=e.slideBackgroundContentElement,a=e.getAttribute("data-background-iframe");if(!1===s.hasAttribute("data-loaded")){s.setAttribute("data-loaded","true");let n=e.getAttribute("data-background-image"),r=e.getAttribute("data-background-video"),o=e.hasAttribute("data-background-video-loop"),l=e.hasAttribute("data-background-video-muted");if(n)/^data:/.test(n.trim())?t.style.backgroundImage=`url(${n.trim()})`:t.style.backgroundImage=n.split(",").map((e=>`url(${((e="")=>encodeURI(e).replace(/%5B/g,"[").replace(/%5D/g,"]").replace(/[!'()*]/g,(e=>`%${e.charCodeAt(0).toString(16).toUpperCase()}`)))(decodeURI(e.trim()))})`)).join(",");else if(r&&!this.Reveal.isSpeakerNotes()){let e=document.createElement("video");o&&e.setAttribute("loop",""),l&&(e.muted=!0),u&&(e.muted=!0,e.setAttribute("playsinline","")),r.split(",").forEach((t=>{const i=document.createElement("source");i.setAttribute("src",t);let s=((e="")=>c[e.split(".").pop()])(t);s&&i.setAttribute("type",s),e.appendChild(i)})),t.appendChild(e)}else if(a&&!0!==i.excludeIframes){let e=document.createElement("iframe");e.setAttribute("allowfullscreen",""),e.setAttribute("mozallowfullscreen",""),e.setAttribute("webkitallowfullscreen",""),e.setAttribute("allow","autoplay"),e.setAttribute("data-src",a),e.style.width="100%",e.style.height="100%",e.style.maxHeight="100%",e.style.maxWidth="100%",t.appendChild(e)}}let n=t.querySelector("iframe[data-src]");n&&this.shouldPreload(s)&&!/autoplay=(1|true|yes)/gi.test(a)&&n.getAttribute("src")!==a&&n.setAttribute("src",a)}this.layout(e)}layout(e){Array.from(e.querySelectorAll(".r-fit-text")).forEach((e=>{p(e,{minSize:24,maxSize:.8*this.Reveal.getConfig().height,observeMutations:!1,observeWindow:!1})}))}unload(e){e.style.display="none";let i=this.Reveal.getSlideBackground(e);i&&(i.style.display="none",t(i,"iframe[src]").forEach((e=>{e.removeAttribute("src")}))),t(e,"video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]").forEach((e=>{e.setAttribute("data-src",e.getAttribute("src")),e.removeAttribute("src")})),t(e,"video[data-lazy-loaded] source[src], audio source[src]").forEach((e=>{e.setAttribute("data-src",e.getAttribute("src")),e.removeAttribute("src")}))}formatEmbeddedContent(){let e=(e,i,s)=>{t(this.Reveal.getSlidesElement(),"iframe["+e+'*="'+i+'"]').forEach((t=>{let i=t.getAttribute(e);i&&-1===i.indexOf(s)&&t.setAttribute(e,i+(/\?/.test(i)?"&":"?")+s)}))};e("src","youtube.com/embed/","enablejsapi=1"),e("data-src","youtube.com/embed/","enablejsapi=1"),e("src","player.vimeo.com/","api=1"),e("data-src","player.vimeo.com/","api=1")}startEmbeddedContent(e){e&&!this.Reveal.isSpeakerNotes()&&(t(e,'img[src$=".gif"]').forEach((e=>{e.setAttribute("src",e.getAttribute("src"))})),t(e,"video, audio").forEach((e=>{if(r(e,".fragment")&&!r(e,".fragment.visible"))return;let t=this.Reveal.getConfig().autoPlayMedia;if("boolean"!=typeof t&&(t=e.hasAttribute("data-autoplay")||!!r(e,".slide-background")),t&&"function"==typeof e.play)if(e.readyState>1)this.startEmbeddedMedia({target:e});else if(u){let t=e.play();t&&"function"==typeof t.catch&&!1===e.controls&&t.catch((()=>{e.controls=!0,e.addEventListener("play",(()=>{e.controls=!1}))}))}else e.removeEventListener("loadeddata",this.startEmbeddedMedia),e.addEventListener("loadeddata",this.startEmbeddedMedia)})),t(e,"iframe[src]").forEach((e=>{r(e,".fragment")&&!r(e,".fragment.visible")||this.startEmbeddedIframe({target:e})})),t(e,"iframe[data-src]").forEach((e=>{r(e,".fragment")&&!r(e,".fragment.visible")||e.getAttribute("src")!==e.getAttribute("data-src")&&(e.removeEventListener("load",this.startEmbeddedIframe),e.addEventListener("load",this.startEmbeddedIframe),e.setAttribute("src",e.getAttribute("data-src")))})))}startEmbeddedMedia(e){let t=!!r(e.target,"html"),i=!!r(e.target,".present");t&&i&&(e.target.paused||e.target.ended)&&(e.target.currentTime=0,e.target.play()),e.target.removeEventListener("loadeddata",this.startEmbeddedMedia)}startEmbeddedIframe(e){let t=e.target;if(t&&t.contentWindow){let i=!!r(e.target,"html"),s=!!r(e.target,".present");if(i&&s){let e=this.Reveal.getConfig().autoPlayMedia;"boolean"!=typeof e&&(e=t.hasAttribute("data-autoplay")||!!r(t,".slide-background")),/youtube\.com\/embed\//.test(t.getAttribute("src"))&&e?t.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}',"*"):/player\.vimeo\.com\//.test(t.getAttribute("src"))&&e?t.contentWindow.postMessage('{"method":"play"}',"*"):t.contentWindow.postMessage("slide:start","*")}}}stopEmbeddedContent(i,s={}){s=e({unloadIframes:!0},s),i&&i.parentNode&&(t(i,"video, audio").forEach((e=>{e.hasAttribute("data-ignore")||"function"!=typeof e.pause||(e.setAttribute("data-paused-by-reveal",""),e.pause())})),t(i,"iframe").forEach((e=>{e.contentWindow&&e.contentWindow.postMessage("slide:stop","*"),e.removeEventListener("load",this.startEmbeddedIframe)})),t(i,'iframe[src*="youtube.com/embed/"]').forEach((e=>{!e.hasAttribute("data-ignore")&&e.contentWindow&&"function"==typeof e.contentWindow.postMessage&&e.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}',"*")})),t(i,'iframe[src*="player.vimeo.com/"]').forEach((e=>{!e.hasAttribute("data-ignore")&&e.contentWindow&&"function"==typeof e.contentWindow.postMessage&&e.contentWindow.postMessage('{"method":"pause"}',"*")})),!0===s.unloadIframes&&t(i,"iframe[data-src]").forEach((e=>{e.setAttribute("src","about:blank"),e.removeAttribute("src")})))}}const m=".slides section",f=".slides>section",y=".slides>section.present>section",b=/registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener|showPreview/,w=/fade-(down|up|right|left|out|in-then-out|in-then-semi-out)|semi-fade-out|current-visible|shrink|grow/;class E{constructor(e){this.Reveal=e}render(){this.element=document.createElement("div"),this.element.className="slide-number",this.Reveal.getRevealElement().appendChild(this.element)}configure(e,t){let i="none";e.slideNumber&&!this.Reveal.isPrintView()&&("all"===e.showSlideNumber||"speaker"===e.showSlideNumber&&this.Reveal.isSpeakerNotes())&&(i="block"),this.element.style.display=i}update(){this.Reveal.getConfig().slideNumber&&this.element&&(this.element.innerHTML=this.getSlideNumber())}getSlideNumber(e=this.Reveal.getCurrentSlide()){let t,i=this.Reveal.getConfig(),s="h.v";if("function"==typeof i.slideNumber)t=i.slideNumber(e);else{"string"==typeof i.slideNumber&&(s=i.slideNumber),/c/.test(s)||1!==this.Reveal.getHorizontalSlides().length||(s="c");let a=e&&"uncounted"===e.dataset.visibility?0:1;switch(t=[],s){case"c":t.push(this.Reveal.getSlidePastCount(e)+a);break;case"c/t":t.push(this.Reveal.getSlidePastCount(e)+a,"/",this.Reveal.getTotalSlides());break;default:let i=this.Reveal.getIndices(e);t.push(i.h+a);let n="h/v"===s?"/":".";this.Reveal.isVerticalSlide(e)&&t.push(n,i.v+1)}}let a="#"+this.Reveal.location.getHash(e);return this.formatNumber(t[0],t[1],t[2],a)}formatNumber(e,t,i,s="#"+this.Reveal.location.getHash()){return"number"!=typeof i||isNaN(i)?`\n\t\t\t\t\t${e}\n\t\t\t\t\t`:`\n\t\t\t\t\t${e}\n\t\t\t\t\t${t}\n\t\t\t\t\t${i}\n\t\t\t\t\t`}destroy(){this.element.remove()}}class S{constructor(e){this.Reveal=e,this.onInput=this.onInput.bind(this),this.onBlur=this.onBlur.bind(this),this.onKeyDown=this.onKeyDown.bind(this)}render(){this.element=document.createElement("div"),this.element.className="jump-to-slide",this.jumpInput=document.createElement("input"),this.jumpInput.type="text",this.jumpInput.className="jump-to-slide-input",this.jumpInput.placeholder="Jump to slide",this.jumpInput.addEventListener("input",this.onInput),this.jumpInput.addEventListener("keydown",this.onKeyDown),this.jumpInput.addEventListener("blur",this.onBlur),this.element.appendChild(this.jumpInput)}show(){this.indicesOnShow=this.Reveal.getIndices(),this.Reveal.getRevealElement().appendChild(this.element),this.jumpInput.focus()}hide(){this.isVisible()&&(this.element.remove(),this.jumpInput.value="",clearTimeout(this.jumpTimeout),delete this.jumpTimeout)}isVisible(){return!!this.element.parentNode}jump(){clearTimeout(this.jumpTimeout),delete this.jumpTimeout;let e,t=this.jumpInput.value.trim("");if(/^\d+$/.test(t)){const i=this.Reveal.getConfig().slideNumber;if("c"===i||"c/t"===i){const i=this.Reveal.getSlides()[parseInt(t,10)-1];i&&(e=this.Reveal.getIndices(i))}}return e||(/^\d+\.\d+$/.test(t)&&(t=t.replace(".","/")),e=this.Reveal.location.getIndicesFromHash(t,{oneBasedIndex:!0})),!e&&/\S+/i.test(t)&&t.length>1&&(e=this.search(t)),e&&""!==t?(this.Reveal.slide(e.h,e.v,e.f),!0):(this.Reveal.slide(this.indicesOnShow.h,this.indicesOnShow.v,this.indicesOnShow.f),!1)}jumpAfter(e){clearTimeout(this.jumpTimeout),this.jumpTimeout=setTimeout((()=>this.jump()),e)}search(e){const t=new RegExp("\\b"+e.trim()+"\\b","i"),i=this.Reveal.getSlides().find((e=>t.test(e.innerText)));return i?this.Reveal.getIndices(i):null}cancel(){this.Reveal.slide(this.indicesOnShow.h,this.indicesOnShow.v,this.indicesOnShow.f),this.hide()}confirm(){this.jump(),this.hide()}destroy(){this.jumpInput.removeEventListener("input",this.onInput),this.jumpInput.removeEventListener("keydown",this.onKeyDown),this.jumpInput.removeEventListener("blur",this.onBlur),this.element.remove()}onKeyDown(e){13===e.keyCode?this.confirm():27===e.keyCode&&(this.cancel(),e.stopImmediatePropagation())}onInput(e){this.jumpAfter(200)}onBlur(){setTimeout((()=>this.hide()),1)}}const A=e=>{let t=e.match(/^#([0-9a-f]{3})$/i);if(t&&t[1])return t=t[1],{r:17*parseInt(t.charAt(0),16),g:17*parseInt(t.charAt(1),16),b:17*parseInt(t.charAt(2),16)};let i=e.match(/^#([0-9a-f]{6})$/i);if(i&&i[1])return i=i[1],{r:parseInt(i.slice(0,2),16),g:parseInt(i.slice(2,4),16),b:parseInt(i.slice(4,6),16)};let s=e.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);if(s)return{r:parseInt(s[1],10),g:parseInt(s[2],10),b:parseInt(s[3],10)};let a=e.match(/^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i);return a?{r:parseInt(a[1],10),g:parseInt(a[2],10),b:parseInt(a[3],10),a:parseFloat(a[4])}:null};class R{constructor(e){this.Reveal=e}render(){this.element=document.createElement("div"),this.element.className="backgrounds",this.Reveal.getRevealElement().appendChild(this.element)}create(){this.element.innerHTML="",this.element.classList.add("no-transition"),this.Reveal.getHorizontalSlides().forEach((e=>{let i=this.createBackground(e,this.element);t(e,"section").forEach((e=>{this.createBackground(e,i),i.classList.add("stack")}))})),this.Reveal.getConfig().parallaxBackgroundImage?(this.element.style.backgroundImage='url("'+this.Reveal.getConfig().parallaxBackgroundImage+'")',this.element.style.backgroundSize=this.Reveal.getConfig().parallaxBackgroundSize,this.element.style.backgroundRepeat=this.Reveal.getConfig().parallaxBackgroundRepeat,this.element.style.backgroundPosition=this.Reveal.getConfig().parallaxBackgroundPosition,setTimeout((()=>{this.Reveal.getRevealElement().classList.add("has-parallax-background")}),1)):(this.element.style.backgroundImage="",this.Reveal.getRevealElement().classList.remove("has-parallax-background"))}createBackground(e,t){let i=document.createElement("div");i.className="slide-background "+e.className.replace(/present|past|future/,"");let s=document.createElement("div");return s.className="slide-background-content",i.appendChild(s),t.appendChild(i),e.slideBackgroundElement=i,e.slideBackgroundContentElement=s,this.sync(e),i}sync(e){const t=e.slideBackgroundElement,i=e.slideBackgroundContentElement,s={background:e.getAttribute("data-background"),backgroundSize:e.getAttribute("data-background-size"),backgroundImage:e.getAttribute("data-background-image"),backgroundVideo:e.getAttribute("data-background-video"),backgroundIframe:e.getAttribute("data-background-iframe"),backgroundColor:e.getAttribute("data-background-color"),backgroundGradient:e.getAttribute("data-background-gradient"),backgroundRepeat:e.getAttribute("data-background-repeat"),backgroundPosition:e.getAttribute("data-background-position"),backgroundTransition:e.getAttribute("data-background-transition"),backgroundOpacity:e.getAttribute("data-background-opacity")},a=e.hasAttribute("data-preload");e.classList.remove("has-dark-background"),e.classList.remove("has-light-background"),t.removeAttribute("data-loaded"),t.removeAttribute("data-background-hash"),t.removeAttribute("data-background-size"),t.removeAttribute("data-background-transition"),t.style.backgroundColor="",i.style.backgroundSize="",i.style.backgroundRepeat="",i.style.backgroundPosition="",i.style.backgroundImage="",i.style.opacity="",i.innerHTML="",s.background&&(/^(http|file|\/\/)/gi.test(s.background)||/\.(svg|png|jpg|jpeg|gif|bmp|webp)([?#\s]|$)/gi.test(s.background)?e.setAttribute("data-background-image",s.background):t.style.background=s.background),(s.background||s.backgroundColor||s.backgroundGradient||s.backgroundImage||s.backgroundVideo||s.backgroundIframe)&&t.setAttribute("data-background-hash",s.background+s.backgroundSize+s.backgroundImage+s.backgroundVideo+s.backgroundIframe+s.backgroundColor+s.backgroundGradient+s.backgroundRepeat+s.backgroundPosition+s.backgroundTransition+s.backgroundOpacity),s.backgroundSize&&t.setAttribute("data-background-size",s.backgroundSize),s.backgroundColor&&(t.style.backgroundColor=s.backgroundColor),s.backgroundGradient&&(t.style.backgroundImage=s.backgroundGradient),s.backgroundTransition&&t.setAttribute("data-background-transition",s.backgroundTransition),a&&t.setAttribute("data-preload",""),s.backgroundSize&&(i.style.backgroundSize=s.backgroundSize),s.backgroundRepeat&&(i.style.backgroundRepeat=s.backgroundRepeat),s.backgroundPosition&&(i.style.backgroundPosition=s.backgroundPosition),s.backgroundOpacity&&(i.style.opacity=s.backgroundOpacity);const n=this.getContrastClass(e);"string"==typeof n&&e.classList.add(n)}getContrastClass(e){const t=e.slideBackgroundElement;let i=e.getAttribute("data-background-color");if(!i||!A(i)){let e=window.getComputedStyle(t);e&&e.backgroundColor&&(i=e.backgroundColor)}if(i){const e=A(i);if(e&&0!==e.a)return"string"==typeof(s=i)&&(s=A(s)),(s?(299*s.r+587*s.g+114*s.b)/1e3:null)<128?"has-dark-background":"has-light-background"}var s;return null}bubbleSlideContrastClassToElement(e,t){["has-light-background","has-dark-background"].forEach((i=>{e.classList.contains(i)?t.classList.add(i):t.classList.remove(i)}),this)}update(e=!1){let i=this.Reveal.getConfig(),s=this.Reveal.getCurrentSlide(),a=this.Reveal.getIndices(),n=null,r=i.rtl?"future":"past",o=i.rtl?"past":"future";if(Array.from(this.element.childNodes).forEach(((i,s)=>{i.classList.remove("past","present","future"),sa.h?i.classList.add(o):(i.classList.add("present"),n=i),(e||s===a.h)&&t(i,".slide-background").forEach(((e,t)=>{e.classList.remove("past","present","future");const i="number"==typeof a.v?a.v:0;ti?e.classList.add("future"):(e.classList.add("present"),s===a.h&&(n=e))}))})),this.previousBackground&&!this.previousBackground.closest("body")&&(this.previousBackground=null),n&&this.previousBackground){let e=this.previousBackground.getAttribute("data-background-hash"),t=n.getAttribute("data-background-hash");if(t&&t===e&&n!==this.previousBackground){this.element.classList.add("no-transition");const e=n.querySelector("video"),t=this.previousBackground.querySelector("video");if(e&&t){const i=e.parentNode;t.parentNode.appendChild(e),i.appendChild(t)}}}if(this.previousBackground&&this.Reveal.slideContent.stopEmbeddedContent(this.previousBackground,{unloadIframes:!this.Reveal.slideContent.shouldPreload(this.previousBackground)}),n){this.Reveal.slideContent.startEmbeddedContent(n);let e=n.querySelector(".slide-background-content");if(e){let t=e.style.backgroundImage||"";/\.gif/i.test(t)&&(e.style.backgroundImage="",window.getComputedStyle(e).opacity,e.style.backgroundImage=t)}this.previousBackground=n}s&&this.bubbleSlideContrastClassToElement(s,this.Reveal.getRevealElement()),setTimeout((()=>{this.element.classList.remove("no-transition")}),10)}updateParallax(){let e=this.Reveal.getIndices();if(this.Reveal.getConfig().parallaxBackgroundImage){let t,i,s=this.Reveal.getHorizontalSlides(),a=this.Reveal.getVerticalSlides(),n=this.element.style.backgroundSize.split(" ");1===n.length?t=i=parseInt(n[0],10):(t=parseInt(n[0],10),i=parseInt(n[1],10));let r,o,l=this.element.offsetWidth,d=s.length;r="number"==typeof this.Reveal.getConfig().parallaxBackgroundHorizontal?this.Reveal.getConfig().parallaxBackgroundHorizontal:d>1?(t-l)/(d-1):0,o=r*e.h*-1;let c,h,u=this.element.offsetHeight,g=a.length;c="number"==typeof this.Reveal.getConfig().parallaxBackgroundVertical?this.Reveal.getConfig().parallaxBackgroundVertical:(i-u)/(g-1),h=g>0?c*e.v:0,this.element.style.backgroundPosition=o+"px "+-h+"px"}}destroy(){this.element.remove()}}let k=0;class L{constructor(e){this.Reveal=e}run(e,t){this.reset();let i=this.Reveal.getSlides(),s=i.indexOf(t),a=i.indexOf(e);if(e&&t&&e.hasAttribute("data-auto-animate")&&t.hasAttribute("data-auto-animate")&&e.getAttribute("data-auto-animate-id")===t.getAttribute("data-auto-animate-id")&&!(s>a?t:e).hasAttribute("data-auto-animate-restart")){this.autoAnimateStyleSheet=this.autoAnimateStyleSheet||l();let i=this.getAutoAnimateOptions(t);e.dataset.autoAnimate="pending",t.dataset.autoAnimate="pending",i.slideDirection=s>a?"forward":"backward";let n="none"===e.style.display;n&&(e.style.display=this.Reveal.getConfig().display);let r=this.getAutoAnimatableElements(e,t).map((e=>this.autoAnimateElements(e.from,e.to,e.options||{},i,k++)));if(n&&(e.style.display="none"),"false"!==t.dataset.autoAnimateUnmatched&&!0===this.Reveal.getConfig().autoAnimateUnmatched){let e=.8*i.duration,s=.2*i.duration;this.getUnmatchedAutoAnimateElements(t).forEach((e=>{let t=this.getAutoAnimateOptions(e,i),s="unmatched";t.duration===i.duration&&t.delay===i.delay||(s="unmatched-"+k++,r.push(`[data-auto-animate="running"] [data-auto-animate-target="${s}"] { transition: opacity ${t.duration}s ease ${t.delay}s; }`)),e.dataset.autoAnimateTarget=s}),this),r.push(`[data-auto-animate="running"] [data-auto-animate-target="unmatched"] { transition: opacity ${e}s ease ${s}s; }`)}this.autoAnimateStyleSheet.innerHTML=r.join(""),requestAnimationFrame((()=>{this.autoAnimateStyleSheet&&(getComputedStyle(this.autoAnimateStyleSheet).fontWeight,t.dataset.autoAnimate="running")})),this.Reveal.dispatchEvent({type:"autoanimate",data:{fromSlide:e,toSlide:t,sheet:this.autoAnimateStyleSheet}})}}reset(){t(this.Reveal.getRevealElement(),'[data-auto-animate]:not([data-auto-animate=""])').forEach((e=>{e.dataset.autoAnimate=""})),t(this.Reveal.getRevealElement(),"[data-auto-animate-target]").forEach((e=>{delete e.dataset.autoAnimateTarget})),this.autoAnimateStyleSheet&&this.autoAnimateStyleSheet.parentNode&&(this.autoAnimateStyleSheet.parentNode.removeChild(this.autoAnimateStyleSheet),this.autoAnimateStyleSheet=null)}autoAnimateElements(e,t,i,s,a){e.dataset.autoAnimateTarget="",t.dataset.autoAnimateTarget=a;let n=this.getAutoAnimateOptions(t,s);void 0!==i.delay&&(n.delay=i.delay),void 0!==i.duration&&(n.duration=i.duration),void 0!==i.easing&&(n.easing=i.easing);let r=this.getAutoAnimatableProperties("from",e,i),o=this.getAutoAnimatableProperties("to",t,i);if(t.classList.contains("fragment")&&(delete o.styles.opacity,e.classList.contains("fragment"))){(e.className.match(w)||[""])[0]===(t.className.match(w)||[""])[0]&&"forward"===s.slideDirection&&t.classList.add("visible","disabled")}if(!1!==i.translate||!1!==i.scale){let e=this.Reveal.getScale(),t={x:(r.x-o.x)/e,y:(r.y-o.y)/e,scaleX:r.width/o.width,scaleY:r.height/o.height};t.x=Math.round(1e3*t.x)/1e3,t.y=Math.round(1e3*t.y)/1e3,t.scaleX=Math.round(1e3*t.scaleX)/1e3,t.scaleX=Math.round(1e3*t.scaleX)/1e3;let s=!1!==i.translate&&(0!==t.x||0!==t.y),a=!1!==i.scale&&(0!==t.scaleX||0!==t.scaleY);if(s||a){let e=[];s&&e.push(`translate(${t.x}px, ${t.y}px)`),a&&e.push(`scale(${t.scaleX}, ${t.scaleY})`),r.styles.transform=e.join(" "),r.styles["transform-origin"]="top left",o.styles.transform="none"}}for(let e in o.styles){const t=o.styles[e],i=r.styles[e];t===i?delete o.styles[e]:(!0===t.explicitValue&&(o.styles[e]=t.value),!0===i.explicitValue&&(r.styles[e]=i.value))}let l="",d=Object.keys(o.styles);if(d.length>0){r.styles.transition="none",o.styles.transition=`all ${n.duration}s ${n.easing} ${n.delay}s`,o.styles["transition-property"]=d.join(", "),o.styles["will-change"]=d.join(", "),l='[data-auto-animate-target="'+a+'"] {'+Object.keys(r.styles).map((e=>e+": "+r.styles[e]+" !important;")).join("")+'}[data-auto-animate="running"] [data-auto-animate-target="'+a+'"] {'+Object.keys(o.styles).map((e=>e+": "+o.styles[e]+" !important;")).join("")+"}"}return l}getAutoAnimateOptions(t,i){let s={easing:this.Reveal.getConfig().autoAnimateEasing,duration:this.Reveal.getConfig().autoAnimateDuration,delay:0};if(s=e(s,i),t.parentNode){let e=r(t.parentNode,"[data-auto-animate-target]");e&&(s=this.getAutoAnimateOptions(e,s))}return t.dataset.autoAnimateEasing&&(s.easing=t.dataset.autoAnimateEasing),t.dataset.autoAnimateDuration&&(s.duration=parseFloat(t.dataset.autoAnimateDuration)),t.dataset.autoAnimateDelay&&(s.delay=parseFloat(t.dataset.autoAnimateDelay)),s}getAutoAnimatableProperties(e,t,i){let s=this.Reveal.getConfig(),a={styles:[]};if(!1!==i.translate||!1!==i.scale){let e;if("function"==typeof i.measure)e=i.measure(t);else if(s.center)e=t.getBoundingClientRect();else{let i=this.Reveal.getScale();e={x:t.offsetLeft*i,y:t.offsetTop*i,width:t.offsetWidth*i,height:t.offsetHeight*i}}a.x=e.x,a.y=e.y,a.width=e.width,a.height=e.height}const n=getComputedStyle(t);return(i.styles||s.autoAnimateStyles).forEach((t=>{let i;"string"==typeof t&&(t={property:t}),void 0!==t.from&&"from"===e?i={value:t.from,explicitValue:!0}:void 0!==t.to&&"to"===e?i={value:t.to,explicitValue:!0}:("line-height"===t.property&&(i=parseFloat(n["line-height"])/parseFloat(n["font-size"])),isNaN(i)&&(i=n[t.property])),""!==i&&(a.styles[t.property]=i)})),a}getAutoAnimatableElements(e,t){let i=("function"==typeof this.Reveal.getConfig().autoAnimateMatcher?this.Reveal.getConfig().autoAnimateMatcher:this.getAutoAnimatePairs).call(this,e,t),s=[];return i.filter(((e,t)=>{if(-1===s.indexOf(e.to))return s.push(e.to),!0}))}getAutoAnimatePairs(e,t){let i=[];const s="h1, h2, h3, h4, h5, h6, p, li";return this.findAutoAnimateMatches(i,e,t,"[data-id]",(e=>e.nodeName+":::"+e.getAttribute("data-id"))),this.findAutoAnimateMatches(i,e,t,s,(e=>e.nodeName+":::"+e.innerText)),this.findAutoAnimateMatches(i,e,t,"img, video, iframe",(e=>e.nodeName+":::"+(e.getAttribute("src")||e.getAttribute("data-src")))),this.findAutoAnimateMatches(i,e,t,"pre",(e=>e.nodeName+":::"+e.innerText)),i.forEach((e=>{n(e.from,s)?e.options={scale:!1}:n(e.from,"pre")&&(e.options={scale:!1,styles:["width","height"]},this.findAutoAnimateMatches(i,e.from,e.to,".hljs .hljs-ln-code",(e=>e.textContent),{scale:!1,styles:[],measure:this.getLocalBoundingBox.bind(this)}),this.findAutoAnimateMatches(i,e.from,e.to,".hljs .hljs-ln-numbers[data-line-number]",(e=>e.getAttribute("data-line-number")),{scale:!1,styles:["width"],measure:this.getLocalBoundingBox.bind(this)}))}),this),i}getLocalBoundingBox(e){const t=this.Reveal.getScale();return{x:Math.round(e.offsetLeft*t*100)/100,y:Math.round(e.offsetTop*t*100)/100,width:Math.round(e.offsetWidth*t*100)/100,height:Math.round(e.offsetHeight*t*100)/100}}findAutoAnimateMatches(e,t,i,s,a,n){let r={},o={};[].slice.call(t.querySelectorAll(s)).forEach(((e,t)=>{const i=a(e);"string"==typeof i&&i.length&&(r[i]=r[i]||[],r[i].push(e))})),[].slice.call(i.querySelectorAll(s)).forEach(((t,i)=>{const s=a(t);let l;if(o[s]=o[s]||[],o[s].push(t),r[s]){const e=o[s].length-1,t=r[s].length-1;r[s][e]?(l=r[s][e],r[s][e]=null):r[s][t]&&(l=r[s][t],r[s][t]=null)}l&&e.push({from:l,to:t,options:n})}))}getUnmatchedAutoAnimateElements(e){return[].slice.call(e.children).reduce(((e,t)=>{const i=t.querySelector("[data-auto-animate-target]");return t.hasAttribute("data-auto-animate-target")||i||e.push(t),t.querySelector("[data-auto-animate-target]")&&(e=e.concat(this.getUnmatchedAutoAnimateElements(t))),e}),[])}}class C{constructor(e){this.Reveal=e,this.active=!1,this.activatedCallbacks=[],this.onScroll=this.onScroll.bind(this)}activate(){if(this.active)return;const e=this.Reveal.getState();this.active=!0,this.slideHTMLBeforeActivation=this.Reveal.getSlidesElement().innerHTML;const i=t(this.Reveal.getRevealElement(),f),s=t(this.Reveal.getRevealElement(),".backgrounds>.slide-background");let a;this.viewportElement.classList.add("loading-scroll-mode","reveal-scroll");const n=window.getComputedStyle(this.viewportElement);n&&n.background&&(a=n.background);const r=[],o=i[0].parentNode;let l;const d=(e,t,i,n)=>{let o;if(l&&this.Reveal.shouldAutoAnimateBetween(l,e))o=document.createElement("div"),o.className="scroll-page-content scroll-auto-animate-page",o.style.display="none",l.closest(".scroll-page-content").parentNode.appendChild(o);else{const e=document.createElement("div");if(e.className="scroll-page",r.push(e),n&&s.length>t){const i=s[t],n=window.getComputedStyle(i);n&&n.background?e.style.background=n.background:a&&(e.style.background=a)}else a&&(e.style.background=a);const i=document.createElement("div");i.className="scroll-page-sticky",e.appendChild(i),o=document.createElement("div"),o.className="scroll-page-content",i.appendChild(o)}o.appendChild(e),e.classList.remove("past","future"),e.setAttribute("data-index-h",t),e.setAttribute("data-index-v",i),e.slideBackgroundElement&&(e.slideBackgroundElement.remove("past","future"),o.insertBefore(e.slideBackgroundElement,e)),l=e};i.forEach(((e,t)=>{this.Reveal.isVerticalStack(e)?e.querySelectorAll("section").forEach(((e,i)=>{d(e,t,i,!0)})):d(e,t,0)}),this),this.createProgressBar(),t(this.Reveal.getRevealElement(),".stack").forEach((e=>e.remove())),r.forEach((e=>o.appendChild(e))),this.Reveal.slideContent.layout(this.Reveal.getSlidesElement()),this.Reveal.layout(),this.Reveal.setState(e),this.activatedCallbacks.forEach((e=>e())),this.activatedCallbacks=[],this.restoreScrollPosition(),this.viewportElement.classList.remove("loading-scroll-mode"),this.viewportElement.addEventListener("scroll",this.onScroll,{passive:!0})}deactivate(){if(!this.active)return;const e=this.Reveal.getState();this.active=!1,this.viewportElement.removeEventListener("scroll",this.onScroll),this.viewportElement.classList.remove("reveal-scroll"),this.removeProgressBar(),this.Reveal.getSlidesElement().innerHTML=this.slideHTMLBeforeActivation,this.Reveal.sync(),this.Reveal.setState(e),this.slideHTMLBeforeActivation=null}toggle(e){"boolean"==typeof e?e?this.activate():this.deactivate():this.isActive()?this.deactivate():this.activate()}isActive(){return this.active}createProgressBar(){this.progressBar=document.createElement("div"),this.progressBar.className="scrollbar",this.progressBarInner=document.createElement("div"),this.progressBarInner.className="scrollbar-inner",this.progressBar.appendChild(this.progressBarInner),this.progressBarPlayhead=document.createElement("div"),this.progressBarPlayhead.className="scrollbar-playhead",this.progressBarInner.appendChild(this.progressBarPlayhead),this.viewportElement.insertBefore(this.progressBar,this.viewportElement.firstChild);const e=e=>{let t=(e.clientY-this.progressBarInner.getBoundingClientRect().top)/this.progressBarHeight;t=Math.max(Math.min(t,1),0),this.viewportElement.scrollTop=t*(this.viewportElement.scrollHeight-this.viewportElement.offsetHeight)},t=i=>{this.draggingProgressBar=!1,this.showProgressBar(),document.removeEventListener("mousemove",e),document.removeEventListener("mouseup",t)};this.progressBarInner.addEventListener("mousedown",(i=>{i.preventDefault(),this.draggingProgressBar=!0,document.addEventListener("mousemove",e),document.addEventListener("mouseup",t),e(i)}))}removeProgressBar(){this.progressBar&&(this.progressBar.remove(),this.progressBar=null)}layout(){this.isActive()&&(this.syncPages(),this.syncScrollPosition())}syncPages(){const e=this.Reveal.getConfig(),t=this.Reveal.getComputedSlideSize(window.innerWidth,window.innerHeight),i=this.Reveal.getScale(),s="compact"===e.scrollLayout,a=this.viewportElement.offsetHeight,n=t.height*i,r=s?n:a;this.scrollTriggerHeight=s?n:a,this.viewportElement.style.setProperty("--page-height",r+"px"),this.viewportElement.style.scrollSnapType="string"==typeof e.scrollSnap?`y ${e.scrollSnap}`:"",this.slideTriggers=[];const o=Array.from(this.Reveal.getRevealElement().querySelectorAll(".scroll-page"));this.pages=o.map((i=>{const n=this.createPage({pageElement:i,slideElement:i.querySelector("section"),stickyElement:i.querySelector(".scroll-page-sticky"),contentElement:i.querySelector(".scroll-page-content"),backgroundElement:i.querySelector(".slide-background"),autoAnimateElements:i.querySelectorAll(".scroll-auto-animate-page"),autoAnimatePages:[]});n.pageElement.style.setProperty("--slide-height",!0===e.center?"auto":t.height+"px"),this.slideTriggers.push({page:n,activate:()=>this.activatePage(n),deactivate:()=>this.deactivatePage(n)}),this.createFragmentTriggersForPage(n),n.autoAnimateElements.length>0&&this.createAutoAnimateTriggersForPage(n);let o=Math.max(n.scrollTriggers.length-1,0);o+=n.autoAnimatePages.reduce(((e,t)=>e+Math.max(t.scrollTriggers.length-1,0)),n.autoAnimatePages.length),n.pageElement.querySelectorAll(".scroll-snap-point").forEach((e=>e.remove()));for(let e=0;e0?(n.pageHeight=a,n.pageElement.style.setProperty("--page-height",a+"px")):(n.pageHeight=r,n.pageElement.style.removeProperty("--page-height")),n.scrollPadding=this.scrollTriggerHeight*o,n.totalHeight=n.pageHeight+n.scrollPadding,n.pageElement.style.setProperty("--page-scroll-padding",n.scrollPadding+"px"),o>0?(n.stickyElement.style.position="sticky",n.stickyElement.style.top=Math.max((a-n.pageHeight)/2,0)+"px"):(n.stickyElement.style.position="relative",n.pageElement.style.scrollSnapAlign=n.pageHeight1?(this.progressBar||this.createProgressBar(),this.syncProgressBar()):this.removeProgressBar()}setTriggerRanges(){this.totalScrollTriggerCount=this.slideTriggers.reduce(((e,t)=>e+Math.max(t.page.scrollTriggers.length,1)),0);let e=0;this.slideTriggers.forEach(((t,i)=>{t.range=[e,e+Math.max(t.page.scrollTriggers.length,1)/this.totalScrollTriggerCount];const s=(t.range[1]-t.range[0])/t.page.scrollTriggers.length;t.page.scrollTriggers.forEach(((t,i)=>{t.range=[e+i*s,e+(i+1)*s]})),e=t.range[1]}))}createFragmentTriggersForPage(e,t){t=t||e.slideElement;const i=this.Reveal.fragments.sort(t.querySelectorAll(".fragment"),!0);return i.length&&(e.fragments=this.Reveal.fragments.sort(t.querySelectorAll(".fragment:not(.disabled)")),e.scrollTriggers.push({activate:()=>{this.Reveal.fragments.update(-1,e.fragments,t)}}),i.forEach(((i,s)=>{e.scrollTriggers.push({activate:()=>{this.Reveal.fragments.update(s,e.fragments,t)}})}))),e.scrollTriggers.length}createAutoAnimateTriggersForPage(e){e.autoAnimateElements.length>0&&this.slideTriggers.push(...Array.from(e.autoAnimateElements).map(((t,i)=>{let s=this.createPage({slideElement:t.querySelector("section"),contentElement:t,backgroundElement:t.querySelector(".slide-background")});return this.createFragmentTriggersForPage(s,s.slideElement),e.autoAnimatePages.push(s),{page:s,activate:()=>this.activatePage(s),deactivate:()=>this.deactivatePage(s)}})))}createPage(e){return e.scrollTriggers=[],e.indexh=parseInt(e.slideElement.getAttribute("data-index-h"),10),e.indexv=parseInt(e.slideElement.getAttribute("data-index-v"),10),e}syncProgressBar(){this.progressBarInner.querySelectorAll(".scrollbar-slide").forEach((e=>e.remove()));const e=this.viewportElement.scrollHeight,t=this.viewportElement.offsetHeight,i=t/e;this.progressBarHeight=this.progressBarInner.offsetHeight,this.playheadHeight=Math.max(i*this.progressBarHeight,8),this.progressBarScrollableHeight=this.progressBarHeight-this.playheadHeight;const s=t/e*this.progressBarHeight,a=Math.min(s/8,4);this.progressBarPlayhead.style.height=this.playheadHeight-a+"px",s>6?this.slideTriggers.forEach((e=>{const{page:t}=e;t.progressBarSlide=document.createElement("div"),t.progressBarSlide.className="scrollbar-slide",t.progressBarSlide.style.top=e.range[0]*this.progressBarHeight+"px",t.progressBarSlide.style.height=(e.range[1]-e.range[0])*this.progressBarHeight-a+"px",t.progressBarSlide.classList.toggle("has-triggers",t.scrollTriggers.length>0),this.progressBarInner.appendChild(t.progressBarSlide),t.scrollTriggerElements=t.scrollTriggers.map(((i,s)=>{const n=document.createElement("div");return n.className="scrollbar-trigger",n.style.top=(i.range[0]-e.range[0])*this.progressBarHeight+"px",n.style.height=(i.range[1]-i.range[0])*this.progressBarHeight-a+"px",t.progressBarSlide.appendChild(n),0===s&&(n.style.display="none"),n}))})):this.pages.forEach((e=>e.progressBarSlide=null))}syncScrollPosition(){const e=this.viewportElement.offsetHeight,t=e/this.viewportElement.scrollHeight,i=this.viewportElement.scrollTop,s=this.viewportElement.scrollHeight-e,a=Math.max(Math.min(i/s,1),0),n=Math.max(Math.min((i+e/2)/this.viewportElement.scrollHeight,1),0);let r;this.slideTriggers.forEach((e=>{const{page:i}=e;a>=e.range[0]-2*t&&a<=e.range[1]+2*t&&!i.loaded?(i.loaded=!0,this.Reveal.slideContent.load(i.slideElement)):i.loaded&&(i.loaded=!1,this.Reveal.slideContent.unload(i.slideElement)),a>=e.range[0]&&a<=e.range[1]?(this.activateTrigger(e),r=e.page):e.active&&this.deactivateTrigger(e)})),r&&r.scrollTriggers.forEach((e=>{n>=e.range[0]&&n<=e.range[1]?this.activateTrigger(e):e.active&&this.deactivateTrigger(e)})),this.setProgressBarValue(i/(this.viewportElement.scrollHeight-e))}setProgressBarValue(e){this.progressBar&&(this.progressBarPlayhead.style.transform=`translateY(${e*this.progressBarScrollableHeight}px)`,this.getAllPages().filter((e=>e.progressBarSlide)).forEach((e=>{e.progressBarSlide.classList.toggle("active",!0===e.active),e.scrollTriggers.forEach(((t,i)=>{e.scrollTriggerElements[i].classList.toggle("active",!0===e.active&&!0===t.active)}))})),this.showProgressBar())}showProgressBar(){this.progressBar.classList.add("visible"),clearTimeout(this.hideProgressBarTimeout),"auto"!==this.Reveal.getConfig().scrollProgress||this.draggingProgressBar||(this.hideProgressBarTimeout=setTimeout((()=>{this.progressBar&&this.progressBar.classList.remove("visible")}),500))}prev(){this.viewportElement.scrollTop-=this.scrollTriggerHeight}next(){this.viewportElement.scrollTop+=this.scrollTriggerHeight}scrollToSlide(e){if(this.active){const t=this.getScrollTriggerBySlide(e);t&&(this.viewportElement.scrollTop=t.range[0]*(this.viewportElement.scrollHeight-this.viewportElement.offsetHeight))}else this.activatedCallbacks.push((()=>this.scrollToSlide(e)))}storeScrollPosition(){clearTimeout(this.storeScrollPositionTimeout),this.storeScrollPositionTimeout=setTimeout((()=>{sessionStorage.setItem("reveal-scroll-top",this.viewportElement.scrollTop),sessionStorage.setItem("reveal-scroll-origin",location.origin+location.pathname),this.storeScrollPositionTimeout=null}),50)}restoreScrollPosition(){const e=sessionStorage.getItem("reveal-scroll-top"),t=sessionStorage.getItem("reveal-scroll-origin");e&&t===location.origin+location.pathname&&(this.viewportElement.scrollTop=parseInt(e,10))}activatePage(e){if(!e.active){e.active=!0;const{slideElement:t,backgroundElement:i,contentElement:s,indexh:a,indexv:n}=e;s.style.display="block",t.classList.add("present"),i&&i.classList.add("present"),this.Reveal.setCurrentScrollPage(t,a,n),this.Reveal.backgrounds.bubbleSlideContrastClassToElement(t,this.viewportElement),Array.from(s.parentNode.querySelectorAll(".scroll-page-content")).forEach((e=>{e!==s&&(e.style.display="none")}))}}deactivatePage(e){e.active&&(e.active=!1,e.slideElement&&e.slideElement.classList.remove("present"),e.backgroundElement&&e.backgroundElement.classList.remove("present"))}activateTrigger(e){e.active||(e.active=!0,e.activate())}deactivateTrigger(e){e.active&&(e.active=!1,e.deactivate&&e.deactivate())}getSlideByIndices(e,t){const i=this.getAllPages().find((i=>i.indexh===e&&i.indexv===t));return i?i.slideElement:null}getScrollTriggerBySlide(e){return this.slideTriggers.find((t=>t.page.slideElement===e))}getAllPages(){return this.pages.flatMap((e=>[e,...e.autoAnimatePages||[]]))}onScroll(){this.syncScrollPosition(),this.storeScrollPosition()}get viewportElement(){return this.Reveal.getViewportElement()}}class x{constructor(e){this.Reveal=e}async activate(){const e=this.Reveal.getConfig(),i=t(this.Reveal.getRevealElement(),m),s=e.slideNumber&&/all|print/i.test(e.showSlideNumber),a=this.Reveal.getComputedSlideSize(window.innerWidth,window.innerHeight),n=Math.floor(a.width*(1+e.margin)),r=Math.floor(a.height*(1+e.margin)),o=a.width,d=a.height;await new Promise(requestAnimationFrame),l("@page{size:"+n+"px "+r+"px; margin: 0px;}"),l(".reveal section>img, .reveal section>video, .reveal section>iframe{max-width: "+o+"px; max-height:"+d+"px}"),document.documentElement.classList.add("reveal-print","print-pdf"),document.body.style.width=n+"px",document.body.style.height=r+"px";const c=this.Reveal.getViewportElement();let h;if(c){const e=window.getComputedStyle(c);e&&e.background&&(h=e.background)}await new Promise(requestAnimationFrame),this.Reveal.layoutSlideContents(o,d),await new Promise(requestAnimationFrame);const u=i.map((e=>e.scrollHeight)),g=[],p=i[0].parentNode;let v=1;i.forEach((function(i,a){if(!1===i.classList.contains("stack")){let l=(n-o)/2,c=(r-d)/2;const p=u[a];let m=Math.max(Math.ceil(p/r),1);m=Math.min(m,e.pdfMaxPagesPerSlide),(1===m&&e.center||i.classList.contains("center"))&&(c=Math.max((r-p)/2,0));const f=document.createElement("div");if(g.push(f),f.className="pdf-page",f.style.height=(r+e.pdfPageHeightOffset)*m+"px",h&&(f.style.background=h),f.appendChild(i),i.style.left=l+"px",i.style.top=c+"px",i.style.width=o+"px",this.Reveal.slideContent.layout(i),i.slideBackgroundElement&&f.insertBefore(i.slideBackgroundElement,i),e.showNotes){const t=this.Reveal.getSlideNotes(i);if(t){const i=8,s="string"==typeof e.showNotes?e.showNotes:"inline",a=document.createElement("div");a.classList.add("speaker-notes"),a.classList.add("speaker-notes-pdf"),a.setAttribute("data-layout",s),a.innerHTML=t,"separate-page"===s?g.push(a):(a.style.left=i+"px",a.style.bottom=i+"px",a.style.width=n-2*i+"px",f.appendChild(a))}}if(s){const e=document.createElement("div");e.classList.add("slide-number"),e.classList.add("slide-number-pdf"),e.innerHTML=v++,f.appendChild(e)}if(e.pdfSeparateFragments){const e=this.Reveal.fragments.sort(f.querySelectorAll(".fragment"),!0);let t;e.forEach((function(e,i){t&&t.forEach((function(e){e.classList.remove("current-fragment")})),e.forEach((function(e){e.classList.add("visible","current-fragment")}),this);const a=f.cloneNode(!0);if(s){const e=i+1;a.querySelector(".slide-number-pdf").innerHTML+="."+e}g.push(a),t=e}),this),e.forEach((function(e){e.forEach((function(e){e.classList.remove("visible","current-fragment")}))}))}else t(f,".fragment:not(.fade-out)").forEach((function(e){e.classList.add("visible")}))}}),this),await new Promise(requestAnimationFrame),g.forEach((e=>p.appendChild(e))),this.Reveal.slideContent.layout(this.Reveal.getSlidesElement()),this.Reveal.dispatchEvent({type:"pdf-ready"}),c.classList.remove("loading-scroll-mode")}isActive(){return"print"===this.Reveal.getConfig().view}}class P{constructor(e){this.Reveal=e}configure(e,t){!1===e.fragments?this.disable():!1===t.fragments&&this.enable()}disable(){t(this.Reveal.getSlidesElement(),".fragment").forEach((e=>{e.classList.add("visible"),e.classList.remove("current-fragment")}))}enable(){t(this.Reveal.getSlidesElement(),".fragment").forEach((e=>{e.classList.remove("visible"),e.classList.remove("current-fragment")}))}availableRoutes(){let e=this.Reveal.getCurrentSlide();if(e&&this.Reveal.getConfig().fragments){let t=e.querySelectorAll(".fragment:not(.disabled)"),i=e.querySelectorAll(".fragment:not(.disabled):not(.visible)");return{prev:t.length-i.length>0,next:!!i.length}}return{prev:!1,next:!1}}sort(e,t=!1){e=Array.from(e);let i=[],s=[],a=[];e.forEach((e=>{if(e.hasAttribute("data-fragment-index")){let t=parseInt(e.getAttribute("data-fragment-index"),10);i[t]||(i[t]=[]),i[t].push(e)}else s.push([e])})),i=i.concat(s);let n=0;return i.forEach((e=>{e.forEach((e=>{a.push(e),e.setAttribute("data-fragment-index",n)})),n++})),!0===t?i:a}sortAll(){this.Reveal.getHorizontalSlides().forEach((e=>{let i=t(e,"section");i.forEach(((e,t)=>{this.sort(e.querySelectorAll(".fragment"))}),this),0===i.length&&this.sort(e.querySelectorAll(".fragment"))}))}update(e,t,i=this.Reveal.getCurrentSlide()){let s={shown:[],hidden:[]};if(i&&this.Reveal.getConfig().fragments&&(t=t||this.sort(i.querySelectorAll(".fragment"))).length){let a=0;if("number"!=typeof e){let t=this.sort(i.querySelectorAll(".fragment.visible")).pop();t&&(e=parseInt(t.getAttribute("data-fragment-index")||0,10))}Array.from(t).forEach(((t,i)=>{if(t.hasAttribute("data-fragment-index")&&(i=parseInt(t.getAttribute("data-fragment-index"),10)),a=Math.max(a,i),i<=e){let a=t.classList.contains("visible");t.classList.add("visible"),t.classList.remove("current-fragment"),i===e&&(this.Reveal.announceStatus(this.Reveal.getStatusText(t)),t.classList.add("current-fragment"),this.Reveal.slideContent.startEmbeddedContent(t)),a||(s.shown.push(t),this.Reveal.dispatchEvent({target:t,type:"visible",bubbles:!1}))}else{let e=t.classList.contains("visible");t.classList.remove("visible"),t.classList.remove("current-fragment"),e&&(this.Reveal.slideContent.stopEmbeddedContent(t),s.hidden.push(t),this.Reveal.dispatchEvent({target:t,type:"hidden",bubbles:!1}))}})),e="number"==typeof e?e:-1,e=Math.max(Math.min(e,a),-1),i.setAttribute("data-fragment",e)}return s.hidden.length&&this.Reveal.dispatchEvent({type:"fragmenthidden",data:{fragment:s.hidden[0],fragments:s.hidden}}),s.shown.length&&this.Reveal.dispatchEvent({type:"fragmentshown",data:{fragment:s.shown[0],fragments:s.shown}}),s}sync(e=this.Reveal.getCurrentSlide()){return this.sort(e.querySelectorAll(".fragment"))}goto(e,t=0){let i=this.Reveal.getCurrentSlide();if(i&&this.Reveal.getConfig().fragments){let s=this.sort(i.querySelectorAll(".fragment:not(.disabled)"));if(s.length){if("number"!=typeof e){let t=this.sort(i.querySelectorAll(".fragment:not(.disabled).visible")).pop();e=t?parseInt(t.getAttribute("data-fragment-index")||0,10):-1}e+=t;let a=this.update(e,s);return this.Reveal.controls.update(),this.Reveal.progress.update(),this.Reveal.getConfig().fragmentInURL&&this.Reveal.location.writeURL(),!(!a.shown.length&&!a.hidden.length)}}return!1}next(){return this.goto(null,1)}prev(){return this.goto(null,-1)}}class T{constructor(e){this.Reveal=e,this.active=!1,this.onSlideClicked=this.onSlideClicked.bind(this)}activate(){if(this.Reveal.getConfig().overview&&!this.Reveal.isScrollView()&&!this.isActive()){this.active=!0,this.Reveal.getRevealElement().classList.add("overview"),this.Reveal.cancelAutoSlide(),this.Reveal.getSlidesElement().appendChild(this.Reveal.getBackgroundsElement()),t(this.Reveal.getRevealElement(),m).forEach((e=>{e.classList.contains("stack")||e.addEventListener("click",this.onSlideClicked,!0)}));const e=70,i=this.Reveal.getComputedSlideSize();this.overviewSlideWidth=i.width+e,this.overviewSlideHeight=i.height+e,this.Reveal.getConfig().rtl&&(this.overviewSlideWidth=-this.overviewSlideWidth),this.Reveal.updateSlidesVisibility(),this.layout(),this.update(),this.Reveal.layout();const s=this.Reveal.getIndices();this.Reveal.dispatchEvent({type:"overviewshown",data:{indexh:s.h,indexv:s.v,currentSlide:this.Reveal.getCurrentSlide()}})}}layout(){this.Reveal.getHorizontalSlides().forEach(((e,i)=>{e.setAttribute("data-index-h",i),a(e,"translate3d("+i*this.overviewSlideWidth+"px, 0, 0)"),e.classList.contains("stack")&&t(e,"section").forEach(((e,t)=>{e.setAttribute("data-index-h",i),e.setAttribute("data-index-v",t),a(e,"translate3d(0, "+t*this.overviewSlideHeight+"px, 0)")}))})),Array.from(this.Reveal.getBackgroundsElement().childNodes).forEach(((e,i)=>{a(e,"translate3d("+i*this.overviewSlideWidth+"px, 0, 0)"),t(e,".slide-background").forEach(((e,t)=>{a(e,"translate3d(0, "+t*this.overviewSlideHeight+"px, 0)")}))}))}update(){const e=Math.min(window.innerWidth,window.innerHeight),t=Math.max(e/5,150)/e,i=this.Reveal.getIndices();this.Reveal.transformSlides({overview:["scale("+t+")","translateX("+-i.h*this.overviewSlideWidth+"px)","translateY("+-i.v*this.overviewSlideHeight+"px)"].join(" ")})}deactivate(){if(this.Reveal.getConfig().overview){this.active=!1,this.Reveal.getRevealElement().classList.remove("overview"),this.Reveal.getRevealElement().classList.add("overview-deactivating"),setTimeout((()=>{this.Reveal.getRevealElement().classList.remove("overview-deactivating")}),1),this.Reveal.getRevealElement().appendChild(this.Reveal.getBackgroundsElement()),t(this.Reveal.getRevealElement(),m).forEach((e=>{a(e,""),e.removeEventListener("click",this.onSlideClicked,!0)})),t(this.Reveal.getBackgroundsElement(),".slide-background").forEach((e=>{a(e,"")})),this.Reveal.transformSlides({overview:""});const e=this.Reveal.getIndices();this.Reveal.slide(e.h,e.v),this.Reveal.layout(),this.Reveal.cueAutoSlide(),this.Reveal.dispatchEvent({type:"overviewhidden",data:{indexh:e.h,indexv:e.v,currentSlide:this.Reveal.getCurrentSlide()}})}}toggle(e){"boolean"==typeof e?e?this.activate():this.deactivate():this.isActive()?this.deactivate():this.activate()}isActive(){return this.active}onSlideClicked(e){if(this.isActive()){e.preventDefault();let t=e.target;for(;t&&!t.nodeName.match(/section/gi);)t=t.parentNode;if(t&&!t.classList.contains("disabled")&&(this.deactivate(),t.nodeName.match(/section/gi))){let e=parseInt(t.getAttribute("data-index-h"),10),i=parseInt(t.getAttribute("data-index-v"),10);this.Reveal.slide(e,i)}}}}class N{constructor(e){this.Reveal=e,this.shortcuts={},this.bindings={},this.onDocumentKeyDown=this.onDocumentKeyDown.bind(this)}configure(e,t){"linear"===e.navigationMode?(this.shortcuts["→ , ↓ , SPACE , N , L , J"]="Next slide",this.shortcuts["← , ↑ , P , H , K"]="Previous slide"):(this.shortcuts["N , SPACE"]="Next slide",this.shortcuts["P , Shift SPACE"]="Previous slide",this.shortcuts["← , H"]="Navigate left",this.shortcuts["→ , L"]="Navigate right",this.shortcuts["↑ , K"]="Navigate up",this.shortcuts["↓ , J"]="Navigate down"),this.shortcuts["Alt + ←/↑/→/↓"]="Navigate without fragments",this.shortcuts["Shift + ←/↑/→/↓"]="Jump to first/last slide",this.shortcuts["B , ."]="Pause",this.shortcuts.F="Fullscreen",this.shortcuts.G="Jump to slide",this.shortcuts["ESC, O"]="Slide overview"}bind(){document.addEventListener("keydown",this.onDocumentKeyDown,!1)}unbind(){document.removeEventListener("keydown",this.onDocumentKeyDown,!1)}addKeyBinding(e,t){"object"==typeof e&&e.keyCode?this.bindings[e.keyCode]={callback:t,key:e.key,description:e.description}:this.bindings[e]={callback:t,key:null,description:null}}removeKeyBinding(e){delete this.bindings[e]}triggerKey(e){this.onDocumentKeyDown({keyCode:e})}registerKeyboardShortcut(e,t){this.shortcuts[e]=t}getShortcuts(){return this.shortcuts}getBindings(){return this.bindings}onDocumentKeyDown(e){let t=this.Reveal.getConfig();if("function"==typeof t.keyboardCondition&&!1===t.keyboardCondition(e))return!0;if("focused"===t.keyboardCondition&&!this.Reveal.isFocused())return!0;let i=e.keyCode,s=!this.Reveal.isAutoSliding();this.Reveal.onUserInput(e);let a=document.activeElement&&!0===document.activeElement.isContentEditable,n=document.activeElement&&document.activeElement.tagName&&/input|textarea/i.test(document.activeElement.tagName),r=document.activeElement&&document.activeElement.className&&/speaker-notes/i.test(document.activeElement.className),l=!(-1!==[32,37,38,39,40,63,78,80,191].indexOf(e.keyCode)&&e.shiftKey||e.altKey)&&(e.shiftKey||e.altKey||e.ctrlKey||e.metaKey);if(a||n||r||l)return;let d,c=[66,86,190,191,112];if("object"==typeof t.keyboard)for(d in t.keyboard)"togglePause"===t.keyboard[d]&&c.push(parseInt(d,10));if(this.Reveal.isPaused()&&-1===c.indexOf(i))return!1;let h="linear"===t.navigationMode||!this.Reveal.hasHorizontalSlides()||!this.Reveal.hasVerticalSlides(),u=!1;if("object"==typeof t.keyboard)for(d in t.keyboard)if(parseInt(d,10)===i){let i=t.keyboard[d];"function"==typeof i?i.apply(null,[e]):"string"==typeof i&&"function"==typeof this.Reveal[i]&&this.Reveal[i].call(),u=!0}if(!1===u)for(d in this.bindings)if(parseInt(d,10)===i){let t=this.bindings[d].callback;"function"==typeof t?t.apply(null,[e]):"string"==typeof t&&"function"==typeof this.Reveal[t]&&this.Reveal[t].call(),u=!0}!1===u&&(u=!0,80===i||33===i?this.Reveal.prev({skipFragments:e.altKey}):78===i||34===i?this.Reveal.next({skipFragments:e.altKey}):72===i||37===i?e.shiftKey?this.Reveal.slide(0):!this.Reveal.overview.isActive()&&h?t.rtl?this.Reveal.next({skipFragments:e.altKey}):this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.left({skipFragments:e.altKey}):76===i||39===i?e.shiftKey?this.Reveal.slide(this.Reveal.getHorizontalSlides().length-1):!this.Reveal.overview.isActive()&&h?t.rtl?this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.next({skipFragments:e.altKey}):this.Reveal.right({skipFragments:e.altKey}):75===i||38===i?e.shiftKey?this.Reveal.slide(void 0,0):!this.Reveal.overview.isActive()&&h?this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.up({skipFragments:e.altKey}):74===i||40===i?e.shiftKey?this.Reveal.slide(void 0,Number.MAX_VALUE):!this.Reveal.overview.isActive()&&h?this.Reveal.next({skipFragments:e.altKey}):this.Reveal.down({skipFragments:e.altKey}):36===i?this.Reveal.slide(0):35===i?this.Reveal.slide(this.Reveal.getHorizontalSlides().length-1):32===i?(this.Reveal.overview.isActive()&&this.Reveal.overview.deactivate(),e.shiftKey?this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.next({skipFragments:e.altKey})):[58,59,66,86,190].includes(i)||191===i&&!e.shiftKey?this.Reveal.togglePause():70===i?o(t.embedded?this.Reveal.getViewportElement():document.documentElement):65===i?t.autoSlideStoppable&&this.Reveal.toggleAutoSlide(s):71===i?t.jumpToSlide&&this.Reveal.toggleJumpToSlide():63!==i&&191!==i||!e.shiftKey?112===i?this.Reveal.toggleHelp():u=!1:this.Reveal.toggleHelp()),u?e.preventDefault&&e.preventDefault():27!==i&&79!==i||(!1===this.Reveal.closeOverlay()&&this.Reveal.overview.toggle(),e.preventDefault&&e.preventDefault()),this.Reveal.cueAutoSlide()}}class M{MAX_REPLACE_STATE_FREQUENCY=1e3;constructor(e){this.Reveal=e,this.writeURLTimeout=0,this.replaceStateTimestamp=0,this.onWindowHashChange=this.onWindowHashChange.bind(this)}bind(){window.addEventListener("hashchange",this.onWindowHashChange,!1)}unbind(){window.removeEventListener("hashchange",this.onWindowHashChange,!1)}getIndicesFromHash(e=window.location.hash,t={}){let i=e.replace(/^#\/?/,""),s=i.split("/");if(/^[0-9]*$/.test(s[0])||!i.length){const e=this.Reveal.getConfig();let i,a=e.hashOneBasedIndex||t.oneBasedIndex?1:0,n=parseInt(s[0],10)-a||0,r=parseInt(s[1],10)-a||0;return e.fragmentInURL&&(i=parseInt(s[2],10),isNaN(i)&&(i=void 0)),{h:n,v:r,f:i}}{let e,t;/\/[-\d]+$/g.test(i)&&(t=parseInt(i.split("/").pop(),10),t=isNaN(t)?void 0:t,i=i.split("/").shift());try{e=document.getElementById(decodeURIComponent(i)).closest(".slides section")}catch(e){}if(e)return{...this.Reveal.getIndices(e),f:t}}return null}readURL(){const e=this.Reveal.getIndices(),t=this.getIndicesFromHash();t?t.h===e.h&&t.v===e.v&&void 0===t.f||this.Reveal.slide(t.h,t.v,t.f):this.Reveal.slide(e.h||0,e.v||0)}writeURL(e){let t=this.Reveal.getConfig(),i=this.Reveal.getCurrentSlide();if(clearTimeout(this.writeURLTimeout),"number"==typeof e)this.writeURLTimeout=setTimeout(this.writeURL,e);else if(i){let e=this.getHash();t.history?window.location.hash=e:t.hash&&("/"===e?this.debouncedReplaceState(window.location.pathname+window.location.search):this.debouncedReplaceState("#"+e))}}replaceState(e){window.history.replaceState(null,null,e),this.replaceStateTimestamp=Date.now()}debouncedReplaceState(e){clearTimeout(this.replaceStateTimeout),Date.now()-this.replaceStateTimestamp>this.MAX_REPLACE_STATE_FREQUENCY?this.replaceState(e):this.replaceStateTimeout=setTimeout((()=>this.replaceState(e)),this.MAX_REPLACE_STATE_FREQUENCY)}getHash(e){let t="/",i=e||this.Reveal.getCurrentSlide(),s=i?i.getAttribute("id"):null;s&&(s=encodeURIComponent(s));let a=this.Reveal.getIndices(e);if(this.Reveal.getConfig().fragmentInURL||(a.f=void 0),"string"==typeof s&&s.length)t="/"+s,a.f>=0&&(t+="/"+a.f);else{let e=this.Reveal.getConfig().hashOneBasedIndex?1:0;(a.h>0||a.v>0||a.f>=0)&&(t+=a.h+e),(a.v>0||a.f>=0)&&(t+="/"+(a.v+e)),a.f>=0&&(t+="/"+a.f)}return t}onWindowHashChange(e){this.readURL()}}class I{constructor(e){this.Reveal=e,this.onNavigateLeftClicked=this.onNavigateLeftClicked.bind(this),this.onNavigateRightClicked=this.onNavigateRightClicked.bind(this),this.onNavigateUpClicked=this.onNavigateUpClicked.bind(this),this.onNavigateDownClicked=this.onNavigateDownClicked.bind(this),this.onNavigatePrevClicked=this.onNavigatePrevClicked.bind(this),this.onNavigateNextClicked=this.onNavigateNextClicked.bind(this),this.onEnterFullscreen=this.onEnterFullscreen.bind(this)}render(){const e=this.Reveal.getConfig().rtl,i=this.Reveal.getRevealElement();this.element=document.createElement("aside"),this.element.className="controls",this.element.innerHTML=`\n\t\t\t\n\t\t\t\n\t\t\t`,this.Reveal.getRevealElement().appendChild(this.element),this.controlsLeft=t(i,".navigate-left"),this.controlsRight=t(i,".navigate-right"),this.controlsUp=t(i,".navigate-up"),this.controlsDown=t(i,".navigate-down"),this.controlsPrev=t(i,".navigate-prev"),this.controlsNext=t(i,".navigate-next"),this.controlsFullscreen=t(i,".enter-fullscreen"),this.controlsRightArrow=this.element.querySelector(".navigate-right"),this.controlsLeftArrow=this.element.querySelector(".navigate-left"),this.controlsDownArrow=this.element.querySelector(".navigate-down")}configure(e,t){this.element.style.display=e.controls?"block":"none",this.element.setAttribute("data-controls-layout",e.controlsLayout),this.element.setAttribute("data-controls-back-arrows",e.controlsBackArrows)}bind(){let e=["touchstart","click"];g&&(e=["touchstart"]),e.forEach((e=>{this.controlsLeft.forEach((t=>t.addEventListener(e,this.onNavigateLeftClicked,!1))),this.controlsRight.forEach((t=>t.addEventListener(e,this.onNavigateRightClicked,!1))),this.controlsUp.forEach((t=>t.addEventListener(e,this.onNavigateUpClicked,!1))),this.controlsDown.forEach((t=>t.addEventListener(e,this.onNavigateDownClicked,!1))),this.controlsPrev.forEach((t=>t.addEventListener(e,this.onNavigatePrevClicked,!1))),this.controlsNext.forEach((t=>t.addEventListener(e,this.onNavigateNextClicked,!1))),this.controlsFullscreen.forEach((t=>t.addEventListener(e,this.onEnterFullscreen,!1)))}))}unbind(){["touchstart","click"].forEach((e=>{this.controlsLeft.forEach((t=>t.removeEventListener(e,this.onNavigateLeftClicked,!1))),this.controlsRight.forEach((t=>t.removeEventListener(e,this.onNavigateRightClicked,!1))),this.controlsUp.forEach((t=>t.removeEventListener(e,this.onNavigateUpClicked,!1))),this.controlsDown.forEach((t=>t.removeEventListener(e,this.onNavigateDownClicked,!1))),this.controlsPrev.forEach((t=>t.removeEventListener(e,this.onNavigatePrevClicked,!1))),this.controlsNext.forEach((t=>t.removeEventListener(e,this.onNavigateNextClicked,!1))),this.controlsFullscreen.forEach((t=>t.removeEventListener(e,this.onEnterFullscreen,!1)))}))}update(){let e=this.Reveal.availableRoutes();[...this.controlsLeft,...this.controlsRight,...this.controlsUp,...this.controlsDown,...this.controlsPrev,...this.controlsNext].forEach((e=>{e.classList.remove("enabled","fragmented"),e.setAttribute("disabled","disabled")})),e.left&&this.controlsLeft.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),e.right&&this.controlsRight.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),e.up&&this.controlsUp.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),e.down&&this.controlsDown.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),(e.left||e.up)&&this.controlsPrev.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),(e.right||e.down)&&this.controlsNext.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")}));let t=this.Reveal.getCurrentSlide();if(t){let e=this.Reveal.fragments.availableRoutes();e.prev&&this.controlsPrev.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),e.next&&this.controlsNext.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),this.Reveal.isVerticalSlide(t)?(e.prev&&this.controlsUp.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),e.next&&this.controlsDown.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")}))):(e.prev&&this.controlsLeft.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),e.next&&this.controlsRight.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})))}if(this.Reveal.getConfig().controlsTutorial){let t=this.Reveal.getIndices();!this.Reveal.hasNavigatedVertically()&&e.down?this.controlsDownArrow.classList.add("highlight"):(this.controlsDownArrow.classList.remove("highlight"),this.Reveal.getConfig().rtl?!this.Reveal.hasNavigatedHorizontally()&&e.left&&0===t.v?this.controlsLeftArrow.classList.add("highlight"):this.controlsLeftArrow.classList.remove("highlight"):!this.Reveal.hasNavigatedHorizontally()&&e.right&&0===t.v?this.controlsRightArrow.classList.add("highlight"):this.controlsRightArrow.classList.remove("highlight"))}}destroy(){this.unbind(),this.element.remove()}onNavigateLeftClicked(e){e.preventDefault(),this.Reveal.onUserInput(),"linear"===this.Reveal.getConfig().navigationMode?this.Reveal.prev():this.Reveal.left()}onNavigateRightClicked(e){e.preventDefault(),this.Reveal.onUserInput(),"linear"===this.Reveal.getConfig().navigationMode?this.Reveal.next():this.Reveal.right()}onNavigateUpClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.up()}onNavigateDownClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.down()}onNavigatePrevClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.prev()}onNavigateNextClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.next()}onEnterFullscreen(e){const t=this.Reveal.getConfig(),i=this.Reveal.getViewportElement();o(t.embedded?i:i.parentElement)}}class B{constructor(e){this.Reveal=e,this.onProgressClicked=this.onProgressClicked.bind(this)}render(){this.element=document.createElement("div"),this.element.className="progress",this.Reveal.getRevealElement().appendChild(this.element),this.bar=document.createElement("span"),this.element.appendChild(this.bar)}configure(e,t){this.element.style.display=e.progress?"block":"none"}bind(){this.Reveal.getConfig().progress&&this.element&&this.element.addEventListener("click",this.onProgressClicked,!1)}unbind(){this.Reveal.getConfig().progress&&this.element&&this.element.removeEventListener("click",this.onProgressClicked,!1)}update(){if(this.Reveal.getConfig().progress&&this.bar){let e=this.Reveal.getProgress();this.Reveal.getTotalSlides()<2&&(e=0),this.bar.style.transform="scaleX("+e+")"}}getMaxWidth(){return this.Reveal.getRevealElement().offsetWidth}onProgressClicked(e){this.Reveal.onUserInput(e),e.preventDefault();let t=this.Reveal.getSlides(),i=t.length,s=Math.floor(e.clientX/this.getMaxWidth()*i);this.Reveal.getConfig().rtl&&(s=i-s);let a=this.Reveal.getIndices(t[s]);this.Reveal.slide(a.h,a.v)}destroy(){this.element.remove()}}class H{constructor(e){this.Reveal=e,this.lastMouseWheelStep=0,this.cursorHidden=!1,this.cursorInactiveTimeout=0,this.onDocumentCursorActive=this.onDocumentCursorActive.bind(this),this.onDocumentMouseScroll=this.onDocumentMouseScroll.bind(this)}configure(e,t){e.mouseWheel?document.addEventListener("wheel",this.onDocumentMouseScroll,!1):document.removeEventListener("wheel",this.onDocumentMouseScroll,!1),e.hideInactiveCursor?(document.addEventListener("mousemove",this.onDocumentCursorActive,!1),document.addEventListener("mousedown",this.onDocumentCursorActive,!1)):(this.showCursor(),document.removeEventListener("mousemove",this.onDocumentCursorActive,!1),document.removeEventListener("mousedown",this.onDocumentCursorActive,!1))}showCursor(){this.cursorHidden&&(this.cursorHidden=!1,this.Reveal.getRevealElement().style.cursor="")}hideCursor(){!1===this.cursorHidden&&(this.cursorHidden=!0,this.Reveal.getRevealElement().style.cursor="none")}destroy(){this.showCursor(),document.removeEventListener("wheel",this.onDocumentMouseScroll,!1),document.removeEventListener("mousemove",this.onDocumentCursorActive,!1),document.removeEventListener("mousedown",this.onDocumentCursorActive,!1)}onDocumentCursorActive(e){this.showCursor(),clearTimeout(this.cursorInactiveTimeout),this.cursorInactiveTimeout=setTimeout(this.hideCursor.bind(this),this.Reveal.getConfig().hideCursorTime)}onDocumentMouseScroll(e){if(Date.now()-this.lastMouseWheelStep>1e3){this.lastMouseWheelStep=Date.now();let t=e.detail||-e.wheelDelta;t>0?this.Reveal.next():t<0&&this.Reveal.prev()}}}const D=(e,t)=>{const i=document.createElement("script");i.type="text/javascript",i.async=!1,i.defer=!1,i.src=e,"function"==typeof t&&(i.onload=i.onreadystatechange=e=>{("load"===e.type||/loaded|complete/.test(i.readyState))&&(i.onload=i.onreadystatechange=i.onerror=null,t())},i.onerror=e=>{i.onload=i.onreadystatechange=i.onerror=null,t(new Error("Failed loading script: "+i.src+"\n"+e))});const s=document.querySelector("head");s.insertBefore(i,s.lastChild)};class F{constructor(e){this.Reveal=e,this.state="idle",this.registeredPlugins={},this.asyncDependencies=[]}load(e,t){return this.state="loading",e.forEach(this.registerPlugin.bind(this)),new Promise((e=>{let i=[],s=0;if(t.forEach((e=>{e.condition&&!e.condition()||(e.async?this.asyncDependencies.push(e):i.push(e))})),i.length){s=i.length;const t=t=>{t&&"function"==typeof t.callback&&t.callback(),0==--s&&this.initPlugins().then(e)};i.forEach((e=>{"string"==typeof e.id?(this.registerPlugin(e),t(e)):"string"==typeof e.src?D(e.src,(()=>t(e))):(console.warn("Unrecognized plugin format",e),t())}))}else this.initPlugins().then(e)}))}initPlugins(){return new Promise((e=>{let t=Object.values(this.registeredPlugins),i=t.length;if(0===i)this.loadAsync().then(e);else{let s,a=()=>{0==--i?this.loadAsync().then(e):s()},n=0;s=()=>{let e=t[n++];if("function"==typeof e.init){let t=e.init(this.Reveal);t&&"function"==typeof t.then?t.then(a):a()}else a()},s()}}))}loadAsync(){return this.state="loaded",this.asyncDependencies.length&&this.asyncDependencies.forEach((e=>{D(e.src,e.callback)})),Promise.resolve()}registerPlugin(e){2===arguments.length&&"string"==typeof arguments[0]?(e=arguments[1]).id=arguments[0]:"function"==typeof e&&(e=e());let t=e.id;"string"!=typeof t?console.warn("Unrecognized plugin format; can't find plugin.id",e):void 0===this.registeredPlugins[t]?(this.registeredPlugins[t]=e,"loaded"===this.state&&"function"==typeof e.init&&e.init(this.Reveal)):console.warn('reveal.js: "'+t+'" plugin has already been registered')}hasPlugin(e){return!!this.registeredPlugins[e]}getPlugin(e){return this.registeredPlugins[e]}getRegisteredPlugins(){return this.registeredPlugins}destroy(){Object.values(this.registeredPlugins).forEach((e=>{"function"==typeof e.destroy&&e.destroy()})),this.registeredPlugins={},this.asyncDependencies=[]}}class z{constructor(e){this.Reveal=e,this.touchStartX=0,this.touchStartY=0,this.touchStartCount=0,this.touchCaptured=!1,this.onPointerDown=this.onPointerDown.bind(this),this.onPointerMove=this.onPointerMove.bind(this),this.onPointerUp=this.onPointerUp.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this)}bind(){let e=this.Reveal.getRevealElement();"onpointerdown"in window?(e.addEventListener("pointerdown",this.onPointerDown,!1),e.addEventListener("pointermove",this.onPointerMove,!1),e.addEventListener("pointerup",this.onPointerUp,!1)):window.navigator.msPointerEnabled?(e.addEventListener("MSPointerDown",this.onPointerDown,!1),e.addEventListener("MSPointerMove",this.onPointerMove,!1),e.addEventListener("MSPointerUp",this.onPointerUp,!1)):(e.addEventListener("touchstart",this.onTouchStart,!1),e.addEventListener("touchmove",this.onTouchMove,!1),e.addEventListener("touchend",this.onTouchEnd,!1))}unbind(){let e=this.Reveal.getRevealElement();e.removeEventListener("pointerdown",this.onPointerDown,!1),e.removeEventListener("pointermove",this.onPointerMove,!1),e.removeEventListener("pointerup",this.onPointerUp,!1),e.removeEventListener("MSPointerDown",this.onPointerDown,!1),e.removeEventListener("MSPointerMove",this.onPointerMove,!1),e.removeEventListener("MSPointerUp",this.onPointerUp,!1),e.removeEventListener("touchstart",this.onTouchStart,!1),e.removeEventListener("touchmove",this.onTouchMove,!1),e.removeEventListener("touchend",this.onTouchEnd,!1)}isSwipePrevented(e){if(n(e,"video[controls], audio[controls]"))return!0;for(;e&&"function"==typeof e.hasAttribute;){if(e.hasAttribute("data-prevent-swipe"))return!0;e=e.parentNode}return!1}onTouchStart(e){if(this.touchCaptured=!1,this.isSwipePrevented(e.target))return!0;this.touchStartX=e.touches[0].clientX,this.touchStartY=e.touches[0].clientY,this.touchStartCount=e.touches.length}onTouchMove(e){if(this.isSwipePrevented(e.target))return!0;let t=this.Reveal.getConfig();if(this.touchCaptured)g&&e.preventDefault();else{this.Reveal.onUserInput(e);let i=e.touches[0].clientX,s=e.touches[0].clientY;if(1===e.touches.length&&2!==this.touchStartCount){let a=this.Reveal.availableRoutes({includeFragments:!0}),n=i-this.touchStartX,r=s-this.touchStartY;n>40&&Math.abs(n)>Math.abs(r)?(this.touchCaptured=!0,"linear"===t.navigationMode?t.rtl?this.Reveal.next():this.Reveal.prev():this.Reveal.left()):n<-40&&Math.abs(n)>Math.abs(r)?(this.touchCaptured=!0,"linear"===t.navigationMode?t.rtl?this.Reveal.prev():this.Reveal.next():this.Reveal.right()):r>40&&a.up?(this.touchCaptured=!0,"linear"===t.navigationMode?this.Reveal.prev():this.Reveal.up()):r<-40&&a.down&&(this.touchCaptured=!0,"linear"===t.navigationMode?this.Reveal.next():this.Reveal.down()),t.embedded?(this.touchCaptured||this.Reveal.isVerticalSlide())&&e.preventDefault():e.preventDefault()}}}onTouchEnd(e){this.touchCaptured=!1}onPointerDown(e){e.pointerType!==e.MSPOINTER_TYPE_TOUCH&&"touch"!==e.pointerType||(e.touches=[{clientX:e.clientX,clientY:e.clientY}],this.onTouchStart(e))}onPointerMove(e){e.pointerType!==e.MSPOINTER_TYPE_TOUCH&&"touch"!==e.pointerType||(e.touches=[{clientX:e.clientX,clientY:e.clientY}],this.onTouchMove(e))}onPointerUp(e){e.pointerType!==e.MSPOINTER_TYPE_TOUCH&&"touch"!==e.pointerType||(e.touches=[{clientX:e.clientX,clientY:e.clientY}],this.onTouchEnd(e))}}const q="focus",O="blur";class W{constructor(e){this.Reveal=e,this.onRevealPointerDown=this.onRevealPointerDown.bind(this),this.onDocumentPointerDown=this.onDocumentPointerDown.bind(this)}configure(e,t){e.embedded?this.blur():(this.focus(),this.unbind())}bind(){this.Reveal.getConfig().embedded&&this.Reveal.getRevealElement().addEventListener("pointerdown",this.onRevealPointerDown,!1)}unbind(){this.Reveal.getRevealElement().removeEventListener("pointerdown",this.onRevealPointerDown,!1),document.removeEventListener("pointerdown",this.onDocumentPointerDown,!1)}focus(){this.state!==q&&(this.Reveal.getRevealElement().classList.add("focused"),document.addEventListener("pointerdown",this.onDocumentPointerDown,!1)),this.state=q}blur(){this.state!==O&&(this.Reveal.getRevealElement().classList.remove("focused"),document.removeEventListener("pointerdown",this.onDocumentPointerDown,!1)),this.state=O}isFocused(){return this.state===q}destroy(){this.Reveal.getRevealElement().classList.remove("focused")}onRevealPointerDown(e){this.focus()}onDocumentPointerDown(e){let t=r(e.target,".reveal");t&&t===this.Reveal.getRevealElement()||this.blur()}}class U{constructor(e){this.Reveal=e}render(){this.element=document.createElement("div"),this.element.className="speaker-notes",this.element.setAttribute("data-prevent-swipe",""),this.element.setAttribute("tabindex","0"),this.Reveal.getRevealElement().appendChild(this.element)}configure(e,t){e.showNotes&&this.element.setAttribute("data-layout","string"==typeof e.showNotes?e.showNotes:"inline")}update(){this.Reveal.getConfig().showNotes&&this.element&&this.Reveal.getCurrentSlide()&&!this.Reveal.isScrollView()&&!this.Reveal.isPrintView()&&(this.element.innerHTML=this.getSlideNotes()||'No notes on this slide.')}updateVisibility(){this.Reveal.getConfig().showNotes&&this.hasNotes()&&!this.Reveal.isScrollView()&&!this.Reveal.isPrintView()?this.Reveal.getRevealElement().classList.add("show-notes"):this.Reveal.getRevealElement().classList.remove("show-notes")}hasNotes(){return this.Reveal.getSlidesElement().querySelectorAll("[data-notes], aside.notes").length>0}isSpeakerNotesWindow(){return!!window.location.search.match(/receiver/gi)}getSlideNotes(e=this.Reveal.getCurrentSlide()){if(e.hasAttribute("data-notes"))return e.getAttribute("data-notes");let t=e.querySelectorAll("aside.notes");return t?Array.from(t).map((e=>e.innerHTML)).join("\n"):null}destroy(){this.element.remove()}}class V{constructor(e,t){this.diameter=100,this.diameter2=this.diameter/2,this.thickness=6,this.playing=!1,this.progress=0,this.progressOffset=1,this.container=e,this.progressCheck=t,this.canvas=document.createElement("canvas"),this.canvas.className="playback",this.canvas.width=this.diameter,this.canvas.height=this.diameter,this.canvas.style.width=this.diameter2+"px",this.canvas.style.height=this.diameter2+"px",this.context=this.canvas.getContext("2d"),this.container.appendChild(this.canvas),this.render()}setPlaying(e){const t=this.playing;this.playing=e,!t&&this.playing?this.animate():this.render()}animate(){const e=this.progress;this.progress=this.progressCheck(),e>.8&&this.progress<.2&&(this.progressOffset=this.progress),this.render(),this.playing&&requestAnimationFrame(this.animate.bind(this))}render(){let e=this.playing?this.progress:0,t=this.diameter2-this.thickness,i=this.diameter2,s=this.diameter2,a=28;this.progressOffset+=.1*(1-this.progressOffset);const n=-Math.PI/2+e*(2*Math.PI),r=-Math.PI/2+this.progressOffset*(2*Math.PI);this.context.save(),this.context.clearRect(0,0,this.diameter,this.diameter),this.context.beginPath(),this.context.arc(i,s,t+4,0,2*Math.PI,!1),this.context.fillStyle="rgba( 0, 0, 0, 0.4 )",this.context.fill(),this.context.beginPath(),this.context.arc(i,s,t,0,2*Math.PI,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle="rgba( 255, 255, 255, 0.2 )",this.context.stroke(),this.playing&&(this.context.beginPath(),this.context.arc(i,s,t,r,n,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle="#fff",this.context.stroke()),this.context.translate(i-14,s-14),this.playing?(this.context.fillStyle="#fff",this.context.fillRect(0,0,10,a),this.context.fillRect(18,0,10,a)):(this.context.beginPath(),this.context.translate(4,0),this.context.moveTo(0,0),this.context.lineTo(24,14),this.context.lineTo(0,a),this.context.fillStyle="#fff",this.context.fill()),this.context.restore()}on(e,t){this.canvas.addEventListener(e,t,!1)}off(e,t){this.canvas.removeEventListener(e,t,!1)}destroy(){this.playing=!1,this.canvas.parentNode&&this.container.removeChild(this.canvas)}}var j={width:960,height:700,margin:.04,minScale:.2,maxScale:2,controls:!0,controlsTutorial:!0,controlsLayout:"bottom-right",controlsBackArrows:"faded",progress:!0,slideNumber:!1,showSlideNumber:"all",hashOneBasedIndex:!1,hash:!1,respondToHashChanges:!0,jumpToSlide:!0,history:!1,keyboard:!0,keyboardCondition:null,disableLayout:!1,overview:!0,center:!0,touch:!0,loop:!1,rtl:!1,navigationMode:"default",shuffle:!1,fragments:!0,fragmentInURL:!0,embedded:!1,help:!0,pause:!0,showNotes:!1,showHiddenSlides:!1,autoPlayMedia:null,preloadIframes:null,autoAnimate:!0,autoAnimateMatcher:null,autoAnimateEasing:"ease",autoAnimateDuration:1,autoAnimateUnmatched:!0,autoAnimateStyles:["opacity","color","background-color","padding","font-size","line-height","letter-spacing","border-width","border-color","border-radius","outline","outline-offset"],autoSlide:0,autoSlideStoppable:!0,autoSlideMethod:null,defaultTiming:null,mouseWheel:!1,previewLinks:!1,postMessage:!0,postMessageEvents:!1,focusBodyOnPageVisibilityChange:!0,transition:"slide",transitionSpeed:"default",backgroundTransition:"fade",parallaxBackgroundImage:"",parallaxBackgroundSize:"",parallaxBackgroundRepeat:"",parallaxBackgroundPosition:"",parallaxBackgroundHorizontal:null,parallaxBackgroundVertical:null,view:null,scrollLayout:"full",scrollSnap:"mandatory",scrollProgress:"auto",scrollActivationWidth:435,pdfMaxPagesPerSlide:Number.POSITIVE_INFINITY,pdfSeparateFragments:!0,pdfPageHeightOffset:-1,viewDistance:3,mobileViewDistance:2,display:"block",hideInactiveCursor:!0,hideCursorTime:5e3,sortFragmentsOnSync:!0,dependencies:[],plugins:[]};const K="5.1.0";function $(n,o){arguments.length<2&&(o=arguments[0],n=document.querySelector(".reveal"));const l={};let c,h,g,p,w,A={},k=!1,D=!1,q={hasNavigatedHorizontally:!1,hasNavigatedVertically:!1},O=[],$=1,X={layout:"",overview:""},Y={},_="idle",J=0,G=0,Q=-1,Z=!1,ee=new v(l),te=new E(l),ie=new S(l),se=new L(l),ae=new R(l),ne=new C(l),re=new x(l),oe=new P(l),le=new T(l),de=new N(l),ce=new M(l),he=new I(l),ue=new B(l),ge=new H(l),pe=new F(l),ve=new W(l),me=new z(l),fe=new U(l);function ye(){D=!0,A.showHiddenSlides||t(Y.wrapper,'section[data-visibility="hidden"]').forEach((e=>{const t=e.parentNode;1===t.childElementCount&&/section/i.test(t.nodeName)?t.remove():e.remove()})),function(){Y.slides.classList.add("no-transition"),u?Y.wrapper.classList.add("no-hover"):Y.wrapper.classList.remove("no-hover");ae.render(),te.render(),ie.render(),he.render(),ue.render(),fe.render(),Y.pauseOverlay=((e,t,i,s="")=>{let a=e.querySelectorAll("."+i);for(let t=0;tResume presentation':null),Y.statusElement=function(){let e=Y.wrapper.querySelector(".aria-status");e||(e=document.createElement("div"),e.style.position="absolute",e.style.height="1px",e.style.width="1px",e.style.overflow="hidden",e.style.clip="rect( 1px, 1px, 1px, 1px )",e.classList.add("aria-status"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),Y.wrapper.appendChild(e));return e}(),Y.wrapper.setAttribute("role","application")}(),A.postMessage&&window.addEventListener("message",At,!1),setInterval((()=>{(!ne.isActive()&&0!==Y.wrapper.scrollTop||0!==Y.wrapper.scrollLeft)&&(Y.wrapper.scrollTop=0,Y.wrapper.scrollLeft=0)}),1e3),document.addEventListener("fullscreenchange",xt),document.addEventListener("webkitfullscreenchange",xt),rt().forEach((e=>{t(e,"section").forEach(((e,t)=>{t>0&&(e.classList.remove("present"),e.classList.remove("past"),e.classList.add("future"),e.setAttribute("aria-hidden","true"))}))})),Ee(),ae.update(!0),function(){const e="print"===A.view,t="scroll"===A.view||"reader"===A.view;(e||t)&&(e?Ae():me.unbind(),Y.viewport.classList.add("loading-scroll-mode"),e?"complete"===document.readyState?re.activate():window.addEventListener("load",(()=>re.activate())):ne.activate())}(),ce.readURL(),setTimeout((()=>{Y.slides.classList.remove("no-transition"),Y.wrapper.classList.add("ready"),Ce({type:"ready",data:{indexh:c,indexv:h,currentSlide:p}})}),1)}function be(e){Y.statusElement.textContent=e}function we(e){let t="";if(3===e.nodeType)t+=e.textContent;else if(1===e.nodeType){let i=e.getAttribute("aria-hidden"),s="none"===window.getComputedStyle(e).display;"true"===i||s||Array.from(e.childNodes).forEach((e=>{t+=we(e)}))}return t=t.trim(),""===t?"":t+" "}function Ee(t){const s={...A};if("object"==typeof t&&e(A,t),!1===l.isReady())return;const a=Y.wrapper.querySelectorAll(m).length;Y.wrapper.classList.remove(s.transition),Y.wrapper.classList.add(A.transition),Y.wrapper.setAttribute("data-transition-speed",A.transitionSpeed),Y.wrapper.setAttribute("data-background-transition",A.backgroundTransition),Y.viewport.style.setProperty("--slide-width","string"==typeof A.width?A.width:A.width+"px"),Y.viewport.style.setProperty("--slide-height","string"==typeof A.height?A.height:A.height+"px"),A.shuffle&&Ge(),i(Y.wrapper,"embedded",A.embedded),i(Y.wrapper,"rtl",A.rtl),i(Y.wrapper,"center",A.center),!1===A.pause&&Ke(),A.previewLinks?(Te(),Ne("[data-preview-link=false]")):(Ne(),Te("[data-preview-link]:not([data-preview-link=false])")),se.reset(),w&&(w.destroy(),w=null),a>1&&A.autoSlide&&A.autoSlideStoppable&&(w=new V(Y.wrapper,(()=>Math.min(Math.max((Date.now()-Q)/J,0),1))),w.on("click",Tt),Z=!1),"default"!==A.navigationMode?Y.wrapper.setAttribute("data-navigation-mode",A.navigationMode):Y.wrapper.removeAttribute("data-navigation-mode"),fe.configure(A,s),ve.configure(A,s),ge.configure(A,s),he.configure(A,s),ue.configure(A,s),de.configure(A,s),oe.configure(A,s),te.configure(A,s),Je()}function Se(){window.addEventListener("resize",Lt,!1),A.touch&&me.bind(),A.keyboard&&de.bind(),A.progress&&ue.bind(),A.respondToHashChanges&&ce.bind(),he.bind(),ve.bind(),Y.slides.addEventListener("click",kt,!1),Y.slides.addEventListener("transitionend",Rt,!1),Y.pauseOverlay.addEventListener("click",Ke,!1),A.focusBodyOnPageVisibilityChange&&document.addEventListener("visibilitychange",Ct,!1)}function Ae(){me.unbind(),ve.unbind(),de.unbind(),he.unbind(),ue.unbind(),ce.unbind(),window.removeEventListener("resize",Lt,!1),Y.slides.removeEventListener("click",kt,!1),Y.slides.removeEventListener("transitionend",Rt,!1),Y.pauseOverlay.removeEventListener("click",Ke,!1)}function Re(e,t,i){n.addEventListener(e,t,i)}function ke(e,t,i){n.removeEventListener(e,t,i)}function Le(e){"string"==typeof e.layout&&(X.layout=e.layout),"string"==typeof e.overview&&(X.overview=e.overview),X.layout?a(Y.slides,X.layout+" "+X.overview):a(Y.slides,X.overview)}function Ce({target:t=Y.wrapper,type:i,data:s,bubbles:a=!0}){let n=document.createEvent("HTMLEvents",1,2);return n.initEvent(i,a,!0),e(n,s),t.dispatchEvent(n),t===Y.wrapper&&Pe(i),n}function xe(e){Ce({type:"slidechanged",data:{indexh:c,indexv:h,previousSlide:g,currentSlide:p,origin:e}})}function Pe(t,i){if(A.postMessageEvents&&window.parent!==window.self){let s={namespace:"reveal",eventName:t,state:ut()};e(s,i),window.parent.postMessage(JSON.stringify(s),"*")}}function Te(e="a"){Array.from(Y.wrapper.querySelectorAll(e)).forEach((e=>{/^(http|www)/gi.test(e.getAttribute("href"))&&e.addEventListener("click",Pt,!1)}))}function Ne(e="a"){Array.from(Y.wrapper.querySelectorAll(e)).forEach((e=>{/^(http|www)/gi.test(e.getAttribute("href"))&&e.removeEventListener("click",Pt,!1)}))}function Me(e){Be(),Y.overlay=document.createElement("div"),Y.overlay.classList.add("overlay"),Y.overlay.classList.add("overlay-preview"),Y.wrapper.appendChild(Y.overlay),Y.overlay.innerHTML=`
\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\tUnable to load iframe. This is likely due to the site's policy (x-frame-options).\n\t\t\t\t\n\t\t\t
`,Y.overlay.querySelector("iframe").addEventListener("load",(e=>{Y.overlay.classList.add("loaded")}),!1),Y.overlay.querySelector(".close").addEventListener("click",(e=>{Be(),e.preventDefault()}),!1),Y.overlay.querySelector(".external").addEventListener("click",(e=>{Be()}),!1)}function Ie(){if(A.help){Be(),Y.overlay=document.createElement("div"),Y.overlay.classList.add("overlay"),Y.overlay.classList.add("overlay-help"),Y.wrapper.appendChild(Y.overlay);let e='

Keyboard Shortcuts


',t=de.getShortcuts(),i=de.getBindings();e+="";for(let i in t)e+=``;for(let t in i)i[t].key&&i[t].description&&(e+=``);e+="
KEYACTION
${i}${t[i]}
${i[t].key}${i[t].description}
",Y.overlay.innerHTML=`\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
${e}
\n\t\t\t\t
\n\t\t\t`,Y.overlay.querySelector(".close").addEventListener("click",(e=>{Be(),e.preventDefault()}),!1)}}function Be(){return!!Y.overlay&&(Y.overlay.parentNode.removeChild(Y.overlay),Y.overlay=null,!0)}function He(){if(Y.wrapper&&!re.isActive()){const e=Y.viewport.offsetWidth,t=Y.viewport.offsetHeight;if(!A.disableLayout){u&&!A.embedded&&document.documentElement.style.setProperty("--vh",.01*window.innerHeight+"px");const i=ne.isActive()?Fe(e,t):Fe(),s=$;De(A.width,A.height),Y.slides.style.width=i.width+"px",Y.slides.style.height=i.height+"px",$=Math.min(i.presentationWidth/i.width,i.presentationHeight/i.height),$=Math.max($,A.minScale),$=Math.min($,A.maxScale),1===$||ne.isActive()?(Y.slides.style.zoom="",Y.slides.style.left="",Y.slides.style.top="",Y.slides.style.bottom="",Y.slides.style.right="",Le({layout:""})):(Y.slides.style.zoom="",Y.slides.style.left="50%",Y.slides.style.top="50%",Y.slides.style.bottom="auto",Y.slides.style.right="auto",Le({layout:"translate(-50%, -50%) scale("+$+")"}));const a=Array.from(Y.wrapper.querySelectorAll(m));for(let e=0,t=a.length;e0&&e.presentationWidth<=A.scrollActivationWidth?ne.isActive()||(ae.create(),ne.activate()):ne.isActive()&&ne.deactivate()}}(),Y.viewport.style.setProperty("--slide-scale",$),Y.viewport.style.setProperty("--viewport-width",e+"px"),Y.viewport.style.setProperty("--viewport-height",t+"px"),ne.layout(),ue.update(),ae.updateParallax(),le.isActive()&&le.update()}}function De(e,i){t(Y.slides,"section > .stretch, section > .r-stretch").forEach((t=>{let s=((e,t=0)=>{if(e){let i,s=e.style.height;return e.style.height="0px",e.parentNode.style.height="auto",i=t-e.parentNode.offsetHeight,e.style.height=s+"px",e.parentNode.style.removeProperty("height"),i}return t})(t,i);if(/(img|video)/gi.test(t.nodeName)){const i=t.naturalWidth||t.videoWidth,a=t.naturalHeight||t.videoHeight,n=Math.min(e/i,s/a);t.style.width=i*n+"px",t.style.height=a*n+"px"}else t.style.width=e+"px",t.style.height=s+"px"}))}function Fe(e,t){let i=A.width,s=A.height;A.disableLayout&&(i=Y.slides.offsetWidth,s=Y.slides.offsetHeight);const a={width:i,height:s,presentationWidth:e||Y.wrapper.offsetWidth,presentationHeight:t||Y.wrapper.offsetHeight};return a.presentationWidth-=a.presentationWidth*A.margin,a.presentationHeight-=a.presentationHeight*A.margin,"string"==typeof a.width&&/%$/.test(a.width)&&(a.width=parseInt(a.width,10)/100*a.presentationWidth),"string"==typeof a.height&&/%$/.test(a.height)&&(a.height=parseInt(a.height,10)/100*a.presentationHeight),a}function ze(e,t){"object"==typeof e&&"function"==typeof e.setAttribute&&e.setAttribute("data-previous-indexv",t||0)}function qe(e){if("object"==typeof e&&"function"==typeof e.setAttribute&&e.classList.contains("stack")){const t=e.hasAttribute("data-start-indexv")?"data-start-indexv":"data-previous-indexv";return parseInt(e.getAttribute(t)||0,10)}return 0}function Oe(e=p){return e&&e.parentNode&&!!e.parentNode.nodeName.match(/section/i)}function We(){return!(!p||!Oe(p))&&!p.nextElementSibling}function Ue(){return 0===c&&0===h}function Ve(){return!!p&&(!p.nextElementSibling&&(!Oe(p)||!p.parentNode.nextElementSibling))}function je(){if(A.pause){const e=Y.wrapper.classList.contains("paused");pt(),Y.wrapper.classList.add("paused"),!1===e&&Ce({type:"paused"})}}function Ke(){const e=Y.wrapper.classList.contains("paused");Y.wrapper.classList.remove("paused"),gt(),e&&Ce({type:"resumed"})}function $e(e){"boolean"==typeof e?e?je():Ke():Xe()?Ke():je()}function Xe(){return Y.wrapper.classList.contains("paused")}function Ye(e,i,s,a){if(Ce({type:"beforeslidechange",data:{indexh:void 0===e?c:e,indexv:void 0===i?h:i,origin:a}}).defaultPrevented)return;g=p;const r=Y.wrapper.querySelectorAll(f);if(ne.isActive()){const t=ne.getSlideByIndices(e,i);return void(t&&ne.scrollToSlide(t))}if(0===r.length)return;void 0!==i||le.isActive()||(i=qe(r[e])),g&&g.parentNode&&g.parentNode.classList.contains("stack")&&ze(g.parentNode,h);const o=O.concat();O.length=0;let l=c||0,d=h||0;c=Qe(f,void 0===e?c:e),h=Qe(y,void 0===i?h:i);let u=c!==l||h!==d;u||(g=null);let v=r[c],m=v.querySelectorAll("section");n.classList.toggle("is-vertical-slide",m.length>1),p=m[h]||v;let b=!1;u&&g&&p&&!le.isActive()&&(_="running",b=_e(g,p,l,d),b&&Y.slides.classList.add("disable-slide-transitions")),tt(),He(),le.isActive()&&le.update(),void 0!==s&&oe.goto(s),g&&g!==p&&(g.classList.remove("present"),g.setAttribute("aria-hidden","true"),Ue()&&setTimeout((()=>{t(Y.wrapper,f+".stack").forEach((e=>{ze(e,0)}))}),0));e:for(let e=0,t=O.length;e{be(we(p))})),ue.update(),he.update(),fe.update(),ae.update(),ae.updateParallax(),te.update(),oe.update(),ce.writeURL(),gt(),b&&(setTimeout((()=>{Y.slides.classList.remove("disable-slide-transitions")}),0),A.autoAnimate&&se.run(g,p))}function _e(e,t,i,s){return e.hasAttribute("data-auto-animate")&&t.hasAttribute("data-auto-animate")&&e.getAttribute("data-auto-animate-id")===t.getAttribute("data-auto-animate-id")&&!(c>i||h>s?t:e).hasAttribute("data-auto-animate-restart")}function Je(){Ae(),Se(),He(),J=A.autoSlide,gt(),ae.create(),ce.writeURL(),!0===A.sortFragmentsOnSync&&oe.sortAll(),he.update(),ue.update(),tt(),fe.update(),fe.updateVisibility(),ae.update(!0),te.update(),ee.formatEmbeddedContent(),!1===A.autoPlayMedia?ee.stopEmbeddedContent(p,{unloadIframes:!1}):ee.startEmbeddedContent(p),le.isActive()&&le.layout()}function Ge(e=rt()){e.forEach(((t,i)=>{let s=e[Math.floor(Math.random()*e.length)];s.parentNode===t.parentNode&&t.parentNode.insertBefore(t,s);let a=t.querySelectorAll("section");a.length&&Ge(a)}))}function Qe(e,i){let s=t(Y.wrapper,e),a=s.length,n=ne.isActive()||re.isActive(),r=!1,o=!1;if(a){A.loop&&(i>=a&&(r=!0),(i%=a)<0&&(i=a+i,o=!0)),i=Math.max(Math.min(i,a-1),0);for(let e=0;ei?(t.classList.add(a?"past":"future"),A.fragments&&et(t)):e===i&&A.fragments&&(r?et(t):o&&Ze(t))}let e=s[i],t=e.classList.contains("present");e.classList.add("present"),e.removeAttribute("hidden"),e.removeAttribute("aria-hidden"),t||Ce({target:e,type:"visible",bubbles:!1});let l=e.getAttribute("data-state");l&&(O=O.concat(l.split(" ")))}else i=0;return i}function Ze(e){t(e,".fragment").forEach((e=>{e.classList.add("visible"),e.classList.remove("current-fragment")}))}function et(e){t(e,".fragment.visible").forEach((e=>{e.classList.remove("visible","current-fragment")}))}function tt(){let e,i,s=rt(),a=s.length;if(a&&void 0!==c){let n=le.isActive()?10:A.viewDistance;u&&(n=le.isActive()?6:A.mobileViewDistance),re.isActive()&&(n=Number.MAX_VALUE);for(let r=0;r0,right:c0,down:h1&&(s.left=!0,s.right=!0),i.length>1&&(s.up=!0,s.down=!0)),t.length>1&&"linear"===A.navigationMode&&(s.right=s.right||s.down,s.left=s.left||s.up),!0===e){let e=oe.availableRoutes();s.left=s.left||e.prev,s.up=s.up||e.prev,s.down=s.down||e.next,s.right=s.right||e.next}if(A.rtl){let e=s.left;s.left=s.right,s.right=e}return s}function st(e=p){let t=rt(),i=0;e:for(let s=0;s0){let e=p.querySelector(".current-fragment");i=e&&e.hasAttribute("data-fragment-index")?parseInt(e.getAttribute("data-fragment-index"),10):p.querySelectorAll(".fragment.visible").length-1}}return{h:s,v:a,f:i}}function nt(){return t(Y.wrapper,m+':not(.stack):not([data-visibility="uncounted"])')}function rt(){return t(Y.wrapper,f)}function ot(){return t(Y.wrapper,".slides>section>section")}function lt(){return rt().length>1}function dt(){return ot().length>1}function ct(){return nt().length}function ht(e,t){let i=rt()[e],s=i&&i.querySelectorAll("section");return s&&s.length&&"number"==typeof t?s?s[t]:void 0:i}function ut(){let e=at();return{indexh:e.h,indexv:e.v,indexf:e.f,paused:Xe(),overview:le.isActive()}}function gt(){if(pt(),p&&!1!==A.autoSlide){let e=p.querySelector(".current-fragment[data-autoslide]"),i=e?e.getAttribute("data-autoslide"):null,s=p.parentNode?p.parentNode.getAttribute("data-autoslide"):null,a=p.getAttribute("data-autoslide");i?J=parseInt(i,10):a?J=parseInt(a,10):s?J=parseInt(s,10):(J=A.autoSlide,0===p.querySelectorAll(".fragment").length&&t(p,"video, audio").forEach((e=>{e.hasAttribute("data-autoplay")&&J&&1e3*e.duration/e.playbackRate>J&&(J=1e3*e.duration/e.playbackRate+1e3)}))),!J||Z||Xe()||le.isActive()||Ve()&&!oe.availableRoutes().next&&!0!==A.loop||(G=setTimeout((()=>{"function"==typeof A.autoSlideMethod?A.autoSlideMethod():St(),gt()}),J),Q=Date.now()),w&&w.setPlaying(-1!==G)}}function pt(){clearTimeout(G),G=-1}function vt(){J&&!Z&&(Z=!0,Ce({type:"autoslidepaused"}),clearTimeout(G),w&&w.setPlaying(!1))}function mt(){J&&Z&&(Z=!1,Ce({type:"autoslideresumed"}),gt())}function ft({skipFragments:e=!1}={}){if(q.hasNavigatedHorizontally=!0,ne.isActive())return ne.prev();A.rtl?(le.isActive()||e||!1===oe.next())&&it().left&&Ye(c+1,"grid"===A.navigationMode?h:void 0):(le.isActive()||e||!1===oe.prev())&&it().left&&Ye(c-1,"grid"===A.navigationMode?h:void 0)}function yt({skipFragments:e=!1}={}){if(q.hasNavigatedHorizontally=!0,ne.isActive())return ne.next();A.rtl?(le.isActive()||e||!1===oe.prev())&&it().right&&Ye(c-1,"grid"===A.navigationMode?h:void 0):(le.isActive()||e||!1===oe.next())&&it().right&&Ye(c+1,"grid"===A.navigationMode?h:void 0)}function bt({skipFragments:e=!1}={}){if(ne.isActive())return ne.prev();(le.isActive()||e||!1===oe.prev())&&it().up&&Ye(c,h-1)}function wt({skipFragments:e=!1}={}){if(q.hasNavigatedVertically=!0,ne.isActive())return ne.next();(le.isActive()||e||!1===oe.next())&&it().down&&Ye(c,h+1)}function Et({skipFragments:e=!1}={}){if(ne.isActive())return ne.prev();if(e||!1===oe.prev())if(it().up)bt({skipFragments:e});else{let i;if(i=A.rtl?t(Y.wrapper,f+".future").pop():t(Y.wrapper,f+".past").pop(),i&&i.classList.contains("stack")){let e=i.querySelectorAll("section").length-1||void 0;Ye(c-1,e)}else A.rtl?yt({skipFragments:e}):ft({skipFragments:e})}}function St({skipFragments:e=!1}={}){if(q.hasNavigatedHorizontally=!0,q.hasNavigatedVertically=!0,ne.isActive())return ne.next();if(e||!1===oe.next()){let t=it();t.down&&t.right&&A.loop&&We()&&(t.down=!1),t.down?wt({skipFragments:e}):A.rtl?ft({skipFragments:e}):yt({skipFragments:e})}}function At(e){let t=e.data;if("string"==typeof t&&"{"===t.charAt(0)&&"}"===t.charAt(t.length-1)&&(t=JSON.parse(t),t.method&&"function"==typeof l[t.method]))if(!1===b.test(t.method)){const e=l[t.method].apply(l,t.args);Pe("callback",{method:t.method,result:e})}else console.warn('reveal.js: "'+t.method+'" is is blacklisted from the postMessage API')}function Rt(e){"running"===_&&/section/gi.test(e.target.nodeName)&&(_="idle",Ce({type:"slidetransitionend",data:{indexh:c,indexv:h,previousSlide:g,currentSlide:p}}))}function kt(e){const t=r(e.target,'a[href^="#"]');if(t){const i=t.getAttribute("href"),s=ce.getIndicesFromHash(i);s&&(l.slide(s.h,s.v,s.f),e.preventDefault())}}function Lt(e){He()}function Ct(e){!1===document.hidden&&document.activeElement!==document.body&&("function"==typeof document.activeElement.blur&&document.activeElement.blur(),document.body.focus())}function xt(e){(document.fullscreenElement||document.webkitFullscreenElement)===Y.wrapper&&(e.stopImmediatePropagation(),setTimeout((()=>{l.layout(),l.focus.focus()}),1))}function Pt(e){if(e.currentTarget&&e.currentTarget.hasAttribute("href")){let t=e.currentTarget.getAttribute("href");t&&(Me(t),e.preventDefault())}}function Tt(e){Ve()&&!1===A.loop?(Ye(0,0),mt()):Z?mt():vt()}const Nt={VERSION:K,initialize:function(e){if(!n)throw'Unable to find presentation root (
).';if(k=!0,Y.wrapper=n,Y.slides=n.querySelector(".slides"),!Y.slides)throw'Unable to find slides container (
).';return A={...j,...A,...o,...e,...d()},/print-pdf/gi.test(window.location.search)&&(A.view="print"),function(){!0===A.embedded?Y.viewport=r(n,".reveal-viewport")||n:(Y.viewport=document.body,document.documentElement.classList.add("reveal-full-page"));Y.viewport.classList.add("reveal-viewport")}(),window.addEventListener("load",He,!1),pe.load(A.plugins,A.dependencies).then(ye),new Promise((e=>l.on("ready",e)))},configure:Ee,destroy:function(){!1!==k&&(Ae(),pt(),Ne(),fe.destroy(),ve.destroy(),pe.destroy(),ge.destroy(),he.destroy(),ue.destroy(),ae.destroy(),te.destroy(),ie.destroy(),document.removeEventListener("fullscreenchange",xt),document.removeEventListener("webkitfullscreenchange",xt),document.removeEventListener("visibilitychange",Ct,!1),window.removeEventListener("message",At,!1),window.removeEventListener("load",He,!1),Y.pauseOverlay&&Y.pauseOverlay.remove(),Y.statusElement&&Y.statusElement.remove(),document.documentElement.classList.remove("reveal-full-page"),Y.wrapper.classList.remove("ready","center","has-horizontal-slides","has-vertical-slides"),Y.wrapper.removeAttribute("data-transition-speed"),Y.wrapper.removeAttribute("data-background-transition"),Y.viewport.classList.remove("reveal-viewport"),Y.viewport.style.removeProperty("--slide-width"),Y.viewport.style.removeProperty("--slide-height"),Y.slides.style.removeProperty("width"),Y.slides.style.removeProperty("height"),Y.slides.style.removeProperty("zoom"),Y.slides.style.removeProperty("left"),Y.slides.style.removeProperty("top"),Y.slides.style.removeProperty("bottom"),Y.slides.style.removeProperty("right"),Y.slides.style.removeProperty("transform"),Array.from(Y.wrapper.querySelectorAll(m)).forEach((e=>{e.style.removeProperty("display"),e.style.removeProperty("top"),e.removeAttribute("hidden"),e.removeAttribute("aria-hidden")})))},sync:Je,syncSlide:function(e=p){ae.sync(e),oe.sync(e),ee.load(e),ae.update(),fe.update()},syncFragments:oe.sync.bind(oe),slide:Ye,left:ft,right:yt,up:bt,down:wt,prev:Et,next:St,navigateLeft:ft,navigateRight:yt,navigateUp:bt,navigateDown:wt,navigatePrev:Et,navigateNext:St,navigateFragment:oe.goto.bind(oe),prevFragment:oe.prev.bind(oe),nextFragment:oe.next.bind(oe),on:Re,off:ke,addEventListener:Re,removeEventListener:ke,layout:He,shuffle:Ge,availableRoutes:it,availableFragments:oe.availableRoutes.bind(oe),toggleHelp:function(e){"boolean"==typeof e?e?Ie():Be():Y.overlay?Be():Ie()},toggleOverview:le.toggle.bind(le),toggleScrollView:ne.toggle.bind(ne),togglePause:$e,toggleAutoSlide:function(e){"boolean"==typeof e?e?mt():vt():Z?mt():vt()},toggleJumpToSlide:function(e){"boolean"==typeof e?e?ie.show():ie.hide():ie.isVisible()?ie.hide():ie.show()},isFirstSlide:Ue,isLastSlide:Ve,isLastVerticalSlide:We,isVerticalSlide:Oe,isVerticalStack:function(e=p){return e.classList.contains(".stack")||null!==e.querySelector("section")},isPaused:Xe,isAutoSliding:function(){return!(!J||Z)},isSpeakerNotes:fe.isSpeakerNotesWindow.bind(fe),isOverview:le.isActive.bind(le),isFocused:ve.isFocused.bind(ve),isScrollView:ne.isActive.bind(ne),isPrintView:re.isActive.bind(re),isReady:()=>D,loadSlide:ee.load.bind(ee),unloadSlide:ee.unload.bind(ee),startEmbeddedContent:()=>ee.startEmbeddedContent(p),stopEmbeddedContent:()=>ee.stopEmbeddedContent(p,{unloadIframes:!1}),showPreview:Me,hidePreview:Be,addEventListeners:Se,removeEventListeners:Ae,dispatchEvent:Ce,getState:ut,setState:function(e){if("object"==typeof e){Ye(s(e.indexh),s(e.indexv),s(e.indexf));let t=s(e.paused),i=s(e.overview);"boolean"==typeof t&&t!==Xe()&&$e(t),"boolean"==typeof i&&i!==le.isActive()&&le.toggle(i)}},getProgress:function(){let e=ct(),t=st();if(p){let e=p.querySelectorAll(".fragment");if(e.length>0){let i=.9;t+=p.querySelectorAll(".fragment.visible").length/e.length*i}}return Math.min(t/(e-1),1)},getIndices:at,getSlidesAttributes:function(){return nt().map((e=>{let t={};for(let i=0;ig,getCurrentSlide:()=>p,getSlideBackground:function(e,t){let i="number"==typeof e?ht(e,t):e;if(i)return i.slideBackgroundElement},getSlideNotes:fe.getSlideNotes.bind(fe),getSlides:nt,getHorizontalSlides:rt,getVerticalSlides:ot,hasHorizontalSlides:lt,hasVerticalSlides:dt,hasNavigatedHorizontally:()=>q.hasNavigatedHorizontally,hasNavigatedVertically:()=>q.hasNavigatedVertically,shouldAutoAnimateBetween:_e,addKeyBinding:de.addKeyBinding.bind(de),removeKeyBinding:de.removeKeyBinding.bind(de),triggerKey:de.triggerKey.bind(de),registerKeyboardShortcut:de.registerKeyboardShortcut.bind(de),getComputedSlideSize:Fe,setCurrentScrollPage:function(e,t,i){let s=c||0;c=t,h=i;const a=p!==e;g=p,p=e,p&&g&&A.autoAnimate&&_e(g,p,s,h)&&se.run(g,p),a&&(g&&(ee.stopEmbeddedContent(g),ee.stopEmbeddedContent(g.slideBackgroundElement)),ee.startEmbeddedContent(p),ee.startEmbeddedContent(p.slideBackgroundElement)),requestAnimationFrame((()=>{be(we(p))})),xe()},getScale:()=>$,getConfig:()=>A,getQueryHash:d,getSlidePath:ce.getHash.bind(ce),getRevealElement:()=>n,getSlidesElement:()=>Y.slides,getViewportElement:()=>Y.viewport,getBackgroundsElement:()=>ae.element,registerPlugin:pe.registerPlugin.bind(pe),hasPlugin:pe.hasPlugin.bind(pe),getPlugin:pe.getPlugin.bind(pe),getPlugins:pe.getRegisteredPlugins.bind(pe)};return e(l,{...Nt,announceStatus:be,getStatusText:we,focus:ve,scroll:ne,progress:ue,controls:he,location:ce,overview:le,fragments:oe,backgrounds:ae,slideContent:ee,slideNumber:te,onUserInput:function(e){A.autoSlideStoppable&&vt()},closeOverlay:Be,updateSlidesVisibility:tt,layoutSlideContents:De,transformSlides:Le,cueAutoSlide:gt,cancelAutoSlide:pt}),Nt}let X=$,Y=[];X.initialize=e=>(Object.assign(X,new $(document.querySelector(".reveal"),e)),Y.map((e=>e(X))),X.initialize()),["configure","on","off","addEventListener","removeEventListener","registerPlugin"].forEach((e=>{X[e]=(...t)=>{Y.push((i=>i[e].call(null,...t)))}})),X.isReady=()=>!1,X.VERSION=K;export{X as default}; +//# sourceMappingURL=reveal.esm.js.map diff --git a/src/.quarto/_freeze/site_libs/revealjs/dist/reveal.esm.js.map b/src/.quarto/_freeze/site_libs/revealjs/dist/reveal.esm.js.map new file mode 100644 index 0000000000000000000000000000000000000000..5d23aab2d00cb34d43215526a12ff1ad94d5364e --- /dev/null +++ b/src/.quarto/_freeze/site_libs/revealjs/dist/reveal.esm.js.map @@ -0,0 +1 @@ +{"version":3,"file":"reveal.esm.js","sources":["../js/utils/util.js","../js/utils/device.js","../node_modules/fitty/dist/fitty.module.js","../js/controllers/slidecontent.js","../js/utils/constants.js","../js/controllers/slidenumber.js","../js/controllers/jumptoslide.js","../js/utils/color.js","../js/controllers/backgrounds.js","../js/controllers/autoanimate.js","../js/controllers/scrollview.js","../js/controllers/printview.js","../js/controllers/fragments.js","../js/controllers/overview.js","../js/controllers/keyboard.js","../js/controllers/location.js","../js/controllers/controls.js","../js/controllers/progress.js","../js/controllers/pointer.js","../js/utils/loader.js","../js/controllers/plugins.js","../js/controllers/touch.js","../js/controllers/focus.js","../js/controllers/notes.js","../js/components/playback.js","../js/config.js","../js/reveal.js","../js/index.js"],"sourcesContent":["/**\n * Extend object a with the properties of object b.\n * If there's a conflict, object b takes precedence.\n *\n * @param {object} a\n * @param {object} b\n */\nexport const extend = ( a, b ) => {\n\n\tfor( let i in b ) {\n\t\ta[ i ] = b[ i ];\n\t}\n\n\treturn a;\n\n}\n\n/**\n * querySelectorAll but returns an Array.\n */\nexport const queryAll = ( el, selector ) => {\n\n\treturn Array.from( el.querySelectorAll( selector ) );\n\n}\n\n/**\n * classList.toggle() with cross browser support\n */\nexport const toggleClass = ( el, className, value ) => {\n\tif( value ) {\n\t\tel.classList.add( className );\n\t}\n\telse {\n\t\tel.classList.remove( className );\n\t}\n}\n\n/**\n * Utility for deserializing a value.\n *\n * @param {*} value\n * @return {*}\n */\nexport const deserialize = ( value ) => {\n\n\tif( typeof value === 'string' ) {\n\t\tif( value === 'null' ) return null;\n\t\telse if( value === 'true' ) return true;\n\t\telse if( value === 'false' ) return false;\n\t\telse if( value.match( /^-?[\\d\\.]+$/ ) ) return parseFloat( value );\n\t}\n\n\treturn value;\n\n}\n\n/**\n * Measures the distance in pixels between point a\n * and point b.\n *\n * @param {object} a point with x/y properties\n * @param {object} b point with x/y properties\n *\n * @return {number}\n */\nexport const distanceBetween = ( a, b ) => {\n\n\tlet dx = a.x - b.x,\n\t\tdy = a.y - b.y;\n\n\treturn Math.sqrt( dx*dx + dy*dy );\n\n}\n\n/**\n * Applies a CSS transform to the target element.\n *\n * @param {HTMLElement} element\n * @param {string} transform\n */\nexport const transformElement = ( element, transform ) => {\n\n\telement.style.transform = transform;\n\n}\n\n/**\n * Element.matches with IE support.\n *\n * @param {HTMLElement} target The element to match\n * @param {String} selector The CSS selector to match\n * the element against\n *\n * @return {Boolean}\n */\nexport const matches = ( target, selector ) => {\n\n\tlet matchesMethod = target.matches || target.matchesSelector || target.msMatchesSelector;\n\n\treturn !!( matchesMethod && matchesMethod.call( target, selector ) );\n\n}\n\n/**\n * Find the closest parent that matches the given\n * selector.\n *\n * @param {HTMLElement} target The child element\n * @param {String} selector The CSS selector to match\n * the parents against\n *\n * @return {HTMLElement} The matched parent or null\n * if no matching parent was found\n */\nexport const closest = ( target, selector ) => {\n\n\t// Native Element.closest\n\tif( typeof target.closest === 'function' ) {\n\t\treturn target.closest( selector );\n\t}\n\n\t// Polyfill\n\twhile( target ) {\n\t\tif( matches( target, selector ) ) {\n\t\t\treturn target;\n\t\t}\n\n\t\t// Keep searching\n\t\ttarget = target.parentNode;\n\t}\n\n\treturn null;\n\n}\n\n/**\n * Handling the fullscreen functionality via the fullscreen API\n *\n * @see http://fullscreen.spec.whatwg.org/\n * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode\n */\nexport const enterFullscreen = element => {\n\n\telement = element || document.documentElement;\n\n\t// Check which implementation is available\n\tlet requestMethod = element.requestFullscreen ||\n\t\t\t\t\t\telement.webkitRequestFullscreen ||\n\t\t\t\t\t\telement.webkitRequestFullScreen ||\n\t\t\t\t\t\telement.mozRequestFullScreen ||\n\t\t\t\t\t\telement.msRequestFullscreen;\n\n\tif( requestMethod ) {\n\t\trequestMethod.apply( element );\n\t}\n\n}\n\n/**\n * Creates an HTML element and returns a reference to it.\n * If the element already exists the existing instance will\n * be returned.\n *\n * @param {HTMLElement} container\n * @param {string} tagname\n * @param {string} classname\n * @param {string} innerHTML\n *\n * @return {HTMLElement}\n */\nexport const createSingletonNode = ( container, tagname, classname, innerHTML='' ) => {\n\n\t// Find all nodes matching the description\n\tlet nodes = container.querySelectorAll( '.' + classname );\n\n\t// Check all matches to find one which is a direct child of\n\t// the specified container\n\tfor( let i = 0; i < nodes.length; i++ ) {\n\t\tlet testNode = nodes[i];\n\t\tif( testNode.parentNode === container ) {\n\t\t\treturn testNode;\n\t\t}\n\t}\n\n\t// If no node was found, create it now\n\tlet node = document.createElement( tagname );\n\tnode.className = classname;\n\tnode.innerHTML = innerHTML;\n\tcontainer.appendChild( node );\n\n\treturn node;\n\n}\n\n/**\n * Injects the given CSS styles into the DOM.\n *\n * @param {string} value\n */\nexport const createStyleSheet = ( value ) => {\n\n\tlet tag = document.createElement( 'style' );\n\ttag.type = 'text/css';\n\n\tif( value && value.length > 0 ) {\n\t\tif( tag.styleSheet ) {\n\t\t\ttag.styleSheet.cssText = value;\n\t\t}\n\t\telse {\n\t\t\ttag.appendChild( document.createTextNode( value ) );\n\t\t}\n\t}\n\n\tdocument.head.appendChild( tag );\n\n\treturn tag;\n\n}\n\n/**\n * Returns a key:value hash of all query params.\n */\nexport const getQueryHash = () => {\n\n\tlet query = {};\n\n\tlocation.search.replace( /[A-Z0-9]+?=([\\w\\.%-]*)/gi, a => {\n\t\tquery[ a.split( '=' ).shift() ] = a.split( '=' ).pop();\n\t} );\n\n\t// Basic deserialization\n\tfor( let i in query ) {\n\t\tlet value = query[ i ];\n\n\t\tquery[ i ] = deserialize( unescape( value ) );\n\t}\n\n\t// Do not accept new dependencies via query config to avoid\n\t// the potential of malicious script injection\n\tif( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];\n\n\treturn query;\n\n}\n\n/**\n * Returns the remaining height within the parent of the\n * target element.\n *\n * remaining height = [ configured parent height ] - [ current parent height ]\n *\n * @param {HTMLElement} element\n * @param {number} [height]\n */\nexport const getRemainingHeight = ( element, height = 0 ) => {\n\n\tif( element ) {\n\t\tlet newHeight, oldHeight = element.style.height;\n\n\t\t// Change the .stretch element height to 0 in order find the height of all\n\t\t// the other elements\n\t\telement.style.height = '0px';\n\n\t\t// In Overview mode, the parent (.slide) height is set of 700px.\n\t\t// Restore it temporarily to its natural height.\n\t\telement.parentNode.style.height = 'auto';\n\n\t\tnewHeight = height - element.parentNode.offsetHeight;\n\n\t\t// Restore the old height, just in case\n\t\telement.style.height = oldHeight + 'px';\n\n\t\t// Clear the parent (.slide) height. .removeProperty works in IE9+\n\t\telement.parentNode.style.removeProperty('height');\n\n\t\treturn newHeight;\n\t}\n\n\treturn height;\n\n}\n\nconst fileExtensionToMimeMap = {\n\t'mp4': 'video/mp4',\n\t'm4a': 'video/mp4',\n\t'ogv': 'video/ogg',\n\t'mpeg': 'video/mpeg',\n\t'webm': 'video/webm'\n}\n\n/**\n * Guess the MIME type for common file formats.\n */\nexport const getMimeTypeFromFile = ( filename='' ) => {\n\treturn fileExtensionToMimeMap[filename.split('.').pop()]\n}\n\n/**\n * Encodes a string for RFC3986-compliant URL format.\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#encoding_for_rfc3986\n *\n * @param {string} url\n */\nexport const encodeRFC3986URI = ( url='' ) => {\n\treturn encodeURI(url)\n\t .replace(/%5B/g, \"[\")\n\t .replace(/%5D/g, \"]\")\n\t .replace(\n\t\t/[!'()*]/g,\n\t\t(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`\n\t );\n}","const UA = navigator.userAgent;\n\nexport const isMobile = /(iphone|ipod|ipad|android)/gi.test( UA ) ||\n\t\t\t\t\t\t( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS\n\nexport const isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );\n\nexport const isAndroid = /android/gi.test( UA );","/**\n * fitty v2.3.7 - Snugly resizes text to fit its parent container\n * Copyright (c) 2023 Rik Schennink (https://pqina.nl/)\n */\n\nvar e=function(e){if(e){var t=function(e){return[].slice.call(e)},n=0,i=1,r=2,o=3,a=[],l=null,u=\"requestAnimationFrame\"in e?function(){e.cancelAnimationFrame(l),l=e.requestAnimationFrame((function(){return s(a.filter((function(e){return e.dirty&&e.active})))}))}:function(){},c=function(e){return function(){a.forEach((function(t){return t.dirty=e})),u()}},s=function(e){e.filter((function(e){return!e.styleComputed})).forEach((function(e){e.styleComputed=m(e)})),e.filter(y).forEach(v);var t=e.filter(p);t.forEach(d),t.forEach((function(e){v(e),f(e)})),t.forEach(S)},f=function(e){return e.dirty=n},d=function(e){e.availableWidth=e.element.parentNode.clientWidth,e.currentWidth=e.element.scrollWidth,e.previousFontSize=e.currentFontSize,e.currentFontSize=Math.min(Math.max(e.minSize,e.availableWidth/e.currentWidth*e.previousFontSize),e.maxSize),e.whiteSpace=e.multiLine&&e.currentFontSize===e.minSize?\"normal\":\"nowrap\"},p=function(e){return e.dirty!==r||e.dirty===r&&e.element.parentNode.clientWidth!==e.availableWidth},m=function(t){var n=e.getComputedStyle(t.element,null);return t.currentFontSize=parseFloat(n.getPropertyValue(\"font-size\")),t.display=n.getPropertyValue(\"display\"),t.whiteSpace=n.getPropertyValue(\"white-space\"),!0},y=function(e){var t=!1;return!e.preStyleTestCompleted&&(/inline-/.test(e.display)||(t=!0,e.display=\"inline-block\"),\"nowrap\"!==e.whiteSpace&&(t=!0,e.whiteSpace=\"nowrap\"),e.preStyleTestCompleted=!0,t)},v=function(e){e.element.style.whiteSpace=e.whiteSpace,e.element.style.display=e.display,e.element.style.fontSize=e.currentFontSize+\"px\"},S=function(e){e.element.dispatchEvent(new CustomEvent(\"fit\",{detail:{oldValue:e.previousFontSize,newValue:e.currentFontSize,scaleFactor:e.currentFontSize/e.previousFontSize}}))},h=function(e,t){return function(){e.dirty=t,e.active&&u()}},w=function(e){return function(){a=a.filter((function(t){return t.element!==e.element})),e.observeMutations&&e.observer.disconnect(),e.element.style.whiteSpace=e.originalStyle.whiteSpace,e.element.style.display=e.originalStyle.display,e.element.style.fontSize=e.originalStyle.fontSize}},b=function(e){return function(){e.active||(e.active=!0,u())}},z=function(e){return function(){return e.active=!1}},F=function(e){e.observeMutations&&(e.observer=new MutationObserver(h(e,i)),e.observer.observe(e.element,e.observeMutations))},g={minSize:16,maxSize:512,multiLine:!0,observeMutations:\"MutationObserver\"in e&&{subtree:!0,childList:!0,characterData:!0}},W=null,E=function(){e.clearTimeout(W),W=e.setTimeout(c(r),x.observeWindowDelay)},M=[\"resize\",\"orientationchange\"];return Object.defineProperty(x,\"observeWindow\",{set:function(t){var n=\"\".concat(t?\"add\":\"remove\",\"EventListener\");M.forEach((function(t){e[n](t,E)}))}}),x.observeWindow=!0,x.observeWindowDelay=100,x.fitAll=c(o),x}function C(e,t){var n=Object.assign({},g,t),i=e.map((function(e){var t=Object.assign({},n,{element:e,active:!0});return function(e){e.originalStyle={whiteSpace:e.element.style.whiteSpace,display:e.element.style.display,fontSize:e.element.style.fontSize},F(e),e.newbie=!0,e.dirty=!0,a.push(e)}(t),{element:e,fit:h(t,o),unfreeze:b(t),freeze:z(t),unsubscribe:w(t)}}));return u(),i}function x(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return\"string\"==typeof e?C(t(document.querySelectorAll(e)),n):C([e],n)[0]}}(\"undefined\"==typeof window?null:window);export default e;\n","import { extend, queryAll, closest, getMimeTypeFromFile, encodeRFC3986URI } from '../utils/util.js'\nimport { isMobile } from '../utils/device.js'\n\nimport fitty from 'fitty';\n\n/**\n * Handles loading, unloading and playback of slide\n * content such as images, videos and iframes.\n */\nexport default class SlideContent {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.startEmbeddedIframe = this.startEmbeddedIframe.bind( this );\n\n\t}\n\n\t/**\n\t * Should the given element be preloaded?\n\t * Decides based on local element attributes and global config.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tshouldPreload( element ) {\n\n\t\tif( this.Reveal.isScrollView() ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Prefer an explicit global preload setting\n\t\tlet preload = this.Reveal.getConfig().preloadIframes;\n\n\t\t// If no global setting is available, fall back on the element's\n\t\t// own preload setting\n\t\tif( typeof preload !== 'boolean' ) {\n\t\t\tpreload = element.hasAttribute( 'data-preload' );\n\t\t}\n\n\t\treturn preload;\n\t}\n\n\t/**\n\t * Called when the given slide is within the configured view\n\t * distance. Shows the slide element and loads any content\n\t * that is set to load lazily (data-src).\n\t *\n\t * @param {HTMLElement} slide Slide to show\n\t */\n\tload( slide, options = {} ) {\n\n\t\t// Show the slide element\n\t\tslide.style.display = this.Reveal.getConfig().display;\n\n\t\t// Media elements with data-src attributes\n\t\tqueryAll( slide, 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ).forEach( element => {\n\t\t\tif( element.tagName !== 'IFRAME' || this.shouldPreload( element ) ) {\n\t\t\t\telement.setAttribute( 'src', element.getAttribute( 'data-src' ) );\n\t\t\t\telement.setAttribute( 'data-lazy-loaded', '' );\n\t\t\t\telement.removeAttribute( 'data-src' );\n\t\t\t}\n\t\t} );\n\n\t\t// Media elements with children\n\t\tqueryAll( slide, 'video, audio' ).forEach( media => {\n\t\t\tlet sources = 0;\n\n\t\t\tqueryAll( media, 'source[data-src]' ).forEach( source => {\n\t\t\t\tsource.setAttribute( 'src', source.getAttribute( 'data-src' ) );\n\t\t\t\tsource.removeAttribute( 'data-src' );\n\t\t\t\tsource.setAttribute( 'data-lazy-loaded', '' );\n\t\t\t\tsources += 1;\n\t\t\t} );\n\n\t\t\t// Enable inline video playback in mobile Safari\n\t\t\tif( isMobile && media.tagName === 'VIDEO' ) {\n\t\t\t\tmedia.setAttribute( 'playsinline', '' );\n\t\t\t}\n\n\t\t\t// If we rewrote sources for this video/audio element, we need\n\t\t\t// to manually tell it to load from its new origin\n\t\t\tif( sources > 0 ) {\n\t\t\t\tmedia.load();\n\t\t\t}\n\t\t} );\n\n\n\t\t// Show the corresponding background element\n\t\tlet background = slide.slideBackgroundElement;\n\t\tif( background ) {\n\t\t\tbackground.style.display = 'block';\n\n\t\t\tlet backgroundContent = slide.slideBackgroundContentElement;\n\t\t\tlet backgroundIframe = slide.getAttribute( 'data-background-iframe' );\n\n\t\t\t// If the background contains media, load it\n\t\t\tif( background.hasAttribute( 'data-loaded' ) === false ) {\n\t\t\t\tbackground.setAttribute( 'data-loaded', 'true' );\n\n\t\t\t\tlet backgroundImage = slide.getAttribute( 'data-background-image' ),\n\t\t\t\t\tbackgroundVideo = slide.getAttribute( 'data-background-video' ),\n\t\t\t\t\tbackgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ),\n\t\t\t\t\tbackgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' );\n\n\t\t\t\t// Images\n\t\t\t\tif( backgroundImage ) {\n\t\t\t\t\t// base64\n\t\t\t\t\tif( /^data:/.test( backgroundImage.trim() ) ) {\n\t\t\t\t\t\tbackgroundContent.style.backgroundImage = `url(${backgroundImage.trim()})`;\n\t\t\t\t\t}\n\t\t\t\t\t// URL(s)\n\t\t\t\t\telse {\n\t\t\t\t\t\tbackgroundContent.style.backgroundImage = backgroundImage.split( ',' ).map( background => {\n\t\t\t\t\t\t\t// Decode URL(s) that are already encoded first\n\t\t\t\t\t\t\tlet decoded = decodeURI(background.trim());\n\t\t\t\t\t\t\treturn `url(${encodeRFC3986URI(decoded)})`;\n\t\t\t\t\t\t}).join( ',' );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Videos\n\t\t\t\telse if ( backgroundVideo && !this.Reveal.isSpeakerNotes() ) {\n\t\t\t\t\tlet video = document.createElement( 'video' );\n\n\t\t\t\t\tif( backgroundVideoLoop ) {\n\t\t\t\t\t\tvideo.setAttribute( 'loop', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\tif( backgroundVideoMuted ) {\n\t\t\t\t\t\tvideo.muted = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Enable inline playback in mobile Safari\n\t\t\t\t\t//\n\t\t\t\t\t// Mute is required for video to play when using\n\t\t\t\t\t// swipe gestures to navigate since they don't\n\t\t\t\t\t// count as direct user actions :'(\n\t\t\t\t\tif( isMobile ) {\n\t\t\t\t\t\tvideo.muted = true;\n\t\t\t\t\t\tvideo.setAttribute( 'playsinline', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support comma separated lists of video sources\n\t\t\t\t\tbackgroundVideo.split( ',' ).forEach( source => {\n\t\t\t\t\t\tconst sourceElement = document.createElement( 'source' );\n\t\t\t\t\t\tsourceElement.setAttribute( 'src', source );\n\n\t\t\t\t\t\tlet type = getMimeTypeFromFile( source );\n\t\t\t\t\t\tif( type ) {\n\t\t\t\t\t\t\tsourceElement.setAttribute( 'type', type );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvideo.appendChild( sourceElement );\n\t\t\t\t\t} );\n\n\t\t\t\t\tbackgroundContent.appendChild( video );\n\t\t\t\t}\n\t\t\t\t// Iframes\n\t\t\t\telse if( backgroundIframe && options.excludeIframes !== true ) {\n\t\t\t\t\tlet iframe = document.createElement( 'iframe' );\n\t\t\t\t\tiframe.setAttribute( 'allowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'mozallowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'webkitallowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'allow', 'autoplay' );\n\n\t\t\t\t\tiframe.setAttribute( 'data-src', backgroundIframe );\n\n\t\t\t\t\tiframe.style.width = '100%';\n\t\t\t\t\tiframe.style.height = '100%';\n\t\t\t\t\tiframe.style.maxHeight = '100%';\n\t\t\t\t\tiframe.style.maxWidth = '100%';\n\n\t\t\t\t\tbackgroundContent.appendChild( iframe );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start loading preloadable iframes\n\t\t\tlet backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' );\n\t\t\tif( backgroundIframeElement ) {\n\n\t\t\t\t// Check if this iframe is eligible to be preloaded\n\t\t\t\tif( this.shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {\n\t\t\t\t\tif( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) {\n\t\t\t\t\t\tbackgroundIframeElement.setAttribute( 'src', backgroundIframe );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.layout( slide );\n\n\t}\n\n\t/**\n\t * Applies JS-dependent layout helpers for the scope.\n\t */\n\tlayout( scopeElement ) {\n\n\t\t// Autosize text with the r-fit-text class based on the\n\t\t// size of its container. This needs to happen after the\n\t\t// slide is visible in order to measure the text.\n\t\tArray.from( scopeElement.querySelectorAll( '.r-fit-text' ) ).forEach( element => {\n\t\t\tfitty( element, {\n\t\t\t\tminSize: 24,\n\t\t\t\tmaxSize: this.Reveal.getConfig().height * 0.8,\n\t\t\t\tobserveMutations: false,\n\t\t\t\tobserveWindow: false\n\t\t\t} );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Unloads and hides the given slide. This is called when the\n\t * slide is moved outside of the configured view distance.\n\t *\n\t * @param {HTMLElement} slide\n\t */\n\tunload( slide ) {\n\n\t\t// Hide the slide element\n\t\tslide.style.display = 'none';\n\n\t\t// Hide the corresponding background element\n\t\tlet background = this.Reveal.getSlideBackground( slide );\n\t\tif( background ) {\n\t\t\tbackground.style.display = 'none';\n\n\t\t\t// Unload any background iframes\n\t\t\tqueryAll( background, 'iframe[src]' ).forEach( element => {\n\t\t\t\telement.removeAttribute( 'src' );\n\t\t\t} );\n\t\t}\n\n\t\t// Reset lazy-loaded media elements with src attributes\n\t\tqueryAll( slide, 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ).forEach( element => {\n\t\t\telement.setAttribute( 'data-src', element.getAttribute( 'src' ) );\n\t\t\telement.removeAttribute( 'src' );\n\t\t} );\n\n\t\t// Reset lazy-loaded media elements with children\n\t\tqueryAll( slide, 'video[data-lazy-loaded] source[src], audio source[src]' ).forEach( source => {\n\t\t\tsource.setAttribute( 'data-src', source.getAttribute( 'src' ) );\n\t\t\tsource.removeAttribute( 'src' );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Enforces origin-specific format rules for embedded media.\n\t */\n\tformatEmbeddedContent() {\n\n\t\tlet _appendParamToIframeSource = ( sourceAttribute, sourceURL, param ) => {\n\t\t\tqueryAll( this.Reveal.getSlidesElement(), 'iframe['+ sourceAttribute +'*=\"'+ sourceURL +'\"]' ).forEach( el => {\n\t\t\t\tlet src = el.getAttribute( sourceAttribute );\n\t\t\t\tif( src && src.indexOf( param ) === -1 ) {\n\t\t\t\t\tel.setAttribute( sourceAttribute, src + ( !/\\?/.test( src ) ? '?' : '&' ) + param );\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\t// YouTube frames must include \"?enablejsapi=1\"\n\t\t_appendParamToIframeSource( 'src', 'youtube.com/embed/', 'enablejsapi=1' );\n\t\t_appendParamToIframeSource( 'data-src', 'youtube.com/embed/', 'enablejsapi=1' );\n\n\t\t// Vimeo frames must include \"?api=1\"\n\t\t_appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' );\n\t\t_appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' );\n\n\t}\n\n\t/**\n\t * Start playback of any embedded content inside of\n\t * the given element.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tstartEmbeddedContent( element ) {\n\n\t\tif( element && !this.Reveal.isSpeakerNotes() ) {\n\n\t\t\t// Restart GIFs\n\t\t\tqueryAll( element, 'img[src$=\".gif\"]' ).forEach( el => {\n\t\t\t\t// Setting the same unchanged source like this was confirmed\n\t\t\t\t// to work in Chrome, FF & Safari\n\t\t\t\tel.setAttribute( 'src', el.getAttribute( 'src' ) );\n\t\t\t} );\n\n\t\t\t// HTML5 media elements\n\t\t\tqueryAll( element, 'video, audio' ).forEach( el => {\n\t\t\t\tif( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Prefer an explicit global autoplay setting\n\t\t\t\tlet autoplay = this.Reveal.getConfig().autoPlayMedia;\n\n\t\t\t\t// If no global setting is available, fall back on the element's\n\t\t\t\t// own autoplay setting\n\t\t\t\tif( typeof autoplay !== 'boolean' ) {\n\t\t\t\t\tautoplay = el.hasAttribute( 'data-autoplay' ) || !!closest( el, '.slide-background' );\n\t\t\t\t}\n\n\t\t\t\tif( autoplay && typeof el.play === 'function' ) {\n\n\t\t\t\t\t// If the media is ready, start playback\n\t\t\t\t\tif( el.readyState > 1 ) {\n\t\t\t\t\t\tthis.startEmbeddedMedia( { target: el } );\n\t\t\t\t\t}\n\t\t\t\t\t// Mobile devices never fire a loaded event so instead\n\t\t\t\t\t// of waiting, we initiate playback\n\t\t\t\t\telse if( isMobile ) {\n\t\t\t\t\t\tlet promise = el.play();\n\n\t\t\t\t\t\t// If autoplay does not work, ensure that the controls are visible so\n\t\t\t\t\t\t// that the viewer can start the media on their own\n\t\t\t\t\t\tif( promise && typeof promise.catch === 'function' && el.controls === false ) {\n\t\t\t\t\t\t\tpromise.catch( () => {\n\t\t\t\t\t\t\t\tel.controls = true;\n\n\t\t\t\t\t\t\t\t// Once the video does start playing, hide the controls again\n\t\t\t\t\t\t\t\tel.addEventListener( 'play', () => {\n\t\t\t\t\t\t\t\t\tel.controls = false;\n\t\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// If the media isn't loaded, wait before playing\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.removeEventListener( 'loadeddata', this.startEmbeddedMedia ); // remove first to avoid dupes\n\t\t\t\t\t\tel.addEventListener( 'loadeddata', this.startEmbeddedMedia );\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Normal iframes\n\t\t\tqueryAll( element, 'iframe[src]' ).forEach( el => {\n\t\t\t\tif( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.startEmbeddedIframe( { target: el } );\n\t\t\t} );\n\n\t\t\t// Lazy loading iframes\n\t\t\tqueryAll( element, 'iframe[data-src]' ).forEach( el => {\n\t\t\t\tif( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) {\n\t\t\t\t\tel.removeEventListener( 'load', this.startEmbeddedIframe ); // remove first to avoid dupes\n\t\t\t\t\tel.addEventListener( 'load', this.startEmbeddedIframe );\n\t\t\t\t\tel.setAttribute( 'src', el.getAttribute( 'data-src' ) );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Starts playing an embedded video/audio element after\n\t * it has finished loading.\n\t *\n\t * @param {object} event\n\t */\n\tstartEmbeddedMedia( event ) {\n\n\t\tlet isAttachedToDOM = !!closest( event.target, 'html' ),\n\t\t\tisVisible \t\t= !!closest( event.target, '.present' );\n\n\t\tif( isAttachedToDOM && isVisible ) {\n\t\t\t// Don't restart if media is already playing\n\t\t\tif( event.target.paused || event.target.ended ) {\n\t\t\t\tevent.target.currentTime = 0;\n\t\t\t\tevent.target.play();\n\t\t\t}\n\t\t}\n\n\t\tevent.target.removeEventListener( 'loadeddata', this.startEmbeddedMedia );\n\n\t}\n\n\t/**\n\t * \"Starts\" the content of an embedded iframe using the\n\t * postMessage API.\n\t *\n\t * @param {object} event\n\t */\n\tstartEmbeddedIframe( event ) {\n\n\t\tlet iframe = event.target;\n\n\t\tif( iframe && iframe.contentWindow ) {\n\n\t\t\tlet isAttachedToDOM = !!closest( event.target, 'html' ),\n\t\t\t\tisVisible \t\t= !!closest( event.target, '.present' );\n\n\t\t\tif( isAttachedToDOM && isVisible ) {\n\n\t\t\t\t// Prefer an explicit global autoplay setting\n\t\t\t\tlet autoplay = this.Reveal.getConfig().autoPlayMedia;\n\n\t\t\t\t// If no global setting is available, fall back on the element's\n\t\t\t\t// own autoplay setting\n\t\t\t\tif( typeof autoplay !== 'boolean' ) {\n\t\t\t\t\tautoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closest( iframe, '.slide-background' );\n\t\t\t\t}\n\n\t\t\t\t// YouTube postMessage API\n\t\t\t\tif( /youtube\\.com\\/embed\\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {\n\t\t\t\t\tiframe.contentWindow.postMessage( '{\"event\":\"command\",\"func\":\"playVideo\",\"args\":\"\"}', '*' );\n\t\t\t\t}\n\t\t\t\t// Vimeo postMessage API\n\t\t\t\telse if( /player\\.vimeo\\.com\\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {\n\t\t\t\t\tiframe.contentWindow.postMessage( '{\"method\":\"play\"}', '*' );\n\t\t\t\t}\n\t\t\t\t// Generic postMessage API\n\t\t\t\telse {\n\t\t\t\t\tiframe.contentWindow.postMessage( 'slide:start', '*' );\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Stop playback of any embedded content inside of\n\t * the targeted slide.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tstopEmbeddedContent( element, options = {} ) {\n\n\t\toptions = extend( {\n\t\t\t// Defaults\n\t\t\tunloadIframes: true\n\t\t}, options );\n\n\t\tif( element && element.parentNode ) {\n\t\t\t// HTML5 media elements\n\t\t\tqueryAll( element, 'video, audio' ).forEach( el => {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) {\n\t\t\t\t\tel.setAttribute('data-paused-by-reveal', '');\n\t\t\t\t\tel.pause();\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Generic postMessage API for non-lazy loaded iframes\n\t\t\tqueryAll( element, 'iframe' ).forEach( el => {\n\t\t\t\tif( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' );\n\t\t\t\tel.removeEventListener( 'load', this.startEmbeddedIframe );\n\t\t\t});\n\n\t\t\t// YouTube postMessage API\n\t\t\tqueryAll( element, 'iframe[src*=\"youtube.com/embed/\"]' ).forEach( el => {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {\n\t\t\t\t\tel.contentWindow.postMessage( '{\"event\":\"command\",\"func\":\"pauseVideo\",\"args\":\"\"}', '*' );\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Vimeo postMessage API\n\t\t\tqueryAll( element, 'iframe[src*=\"player.vimeo.com/\"]' ).forEach( el => {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {\n\t\t\t\t\tel.contentWindow.postMessage( '{\"method\":\"pause\"}', '*' );\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif( options.unloadIframes === true ) {\n\t\t\t\t// Unload lazy-loaded iframes\n\t\t\t\tqueryAll( element, 'iframe[data-src]' ).forEach( el => {\n\t\t\t\t\t// Only removing the src doesn't actually unload the frame\n\t\t\t\t\t// in all browsers (Firefox) so we set it to blank first\n\t\t\t\t\tel.setAttribute( 'src', 'about:blank' );\n\t\t\t\t\tel.removeAttribute( 'src' );\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n","\nexport const SLIDES_SELECTOR = '.slides section';\nexport const HORIZONTAL_SLIDES_SELECTOR = '.slides>section';\nexport const VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section';\nexport const HORIZONTAL_BACKGROUNDS_SELECTOR = '.backgrounds>.slide-background';\n\n// Methods that may not be invoked via the postMessage API\nexport const POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener|showPreview/;\n\n// Regex for retrieving the fragment style from a class attribute\nexport const FRAGMENT_STYLE_REGEX = /fade-(down|up|right|left|out|in-then-out|in-then-semi-out)|semi-fade-out|current-visible|shrink|grow/;\n\n// Slide number formats\nexport const SLIDE_NUMBER_FORMAT_HORIZONTAL_DOT_VERTICAL = 'h.v';\nexport const SLIDE_NUMBER_FORMAT_HORIZONTAL_SLASH_VERTICAL = 'h/v';\nexport const SLIDE_NUMBER_FORMAT_CURRENT = 'c';\nexport const SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL = 'c/t';","import {\n\tSLIDE_NUMBER_FORMAT_CURRENT,\n\tSLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL,\n\tSLIDE_NUMBER_FORMAT_HORIZONTAL_DOT_VERTICAL,\n\tSLIDE_NUMBER_FORMAT_HORIZONTAL_SLASH_VERTICAL\n} from \"../utils/constants\";\n\n/**\n * Handles the display of reveal.js' optional slide number.\n */\nexport default class SlideNumber {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'slide-number';\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tlet slideNumberDisplay = 'none';\n\t\tif( config.slideNumber && !this.Reveal.isPrintView() ) {\n\t\t\tif( config.showSlideNumber === 'all' ) {\n\t\t\t\tslideNumberDisplay = 'block';\n\t\t\t}\n\t\t\telse if( config.showSlideNumber === 'speaker' && this.Reveal.isSpeakerNotes() ) {\n\t\t\t\tslideNumberDisplay = 'block';\n\t\t\t}\n\t\t}\n\n\t\tthis.element.style.display = slideNumberDisplay;\n\n\t}\n\n\t/**\n\t * Updates the slide number to match the current slide.\n\t */\n\tupdate() {\n\n\t\t// Update slide number if enabled\n\t\tif( this.Reveal.getConfig().slideNumber && this.element ) {\n\t\t\tthis.element.innerHTML = this.getSlideNumber();\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns the HTML string corresponding to the current slide\n\t * number, including formatting.\n\t */\n\tgetSlideNumber( slide = this.Reveal.getCurrentSlide() ) {\n\n\t\tlet config = this.Reveal.getConfig();\n\t\tlet value;\n\t\tlet format = SLIDE_NUMBER_FORMAT_HORIZONTAL_DOT_VERTICAL;\n\n\t\tif ( typeof config.slideNumber === 'function' ) {\n\t\t\tvalue = config.slideNumber( slide );\n\t\t} else {\n\t\t\t// Check if a custom number format is available\n\t\t\tif( typeof config.slideNumber === 'string' ) {\n\t\t\t\tformat = config.slideNumber;\n\t\t\t}\n\n\t\t\t// If there are ONLY vertical slides in this deck, always use\n\t\t\t// a flattened slide number\n\t\t\tif( !/c/.test( format ) && this.Reveal.getHorizontalSlides().length === 1 ) {\n\t\t\t\tformat = SLIDE_NUMBER_FORMAT_CURRENT;\n\t\t\t}\n\n\t\t\t// Offset the current slide number by 1 to make it 1-indexed\n\t\t\tlet horizontalOffset = slide && slide.dataset.visibility === 'uncounted' ? 0 : 1;\n\n\t\t\tvalue = [];\n\t\t\tswitch( format ) {\n\t\t\t\tcase SLIDE_NUMBER_FORMAT_CURRENT:\n\t\t\t\t\tvalue.push( this.Reveal.getSlidePastCount( slide ) + horizontalOffset );\n\t\t\t\t\tbreak;\n\t\t\t\tcase SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL:\n\t\t\t\t\tvalue.push( this.Reveal.getSlidePastCount( slide ) + horizontalOffset, '/', this.Reveal.getTotalSlides() );\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tlet indices = this.Reveal.getIndices( slide );\n\t\t\t\t\tvalue.push( indices.h + horizontalOffset );\n\t\t\t\t\tlet sep = format === SLIDE_NUMBER_FORMAT_HORIZONTAL_SLASH_VERTICAL ? '/' : '.';\n\t\t\t\t\tif( this.Reveal.isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );\n\t\t\t}\n\t\t}\n\n\t\tlet url = '#' + this.Reveal.location.getHash( slide );\n\t\treturn this.formatNumber( value[0], value[1], value[2], url );\n\n\t}\n\n\t/**\n\t * Applies HTML formatting to a slide number before it's\n\t * written to the DOM.\n\t *\n\t * @param {number} a Current slide\n\t * @param {string} delimiter Character to separate slide numbers\n\t * @param {(number|*)} b Total slides\n\t * @param {HTMLElement} [url='#'+locationHash()] The url to link to\n\t * @return {string} HTML string fragment\n\t */\n\tformatNumber( a, delimiter, b, url = '#' + this.Reveal.location.getHash() ) {\n\n\t\tif( typeof b === 'number' && !isNaN( b ) ) {\n\t\t\treturn `\n\t\t\t\t\t${a}\n\t\t\t\t\t${delimiter}\n\t\t\t\t\t${b}\n\t\t\t\t\t`;\n\t\t}\n\t\telse {\n\t\t\treturn `\n\t\t\t\t\t${a}\n\t\t\t\t\t`;\n\t\t}\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.element.remove();\n\n\t}\n\n}","import {\n\tSLIDE_NUMBER_FORMAT_CURRENT,\n\tSLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL\n} from \"../utils/constants\";\n\n/**\n * Makes it possible to jump to a slide by entering its\n * slide number or id.\n */\nexport default class JumpToSlide {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.onInput = this.onInput.bind( this );\n\t\tthis.onBlur = this.onBlur.bind( this );\n\t\tthis.onKeyDown = this.onKeyDown.bind( this );\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'jump-to-slide';\n\n this.jumpInput = document.createElement( 'input' );\n this.jumpInput.type = 'text';\n this.jumpInput.className = 'jump-to-slide-input';\n this.jumpInput.placeholder = 'Jump to slide';\n\t\tthis.jumpInput.addEventListener( 'input', this.onInput );\n\t\tthis.jumpInput.addEventListener( 'keydown', this.onKeyDown );\n\t\tthis.jumpInput.addEventListener( 'blur', this.onBlur );\n\n this.element.appendChild( this.jumpInput );\n\n\t}\n\n\tshow() {\n\n\t\tthis.indicesOnShow = this.Reveal.getIndices();\n\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\t\tthis.jumpInput.focus();\n\n\t}\n\n\thide() {\n\n\t\tif( this.isVisible() ) {\n\t\t\tthis.element.remove();\n\t\t\tthis.jumpInput.value = '';\n\n\t\t\tclearTimeout( this.jumpTimeout );\n\t\t\tdelete this.jumpTimeout;\n\t\t}\n\n\t}\n\n\tisVisible() {\n\n\t\treturn !!this.element.parentNode;\n\n\t}\n\n\t/**\n\t * Parses the current input and jumps to the given slide.\n\t */\n\tjump() {\n\n\t\tclearTimeout( this.jumpTimeout );\n\t\tdelete this.jumpTimeout;\n\n\t\tlet query = this.jumpInput.value.trim( '' );\n\t\tlet indices;\n\n\t\t// When slide numbers are formatted to be a single linear mumber\n\t\t// (instead of showing a separate horizontal/vertical index) we\n\t\t// use the same format for slide jumps\n\t\tif( /^\\d+$/.test( query ) ) {\n\t\t\tconst slideNumberFormat = this.Reveal.getConfig().slideNumber;\n\t\t\tif( slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT || slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL ) {\n\t\t\t\tconst slide = this.Reveal.getSlides()[ parseInt( query, 10 ) - 1 ];\n\t\t\t\tif( slide ) {\n\t\t\t\t\tindices = this.Reveal.getIndices( slide );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif( !indices ) {\n\t\t\t// If the query uses \"horizontal.vertical\" format, convert to\n\t\t\t// \"horizontal/vertical\" so that our URL parser can understand\n\t\t\tif( /^\\d+\\.\\d+$/.test( query ) ) {\n\t\t\t\tquery = query.replace( '.', '/' );\n\t\t\t}\n\n\t\t\tindices = this.Reveal.location.getIndicesFromHash( query, { oneBasedIndex: true } );\n\t\t}\n\n\t\t// Still no valid index? Fall back on a text search\n\t\tif( !indices && /\\S+/i.test( query ) && query.length > 1 ) {\n\t\t\tindices = this.search( query );\n\t\t}\n\n\t\tif( indices && query !== '' ) {\n\t\t\tthis.Reveal.slide( indices.h, indices.v, indices.f );\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\tthis.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\tjumpAfter( delay ) {\n\n\t\tclearTimeout( this.jumpTimeout );\n\t\tthis.jumpTimeout = setTimeout( () => this.jump(), delay );\n\n\t}\n\n\t/**\n\t * A lofi search that looks for the given query in all\n\t * of our slides and returns the first match.\n\t */\n\tsearch( query ) {\n\n\t\tconst regex = new RegExp( '\\\\b' + query.trim() + '\\\\b', 'i' );\n\n\t\tconst slide = this.Reveal.getSlides().find( ( slide ) => {\n\t\t\treturn regex.test( slide.innerText );\n\t\t} );\n\n\t\tif( slide ) {\n\t\t\treturn this.Reveal.getIndices( slide );\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\t/**\n\t * Reverts back to the slide we were on when jump to slide was\n\t * invoked.\n\t */\n\tcancel() {\n\n\t\tthis.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );\n\t\tthis.hide();\n\n\t}\n\n\tconfirm() {\n\n\t\tthis.jump();\n\t\tthis.hide();\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.jumpInput.removeEventListener( 'input', this.onInput );\n\t\tthis.jumpInput.removeEventListener( 'keydown', this.onKeyDown );\n\t\tthis.jumpInput.removeEventListener( 'blur', this.onBlur );\n\n\t\tthis.element.remove();\n\n\t}\n\n\tonKeyDown( event ) {\n\n\t\tif( event.keyCode === 13 ) {\n\t\t\tthis.confirm();\n\t\t}\n\t\telse if( event.keyCode === 27 ) {\n\t\t\tthis.cancel();\n\n\t\t\tevent.stopImmediatePropagation();\n\t\t}\n\n\t}\n\n\tonInput( event ) {\n\n\t\tthis.jumpAfter( 200 );\n\n\t}\n\n\tonBlur() {\n\n\t\tsetTimeout( () => this.hide(), 1 );\n\n\t}\n\n}","/**\n * Converts various color input formats to an {r:0,g:0,b:0} object.\n *\n * @param {string} color The string representation of a color\n * @example\n * colorToRgb('#000');\n * @example\n * colorToRgb('#000000');\n * @example\n * colorToRgb('rgb(0,0,0)');\n * @example\n * colorToRgb('rgba(0,0,0)');\n *\n * @return {{r: number, g: number, b: number, [a]: number}|null}\n */\nexport const colorToRgb = ( color ) => {\n\n\tlet hex3 = color.match( /^#([0-9a-f]{3})$/i );\n\tif( hex3 && hex3[1] ) {\n\t\thex3 = hex3[1];\n\t\treturn {\n\t\t\tr: parseInt( hex3.charAt( 0 ), 16 ) * 0x11,\n\t\t\tg: parseInt( hex3.charAt( 1 ), 16 ) * 0x11,\n\t\t\tb: parseInt( hex3.charAt( 2 ), 16 ) * 0x11\n\t\t};\n\t}\n\n\tlet hex6 = color.match( /^#([0-9a-f]{6})$/i );\n\tif( hex6 && hex6[1] ) {\n\t\thex6 = hex6[1];\n\t\treturn {\n\t\t\tr: parseInt( hex6.slice( 0, 2 ), 16 ),\n\t\t\tg: parseInt( hex6.slice( 2, 4 ), 16 ),\n\t\t\tb: parseInt( hex6.slice( 4, 6 ), 16 )\n\t\t};\n\t}\n\n\tlet rgb = color.match( /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i );\n\tif( rgb ) {\n\t\treturn {\n\t\t\tr: parseInt( rgb[1], 10 ),\n\t\t\tg: parseInt( rgb[2], 10 ),\n\t\t\tb: parseInt( rgb[3], 10 )\n\t\t};\n\t}\n\n\tlet rgba = color.match( /^rgba\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\,\\s*([\\d]+|[\\d]*.[\\d]+)\\s*\\)$/i );\n\tif( rgba ) {\n\t\treturn {\n\t\t\tr: parseInt( rgba[1], 10 ),\n\t\t\tg: parseInt( rgba[2], 10 ),\n\t\t\tb: parseInt( rgba[3], 10 ),\n\t\t\ta: parseFloat( rgba[4] )\n\t\t};\n\t}\n\n\treturn null;\n\n}\n\n/**\n * Calculates brightness on a scale of 0-255.\n *\n * @param {string} color See colorToRgb for supported formats.\n * @see {@link colorToRgb}\n */\nexport const colorBrightness = ( color ) => {\n\n\tif( typeof color === 'string' ) color = colorToRgb( color );\n\n\tif( color ) {\n\t\treturn ( color.r * 299 + color.g * 587 + color.b * 114 ) / 1000;\n\t}\n\n\treturn null;\n\n}","import { queryAll } from '../utils/util.js'\nimport { colorToRgb, colorBrightness } from '../utils/color.js'\n\n/**\n * Creates and updates slide backgrounds.\n */\nexport default class Backgrounds {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'backgrounds';\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t}\n\n\t/**\n\t * Creates the slide background elements and appends them\n\t * to the background container. One element is created per\n\t * slide no matter if the given slide has visible background.\n\t */\n\tcreate() {\n\n\t\t// Clear prior backgrounds\n\t\tthis.element.innerHTML = '';\n\t\tthis.element.classList.add( 'no-transition' );\n\n\t\t// Iterate over all horizontal slides\n\t\tthis.Reveal.getHorizontalSlides().forEach( slideh => {\n\n\t\t\tlet backgroundStack = this.createBackground( slideh, this.element );\n\n\t\t\t// Iterate over all vertical slides\n\t\t\tqueryAll( slideh, 'section' ).forEach( slidev => {\n\n\t\t\t\tthis.createBackground( slidev, backgroundStack );\n\n\t\t\t\tbackgroundStack.classList.add( 'stack' );\n\n\t\t\t} );\n\n\t\t} );\n\n\t\t// Add parallax background if specified\n\t\tif( this.Reveal.getConfig().parallaxBackgroundImage ) {\n\n\t\t\tthis.element.style.backgroundImage = 'url(\"' + this.Reveal.getConfig().parallaxBackgroundImage + '\")';\n\t\t\tthis.element.style.backgroundSize = this.Reveal.getConfig().parallaxBackgroundSize;\n\t\t\tthis.element.style.backgroundRepeat = this.Reveal.getConfig().parallaxBackgroundRepeat;\n\t\t\tthis.element.style.backgroundPosition = this.Reveal.getConfig().parallaxBackgroundPosition;\n\n\t\t\t// Make sure the below properties are set on the element - these properties are\n\t\t\t// needed for proper transitions to be set on the element via CSS. To remove\n\t\t\t// annoying background slide-in effect when the presentation starts, apply\n\t\t\t// these properties after short time delay\n\t\t\tsetTimeout( () => {\n\t\t\t\tthis.Reveal.getRevealElement().classList.add( 'has-parallax-background' );\n\t\t\t}, 1 );\n\n\t\t}\n\t\telse {\n\n\t\t\tthis.element.style.backgroundImage = '';\n\t\t\tthis.Reveal.getRevealElement().classList.remove( 'has-parallax-background' );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a background for the given slide.\n\t *\n\t * @param {HTMLElement} slide\n\t * @param {HTMLElement} container The element that the background\n\t * should be appended to\n\t * @return {HTMLElement} New background div\n\t */\n\tcreateBackground( slide, container ) {\n\n\t\t// Main slide background element\n\t\tlet element = document.createElement( 'div' );\n\t\telement.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' );\n\n\t\t// Inner background element that wraps images/videos/iframes\n\t\tlet contentElement = document.createElement( 'div' );\n\t\tcontentElement.className = 'slide-background-content';\n\n\t\telement.appendChild( contentElement );\n\t\tcontainer.appendChild( element );\n\n\t\tslide.slideBackgroundElement = element;\n\t\tslide.slideBackgroundContentElement = contentElement;\n\n\t\t// Syncs the background to reflect all current background settings\n\t\tthis.sync( slide );\n\n\t\treturn element;\n\n\t}\n\n\t/**\n\t * Renders all of the visual properties of a slide background\n\t * based on the various background attributes.\n\t *\n\t * @param {HTMLElement} slide\n\t */\n\tsync( slide ) {\n\n\t\tconst element = slide.slideBackgroundElement,\n\t\t\tcontentElement = slide.slideBackgroundContentElement;\n\n\t\tconst data = {\n\t\t\tbackground: slide.getAttribute( 'data-background' ),\n\t\t\tbackgroundSize: slide.getAttribute( 'data-background-size' ),\n\t\t\tbackgroundImage: slide.getAttribute( 'data-background-image' ),\n\t\t\tbackgroundVideo: slide.getAttribute( 'data-background-video' ),\n\t\t\tbackgroundIframe: slide.getAttribute( 'data-background-iframe' ),\n\t\t\tbackgroundColor: slide.getAttribute( 'data-background-color' ),\n\t\t\tbackgroundGradient: slide.getAttribute( 'data-background-gradient' ),\n\t\t\tbackgroundRepeat: slide.getAttribute( 'data-background-repeat' ),\n\t\t\tbackgroundPosition: slide.getAttribute( 'data-background-position' ),\n\t\t\tbackgroundTransition: slide.getAttribute( 'data-background-transition' ),\n\t\t\tbackgroundOpacity: slide.getAttribute( 'data-background-opacity' ),\n\t\t};\n\n\t\tconst dataPreload = slide.hasAttribute( 'data-preload' );\n\n\t\t// Reset the prior background state in case this is not the\n\t\t// initial sync\n\t\tslide.classList.remove( 'has-dark-background' );\n\t\tslide.classList.remove( 'has-light-background' );\n\n\t\telement.removeAttribute( 'data-loaded' );\n\t\telement.removeAttribute( 'data-background-hash' );\n\t\telement.removeAttribute( 'data-background-size' );\n\t\telement.removeAttribute( 'data-background-transition' );\n\t\telement.style.backgroundColor = '';\n\n\t\tcontentElement.style.backgroundSize = '';\n\t\tcontentElement.style.backgroundRepeat = '';\n\t\tcontentElement.style.backgroundPosition = '';\n\t\tcontentElement.style.backgroundImage = '';\n\t\tcontentElement.style.opacity = '';\n\t\tcontentElement.innerHTML = '';\n\n\t\tif( data.background ) {\n\t\t\t// Auto-wrap image urls in url(...)\n\t\t\tif( /^(http|file|\\/\\/)/gi.test( data.background ) || /\\.(svg|png|jpg|jpeg|gif|bmp|webp)([?#\\s]|$)/gi.test( data.background ) ) {\n\t\t\t\tslide.setAttribute( 'data-background-image', data.background );\n\t\t\t}\n\t\t\telse {\n\t\t\t\telement.style.background = data.background;\n\t\t\t}\n\t\t}\n\n\t\t// Create a hash for this combination of background settings.\n\t\t// This is used to determine when two slide backgrounds are\n\t\t// the same.\n\t\tif( data.background || data.backgroundColor || data.backgroundGradient || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) {\n\t\t\telement.setAttribute( 'data-background-hash', data.background +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundSize +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundImage +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundVideo +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundIframe +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundColor +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundGradient +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundRepeat +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundPosition +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundTransition +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundOpacity );\n\t\t}\n\n\t\t// Additional and optional background properties\n\t\tif( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize );\n\t\tif( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;\n\t\tif( data.backgroundGradient ) element.style.backgroundImage = data.backgroundGradient;\n\t\tif( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );\n\n\t\tif( dataPreload ) element.setAttribute( 'data-preload', '' );\n\n\t\t// Background image options are set on the content wrapper\n\t\tif( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;\n\t\tif( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat;\n\t\tif( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition;\n\t\tif( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity;\n\n\t\tconst contrastClass = this.getContrastClass( slide );\n\n\t\tif( typeof contrastClass === 'string' ) {\n\t\t\tslide.classList.add( contrastClass );\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns a class name that can be applied to a slide to indicate\n\t * if it has a light or dark background.\n\t *\n\t * @param {*} slide\n\t *\n\t * @returns {string|null}\n\t */\n\tgetContrastClass( slide ) {\n\n\t\tconst element = slide.slideBackgroundElement;\n\n\t\t// If this slide has a background color, we add a class that\n\t\t// signals if it is light or dark. If the slide has no background\n\t\t// color, no class will be added\n\t\tlet contrastColor = slide.getAttribute( 'data-background-color' );\n\n\t\t// If no bg color was found, or it cannot be converted by colorToRgb, check the computed background\n\t\tif( !contrastColor || !colorToRgb( contrastColor ) ) {\n\t\t\tlet computedBackgroundStyle = window.getComputedStyle( element );\n\t\t\tif( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) {\n\t\t\t\tcontrastColor = computedBackgroundStyle.backgroundColor;\n\t\t\t}\n\t\t}\n\n\t\tif( contrastColor ) {\n\t\t\tconst rgb = colorToRgb( contrastColor );\n\n\t\t\t// Ignore fully transparent backgrounds. Some browsers return\n\t\t\t// rgba(0,0,0,0) when reading the computed background color of\n\t\t\t// an element with no background\n\t\t\tif( rgb && rgb.a !== 0 ) {\n\t\t\t\tif( colorBrightness( contrastColor ) < 128 ) {\n\t\t\t\t\treturn 'has-dark-background';\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn 'has-light-background';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t/**\n\t * Bubble the 'has-light-background'/'has-dark-background' classes.\n\t */\n\tbubbleSlideContrastClassToElement( slide, target ) {\n\n\t\t[ 'has-light-background', 'has-dark-background' ].forEach( classToBubble => {\n\t\t\tif( slide.classList.contains( classToBubble ) ) {\n\t\t\t\ttarget.classList.add( classToBubble );\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttarget.classList.remove( classToBubble );\n\t\t\t}\n\t\t}, this );\n\n\t}\n\n\t/**\n\t * Updates the background elements to reflect the current\n\t * slide.\n\t *\n\t * @param {boolean} includeAll If true, the backgrounds of\n\t * all vertical slides (not just the present) will be updated.\n\t */\n\tupdate( includeAll = false ) {\n\n\t\tlet config = this.Reveal.getConfig();\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\t\tlet indices = this.Reveal.getIndices();\n\n\t\tlet currentBackground = null;\n\n\t\t// Reverse past/future classes when in RTL mode\n\t\tlet horizontalPast = config.rtl ? 'future' : 'past',\n\t\t\thorizontalFuture = config.rtl ? 'past' : 'future';\n\n\t\t// Update the classes of all backgrounds to match the\n\t\t// states of their slides (past/present/future)\n\t\tArray.from( this.element.childNodes ).forEach( ( backgroundh, h ) => {\n\n\t\t\tbackgroundh.classList.remove( 'past', 'present', 'future' );\n\n\t\t\tif( h < indices.h ) {\n\t\t\t\tbackgroundh.classList.add( horizontalPast );\n\t\t\t}\n\t\t\telse if ( h > indices.h ) {\n\t\t\t\tbackgroundh.classList.add( horizontalFuture );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbackgroundh.classList.add( 'present' );\n\n\t\t\t\t// Store a reference to the current background element\n\t\t\t\tcurrentBackground = backgroundh;\n\t\t\t}\n\n\t\t\tif( includeAll || h === indices.h ) {\n\t\t\t\tqueryAll( backgroundh, '.slide-background' ).forEach( ( backgroundv, v ) => {\n\n\t\t\t\t\tbackgroundv.classList.remove( 'past', 'present', 'future' );\n\n\t\t\t\t\tconst indexv = typeof indices.v === 'number' ? indices.v : 0;\n\n\t\t\t\t\tif( v < indexv ) {\n\t\t\t\t\t\tbackgroundv.classList.add( 'past' );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( v > indexv ) {\n\t\t\t\t\t\tbackgroundv.classList.add( 'future' );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tbackgroundv.classList.add( 'present' );\n\n\t\t\t\t\t\t// Only if this is the present horizontal and vertical slide\n\t\t\t\t\t\tif( h === indices.h ) currentBackground = backgroundv;\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\t\t\t}\n\n\t\t} );\n\n\t\t// The previous background may refer to a DOM element that has\n\t\t// been removed after a presentation is synced & bgs are recreated\n\t\tif( this.previousBackground && !this.previousBackground.closest( 'body' ) ) {\n\t\t\tthis.previousBackground = null;\n\t\t}\n\n\t\tif( currentBackground && this.previousBackground ) {\n\n\t\t\t// Don't transition between identical backgrounds. This\n\t\t\t// prevents unwanted flicker.\n\t\t\tlet previousBackgroundHash = this.previousBackground.getAttribute( 'data-background-hash' );\n\t\t\tlet currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' );\n\n\t\t\tif( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== this.previousBackground ) {\n\t\t\t\tthis.element.classList.add( 'no-transition' );\n\n\t\t\t\t// If multiple slides have the same background video, carry\n\t\t\t\t// the