From 1be9b69478094c84b2718066d4d2c058e752ae10 Mon Sep 17 00:00:00 2001 From: Adam Legg Date: Sat, 23 May 2026 18:40:24 -0400 Subject: [PATCH 1/8] Add support for IAP plugin --- android/jni/mob_nif.zig | 137 ++++++ ios/mob_nif.m | 163 ++++++++ plugins/mob_iap/.formatter.exs | 3 + plugins/mob_iap/.gitignore | 3 + plugins/mob_iap/README.md | 126 ++++++ plugins/mob_iap/lib/mob_iap.ex | 337 +++++++++++++++ plugins/mob_iap/lib/mob_iap/nif.ex | 19 + plugins/mob_iap/lib/mob_iap/product.ex | 51 +++ .../mob_iap/lib/mob_iap/settings_screen.ex | 118 ++++++ plugins/mob_iap/lib/mob_iap/store_screen.ex | 204 +++++++++ .../lib/mob_iap/subscription_screen.ex | 141 +++++++ plugins/mob_iap/lib/mob_iap/transaction.ex | 45 ++ plugins/mob_iap/mix.exs | 41 ++ plugins/mob_iap/priv/mob_plugin.exs | 43 ++ .../priv/native/android/MobIapBridge.kt | 393 ++++++++++++++++++ plugins/mob_iap/priv/native/android/jni/iap.c | 255 ++++++++++++ .../priv/native/ios/MobIapBridge.swift | 276 ++++++++++++ plugins/mob_iap/test/mob_iap/product_test.exs | 65 +++ .../mob_iap/test/mob_iap/transaction_test.exs | 83 ++++ plugins/mob_iap/test/mob_iap_test.exs | 344 +++++++++++++++ plugins/mob_iap/test/test_helper.exs | 1 + src/mob_nif.erl | 22 +- 22 files changed, 2868 insertions(+), 2 deletions(-) create mode 100644 plugins/mob_iap/.formatter.exs create mode 100644 plugins/mob_iap/.gitignore create mode 100644 plugins/mob_iap/README.md create mode 100644 plugins/mob_iap/lib/mob_iap.ex create mode 100644 plugins/mob_iap/lib/mob_iap/nif.ex create mode 100644 plugins/mob_iap/lib/mob_iap/product.ex create mode 100644 plugins/mob_iap/lib/mob_iap/settings_screen.ex create mode 100644 plugins/mob_iap/lib/mob_iap/store_screen.ex create mode 100644 plugins/mob_iap/lib/mob_iap/subscription_screen.ex create mode 100644 plugins/mob_iap/lib/mob_iap/transaction.ex create mode 100644 plugins/mob_iap/mix.exs create mode 100644 plugins/mob_iap/priv/mob_plugin.exs create mode 100644 plugins/mob_iap/priv/native/android/MobIapBridge.kt create mode 100644 plugins/mob_iap/priv/native/android/jni/iap.c create mode 100644 plugins/mob_iap/priv/native/ios/MobIapBridge.swift create mode 100644 plugins/mob_iap/test/mob_iap/product_test.exs create mode 100644 plugins/mob_iap/test/mob_iap/transaction_test.exs create mode 100644 plugins/mob_iap/test/mob_iap_test.exs create mode 100644 plugins/mob_iap/test/test_helper.exs diff --git a/android/jni/mob_nif.zig b/android/jni/mob_nif.zig index bd48c70..5447707 100644 --- a/android/jni/mob_nif.zig +++ b/android/jni/mob_nif.zig @@ -241,6 +241,9 @@ pub export var Bridge: BridgeMethods = .{}; extern var g_jvm: ?*jni.JavaVM; extern var g_activity: jni.JObject; +// mob_iap plugin init — optional; iap.c short-circuits when plugin absent. +extern fn mob_iap_init(env: *jni.JNIEnv, activity: jni.JObject) callconv(.c) void; + // ── get_jenv: attach the current thread if needed ──────────────────────── // Returns the env pointer; *attached is set to 1 iff this call had to // attach (caller must DetachCurrentThread when done). Match the C @@ -4736,6 +4739,11 @@ fn nifLoad(env: ?*erts.ErlNifEnv, priv: *?*anyopaque, info: erts.ERL_NIF_TERM) c return -1; } + // mob_iap plugin — optional; iap.c short-circuits when class absent. + if (g_activity != null) { + mob_iap_init(jenv, g_activity); + } + g_launch_notif_mutex = erts.enif_mutex_create("mob_launch_notif_mutex"); if (g_launch_notif_mutex == null) { loge_nif("nif_load: failed to create launch notif mutex", .{}); @@ -4760,6 +4768,129 @@ fn nifLoad(env: ?*erts.ErlNifEnv, priv: *?*anyopaque, info: erts.ERL_NIF_TERM) c return 0; } +// ── In-App Purchase NIF stubs (mob_iap plugin) ───────────────────────── +// Thin wrappers that extract the BEAM pid and product IDs, then delegate +// to the JNI bridge in iap.c. The actual StoreKit 2 / Play Billing work +// happens on the JVM/ObjC side. + +// NOTE: iap.c JNI callbacks receive ErlNifPid* via jlong. They MUST +// call free() on that pointer after enif_send completes. +extern fn mob_iap_fetch_products(pid: *erts.ErlNifPid, ids: [*:null]const ?[*:0]const u8, count: c_int) void; +extern fn mob_iap_purchase(pid: *erts.ErlNifPid, product_id: [*:0]const u8) void; +extern fn mob_iap_restore(pid: *erts.ErlNifPid) void; +extern fn mob_iap_current_entitlements(pid: *erts.ErlNifPid) void; +extern fn mob_iap_manage_subscriptions() void; + +fn nif_iap_fetch_products( + env: ?*erts.ErlNifEnv, + _: c_int, + argv: [*]const erts.ERL_NIF_TERM, +) callconv(.c) erts.ERL_NIF_TERM { + var pid: erts.ErlNifPid = undefined; + if (erts.enif_self(env, &pid) == null) { + return erts.badarg(env); + } + + var list_len: c_uint = 0; + if (erts.enif_get_list_length(env, argv[0], &list_len) == 0) { + return erts.badarg(env); + } + + const max = @min(list_len, 128); + var ids: [128]?[*:0]const u8 = @splat(null); + var head: erts.ERL_NIF_TERM = undefined; + var tail: erts.ERL_NIF_TERM = argv[0]; + var buf: [256]u8 = undefined; + + const alloc = std.heap.c_allocator; + for (0..max) |i| { + if (erts.enif_get_list_cell(env, tail, &head, &tail) == 0) { + for (0..i) |j| alloc.free(std.mem.span(ids[j].?)); + return erts.badarg(env); + } + if (!fillBufferFromTerm(env, head, &buf)) { + for (0..i) |j| alloc.free(std.mem.span(ids[j].?)); + return erts.badarg(env); + } + const cstr: [*:0]u8 = @ptrCast(&buf); + const len = std.mem.len(cstr) + 1; + const str = alloc.alloc(u8, len) catch { + for (0..i) |j| alloc.free(std.mem.span(ids[j].?)); + return erts.badarg(env); + }; + @memcpy(str[0..len], buf[0..len]); + ids[i] = @ptrCast(str.ptr); + } + + const pid_ptr = std.heap.c_allocator.alloc(erts.ErlNifPid, 1) catch return erts.badarg(env); + pid_ptr[0] = pid; + mob_iap_fetch_products(pid_ptr, &ids, @intCast(max)); + return erts.atom(env, "ok"); +} + +fn nif_iap_purchase( + env: ?*erts.ErlNifEnv, + _: c_int, + argv: [*]const erts.ERL_NIF_TERM, +) callconv(.c) erts.ERL_NIF_TERM { + var pid: erts.ErlNifPid = undefined; + if (erts.enif_self(env, &pid) == null) { + return erts.badarg(env); + } + + var buf: [4096]u8 = @splat(0); + if (!fillBufferFromTerm(env, argv[0], &buf)) { + return erts.badarg(env); + } + + const pid_ptr = std.heap.c_allocator.alloc(erts.ErlNifPid, 1) catch return erts.badarg(env); + pid_ptr[0] = pid; + mob_iap_purchase(pid_ptr, @ptrCast(&buf)); + return erts.atom(env, "ok"); +} + +fn nif_iap_restore( + env: ?*erts.ErlNifEnv, + _: c_int, + _: [*]const erts.ERL_NIF_TERM, +) callconv(.c) erts.ERL_NIF_TERM { + var pid: erts.ErlNifPid = undefined; + if (erts.enif_self(env, &pid) == null) { + return erts.badarg(env); + } + + const pid_ptr = std.heap.c_allocator.alloc(erts.ErlNifPid, 1) catch return erts.badarg(env); + pid_ptr[0] = pid; + mob_iap_restore(pid_ptr); + return erts.atom(env, "ok"); +} + +fn nif_iap_current_entitlements( + env: ?*erts.ErlNifEnv, + _: c_int, + _: [*]const erts.ERL_NIF_TERM, +) callconv(.c) erts.ERL_NIF_TERM { + var pid: erts.ErlNifPid = undefined; + if (erts.enif_self(env, &pid) == null) { + return erts.badarg(env); + } + + const pid_ptr = std.heap.c_allocator.alloc(erts.ErlNifPid, 1) catch return erts.badarg(env); + pid_ptr[0] = pid; + mob_iap_current_entitlements(pid_ptr); + return erts.atom(env, "ok"); +} + +fn nif_iap_manage_subscriptions( + env: ?*erts.ErlNifEnv, + _: c_int, + _: [*]const erts.ERL_NIF_TERM, +) callconv(.c) erts.ERL_NIF_TERM { + _ = env; + mob_iap_manage_subscriptions(); + return erts.atom(env, "ok"); +} + // ── NIF table + ERL_NIF_INIT entry point ───────────────────────────────── // Replaces the static `ErlNifFunc nif_funcs[]` + `ERL_NIF_INIT` macro // that used to live at the bottom of mob_nif.c. The entry point is the @@ -4874,6 +5005,12 @@ const nif_funcs = [_]erts.ErlNifFunc{ .{ .name = "bt_spp_write", .arity = 2, .fptr = nif_bt_spp_write, .flags = erts.ERL_NIF_DIRTY_JOB_IO_BOUND }, .{ .name = "bt_hid_connect", .arity = 1, .fptr = nif_bt_hid_connect, .flags = 0 }, .{ .name = "bt_hid_subscribe_raw", .arity = 1, .fptr = nif_bt_hid_subscribe_raw, .flags = 0 }, + // ── In-App Purchase (mob_iap plugin) ────────────────────────────────────── + .{ .name = "iap_fetch_products", .arity = 1, .fptr = nif_iap_fetch_products, .flags = 0 }, + .{ .name = "iap_purchase", .arity = 1, .fptr = nif_iap_purchase, .flags = 0 }, + .{ .name = "iap_restore", .arity = 0, .fptr = nif_iap_restore, .flags = 0 }, + .{ .name = "iap_current_entitlements", .arity = 0, .fptr = nif_iap_current_entitlements, .flags = 0 }, + .{ .name = "iap_manage_subscriptions", .arity = 0, .fptr = nif_iap_manage_subscriptions, .flags = 0 }, }; var mob_nif_entry: erts.ErlNifEntry = .{ diff --git a/ios/mob_nif.m b/ios/mob_nif.m index d4f1bb5..c0bc486 100644 --- a/ios/mob_nif.m +++ b/ios/mob_nif.m @@ -2039,6 +2039,163 @@ static void mob_send3(const ErlNifPid *pid, const char *a1, const char *a2, cons enif_free_env(e); } +// ════════════════════════════════════════════════════════════════════════════ +// IAP bridge helpers — called from MobIapBridge.swift via @_silgen_name +// ════════════════════════════════════════════════════════════════════════════ + +// Send {:iap, atom} to the BEAM process identified by serialized ErlNifPid. +// pid_bytes points to a serialized ErlNifPid copied by the NIF caller. +static void mob_iap_send2(const void *pid_bytes, const char *tag, const char *atom) { + ErlNifPid pid; + memcpy(&pid, pid_bytes, sizeof(ErlNifPid)); + ErlNifEnv *e = enif_alloc_env(); + ERL_NIF_TERM msg = enif_make_tuple2(e, enif_make_atom(e, tag), enif_make_atom(e, atom)); + enif_send(NULL, &pid, e, msg); + enif_free_env(e); + free((void *)pid_bytes); +} + +// Send {:iap, tag, atom} to the BEAM. +static void mob_iap_send3(const void *pid_bytes, const char *tag, const char *a1, const char *a2) { + ErlNifPid pid; + memcpy(&pid, pid_bytes, sizeof(ErlNifPid)); + ErlNifEnv *e = enif_alloc_env(); + ERL_NIF_TERM msg = + enif_make_tuple3(e, enif_make_atom(e, tag), enif_make_atom(e, a1), enif_make_atom(e, a2)); + enif_send(NULL, &pid, e, msg); + enif_free_env(e); + free((void *)pid_bytes); +} + +// Send {:iap, :products, binary_json} — JSON list of product maps. +static void mob_iap_send_products(const void *pid_bytes, const char *json) { + ErlNifPid pid; + memcpy(&pid, pid_bytes, sizeof(ErlNifPid)); + ErlNifEnv *e = enif_alloc_env(); + ERL_NIF_TERM tag = enif_make_tuple2(e, enif_make_atom(e, "iap"), enif_make_atom(e, "products")); + ERL_NIF_TERM json_bin; + size_t len = strlen(json); + unsigned char *buf = enif_make_new_binary(e, len, &json_bin); + memcpy(buf, json, len); + ERL_NIF_TERM msg = enif_make_tuple2(e, tag, json_bin); + enif_send(NULL, &pid, e, msg); + enif_free_env(e); + free((void *)pid_bytes); +} + +// Send {:iap, tag, binary_json} — a single transaction as JSON map. +static void mob_iap_send_transaction(const void *pid_bytes, const char *tag, const char *json) { + ErlNifPid pid; + memcpy(&pid, pid_bytes, sizeof(ErlNifPid)); + ErlNifEnv *e = enif_alloc_env(); + ERL_NIF_TERM inner = enif_make_tuple2(e, enif_make_atom(e, "iap"), enif_make_atom(e, tag)); + ERL_NIF_TERM json_bin; + size_t len = strlen(json); + unsigned char *buf = enif_make_new_binary(e, len, &json_bin); + memcpy(buf, json, len); + ERL_NIF_TERM msg = enif_make_tuple2(e, inner, json_bin); + enif_send(NULL, &pid, e, msg); + enif_free_env(e); + free((void *)pid_bytes); +} + +// Send {:iap, tag, binary_json} — a JSON array of transactions. +static void mob_iap_send_transactions(const void *pid_bytes, const char *tag, const char *json) { + mob_iap_send_transaction(pid_bytes, tag, json); +} + +// ════════════════════════════════════════════════════════════════════════════ +// IAP NIF function implementations +// ════════════════════════════════════════════════════════════════════════════ + +// Extract the product IDs list from a term — expects a list of binaries. +// On failure, frees any ids already allocated and returns 0. +static size_t iap_extract_product_ids(ErlNifEnv *env, ERL_NIF_TERM list, char **ids, + size_t max_ids) { + unsigned int list_len; + if (!enif_get_list_length(env, list, &list_len)) + return 0; + if (list_len > max_ids) + return 0; + + ERL_NIF_TERM head, tail = list; + for (unsigned int i = 0; i < list_len; i++) { + if (!enif_get_list_cell(env, tail, &head, &tail)) { + for (unsigned int j = 0; j < i; j++) + free(ids[j]); + return 0; + } + ErlNifBinary bin; + if (!enif_inspect_binary(env, head, &bin) && + !enif_inspect_iolist_as_binary(env, head, &bin)) { + for (unsigned int j = 0; j < i; j++) + free(ids[j]); + return 0; + } + ids[i] = strndup((const char *)bin.data, bin.size); + } + return list_len; +} + +static ERL_NIF_TERM nif_iap_fetch_products(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + char *ids[128]; + memset(ids, 0, sizeof(ids)); + size_t count = iap_extract_product_ids(env, argv[0], ids, 128); + + ErlNifPid *pid_copy = malloc(sizeof(ErlNifPid)); + enif_self(env, pid_copy); + + // Build NSArray and pass to MobIapBridge + NSMutableArray *productIds = [NSMutableArray arrayWithCapacity:count]; + for (size_t i = 0; i < count; i++) { + [productIds addObject:[NSString stringWithUTF8String:ids[i]]]; + free(ids[i]); + } + + [MobIapBridge fetchProducts:productIds pidBytes:pid_copy]; + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM nif_iap_purchase(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary bin; + if (!enif_inspect_binary(env, argv[0], &bin) && + !enif_inspect_iolist_as_binary(env, argv[0], &bin)) + return enif_make_badarg(env); + + NSString *productId = [[NSString alloc] initWithBytes:bin.data + length:bin.size + encoding:NSUTF8StringEncoding]; + + ErlNifPid *pid_copy = malloc(sizeof(ErlNifPid)); + enif_self(env, pid_copy); + + [MobIapBridge purchase:productId pidBytes:pid_copy]; + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM nif_iap_restore(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifPid *pid_copy = malloc(sizeof(ErlNifPid)); + enif_self(env, pid_copy); + + [MobIapBridge restorePurchases:pid_copy]; + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM nif_iap_current_entitlements(ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + ErlNifPid *pid_copy = malloc(sizeof(ErlNifPid)); + enif_self(env, pid_copy); + + [MobIapBridge currentEntitlements:pid_copy]; + return enif_make_atom(env, "ok"); +} + +static ERL_NIF_TERM nif_iap_manage_subscriptions(ErlNifEnv *env, int argc, + const ERL_NIF_TERM argv[]) { + [MobIapBridge manageSubscriptions]; + return enif_make_atom(env, "ok"); +} + // Return the root view controller of the key window in the first active scene. static UIViewController *mob_root_vc(void) { for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) { @@ -6651,6 +6808,12 @@ static ERL_NIF_TERM nif_bt_hid_subscribe_raw(ErlNifEnv *env, int argc, const ERL // doesn't head-of-line-block the regular schedulers. See the impl // above for the iOS rationale. {"resolve_ipv4", 1, nif_resolve_ipv4, ERL_NIF_DIRTY_JOB_IO_BOUND}, + // ── In-App Purchase (mob_iap plugin) ────────────────────────────────────── + {"iap_fetch_products", 1, nif_iap_fetch_products, 0}, + {"iap_purchase", 1, nif_iap_purchase, 0}, + {"iap_restore", 0, nif_iap_restore, 0}, + {"iap_current_entitlements", 0, nif_iap_current_entitlements, 0}, + {"iap_manage_subscriptions", 0, nif_iap_manage_subscriptions, 0}, }; static int nif_load(ErlNifEnv *env, void **priv, ERL_NIF_TERM info) { diff --git a/plugins/mob_iap/.formatter.exs b/plugins/mob_iap/.formatter.exs new file mode 100644 index 0000000..d304ff3 --- /dev/null +++ b/plugins/mob_iap/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/plugins/mob_iap/.gitignore b/plugins/mob_iap/.gitignore new file mode 100644 index 0000000..bf166e3 --- /dev/null +++ b/plugins/mob_iap/.gitignore @@ -0,0 +1,3 @@ +/_build/ +/deps/ +/mix.lock diff --git a/plugins/mob_iap/README.md b/plugins/mob_iap/README.md new file mode 100644 index 0000000..2b36a04 --- /dev/null +++ b/plugins/mob_iap/README.md @@ -0,0 +1,126 @@ +# mob_iap — In-App Purchase plugin for Mob + +StoreKit 2 (iOS) and Play Billing 7.0 (Android) for Mob apps. + +## Installation + +Add `:mob_iap` to your deps and activate it in your config: + +```elixir +# mix.exs +defp deps do + [ + {:mob_iap, "~> 0.1"} + ] +end + +# config/runtime.exs +config :mob, :plugins, [:mob_iap] +``` + +## Product types + +| Type | iOS | Android | Description | +|---|---|---|---| +| `:consumable` | ✅ | ✅ | One-time purchasable, consumed after use (gems, coins) | +| `:non_consumable` | ✅ | ✅ | One-time purchasable, permanent (unlock, remove ads) | +| `:auto_renewable` | ✅ | ✅ | Auto-renewing subscription | +| `:non_renewing` | ✅ | ✅ | Non-renewing subscription | + +## API + +```elixir +# Fetch products from the store +MobIap.fetch_products(socket, [:premium, :coins_100]) + +# Initiate a purchase +MobIap.purchase(socket, :premium) + +# Restore previous purchases +MobIap.restore(socket) + +# Get current entitlements (active subs + owned non-consumables) +MobIap.current_entitlements(socket) + +# Open OS subscription management +MobIap.manage_subscriptions(socket) + +# Server-side receipt verification +MobIap.verify(socket, tx.id, tx.original_json) +``` + +All calls are fire-and-forget — results arrive as `handle_info` messages. +JSON payloads must be decoded with the provided helpers: + +```elixir +def handle_info({:iap, :products, json}, socket) do + products = MobIap.decode_products!(json) + {:noreply, assign(socket, :products, products)} +end + +def handle_info({:iap, :purchased, json}, socket) do + tx = MobIap.decode_transaction!(json) + unlock_content(socket, tx.product_id) + {:noreply, socket} +end + +def handle_info({:iap, :cancelled}, socket) do + {:noreply, socket} +end +``` + +## Events + +| Event | Payload | +|---|---| +| `{:iap, :products, json}` | Product list as JSON binary — decode with `MobIap.decode_products!/1` | +| `{:iap, :products_failed}` | Store unavailable | +| `{:iap, :purchased, json}` | Transaction JSON — decode with `MobIap.decode_transaction!/1` | +| `{:iap, :purchase_failed}` | Purchase failed | +| `{:iap, :cancelled}` | User cancelled | +| `{:iap, :purchase_pending, json}` | Pending (e.g. parental approval) — decode with `MobIap.decode_transaction!/1` | +| `{:iap, :restored, json}` | Restored transactions — decode with `MobIap.decode_transactions!/1` | +| `{:iap, :restore_failed}` | Restore failed | +| `{:iap, :entitlements, json}` | Active entitlements — decode with `MobIap.decode_transactions!/1` | +| `{:iap, :entitlements_failed}` | Entitlements query failed | +| `{:iap, :verified, tx_id, result}` | Verification result (`:valid` or `{:invalid, reason}`) | + +## Screens + +```elixir +# Product storefront +Mob.Socket.push_screen(socket, MobIap.StoreScreen, + product_ids: [:premium, :coins_100], + title: "Shop") + +# Active subscriptions management +Mob.Socket.push_screen(socket, MobIap.SubscriptionScreen) + +# Plugin settings +Mob.Socket.push_screen(socket, MobIap.SettingsScreen) +``` + +## Receipt verification + +StoreKit 2 transactions include a signed JWT in `original_json`. For +server-side verification: + +1. Set `verification_endpoint` in plugin settings (or via app config) +2. Call `MobIap.verify/3` with the transaction ID and receipt data +3. The configured endpoint receives a POST with the receipt JWS +4. Verify against Apple's `/verifyReceipt` or Google's + `purchases.products.get` + +## Architecture + +``` +MobIap (Elixir) → :mob_nif (NIF) → MobIapBridge (native) → Store + ◦ iap_fetch_products ◦ mob_nif.zig/erl ◦ Swift (StoreKit 2) + ◦ iap_purchase ◦ Kotlin (Play Billing) + ◦ iap_restore Results flow back as + ◦ iap_current_entitlements enif_send → handle_info + ◦ iap_manage_subscriptions +``` + +The native bridges reside in `priv/native/ios/` and `priv/native/android/`. +They're merged into the host app's native build by the mob_dev pipeline. diff --git a/plugins/mob_iap/lib/mob_iap.ex b/plugins/mob_iap/lib/mob_iap.ex new file mode 100644 index 0000000..55413f2 --- /dev/null +++ b/plugins/mob_iap/lib/mob_iap.ex @@ -0,0 +1,337 @@ +defmodule MobIap do + @moduledoc """ + In-App Purchase for iOS (StoreKit 2) and Android (Play Billing 7). + + All calls are fire-and-forget — results arrive as `handle_info` + messages tagged `{:iap, ...}`. Same async pattern as `Mob.Camera`, + `Mob.Biometric`, and `Mob.Location`. + + ## Setup + + Add `mob_iap` to your deps and `:mob_iap` to `config :mob, :plugins` + in your app config. The plugin manifest registers the native bridges, + permissions, and UI screens automatically. + + ### On-device receipt verification (StoreKit 2) + + StoreKit 2 transactions arrive as signed JWTs. The native side + verifies the signature chain and bundle ID before returning the + transaction. For subscriptions and non-consumables, server-side + verification is still recommended. + + ## Events + + All events arrive in `c:Mob.Screen.handle_info/2`. Payloads for products, + transactions, and transaction lists are JSON binaries — use + `MobIap.decode_products!/1`, `MobIap.decode_transaction!/1`, and + `MobIap.decode_transactions!/1` to convert them to structs. + + # Products fetched from the store (json is a binary — decode with decode_products!/1) + {:iap, :products, json} + + # Store connection error (network, account issue, etc.) + {:iap, :products_failed} + + # Purchase completed — unlock content now (json is a binary — decode with decode_transaction!/1) + {:iap, :purchased, json} + + # Purchase failed + {:iap, :purchase_failed} + + # User cancelled the purchase dialog + {:iap, :cancelled} + + # Purchase pending — e.g. parental approval required (json is a binary) + {:iap, :purchase_pending, json} + + # Restored purchases — re-unlock previously purchased items (json is a binary) + {:iap, :restored, json} + + # Current entitlements — active subs + owned non-consumables (json is a binary) + {:iap, :entitlements, json} + + # Restore failed (network or store error) + {:iap, :restore_failed} + + # Entitlements query failed + {:iap, :entitlements_failed} + + # Server-side verification result + {:iap, :verified, transaction_id, :valid | {:invalid, reason}} + + ## Examples + + def handle_info({:iap, :products, json}, socket) do + products = MobIap.decode_products!(json) + {:noreply, Mob.Screen.assign(socket, :products, products)} + end + + def handle_info({:iap, :purchased, json}, socket) do + tx = MobIap.decode_transaction!(json) + # Unlock content for tx.product_id + unlock(socket, tx) + {:noreply, socket} + end + + def handle_info({:iap, :cancelled}, socket) do + {:noreply, socket} + end + + def handle_info({:iap, :purchase_failed}, socket) do + Logger.warning("Purchase failed") + {:noreply, socket} + end + """ + + @doc """ + Fetch products from the store. + + Product IDs are the identifiers configured in App Store Connect / Play Console. + Returns localized pricing, descriptions, and subscription metadata. + + MobIap.fetch_products(socket, [:premium, :coins_100]) + """ + @spec fetch_products(Mob.Socket.t(), [atom()]) :: Mob.Socket.t() + def fetch_products(socket, product_ids) when is_list(product_ids) do + try do + :mob_nif.iap_fetch_products(Enum.map(product_ids, &Atom.to_string/1)) + catch + :error, :undef -> :ok + end + + socket + end + + @doc """ + Initiate a purchase for a product. + + Shows the native purchase dialog. The user can authenticate with + Touch ID / Face ID and confirm or cancel. The result arrives as + `{:iap, :purchased, tx}` or `{:iap, :cancelled}`. + + MobIap.purchase(socket, :premium) + """ + @spec purchase(Mob.Socket.t(), atom()) :: Mob.Socket.t() + def purchase(socket, product_id) when is_atom(product_id) do + try do + :mob_nif.iap_purchase(Atom.to_string(product_id)) + catch + :error, :undef -> :ok + end + + socket + end + + @doc """ + Restore previously purchased non-consumables and subscriptions. + + Required by App Store guidelines — provide a visible "Restore Purchases" + button in your UI. No-op on Android if `queryPurchasesAsync` catches + everything. + + MobIap.restore(socket) + """ + @spec restore(Mob.Socket.t()) :: Mob.Socket.t() + def restore(socket) do + try do + :mob_nif.iap_restore() + catch + :error, :undef -> :ok + end + + socket + end + + @doc """ + Fetch current entitlements — active subscriptions and owned non-consumables. + + Uses StoreKit 2 `Transaction.currentEntitlements` on iOS, + `BillingClient.queryPurchasesAsync` on Android. Call on app launch + to re-verify entitlements after backgrounding. + + MobIap.current_entitlements(socket) + """ + @spec current_entitlements(Mob.Socket.t()) :: Mob.Socket.t() + def current_entitlements(socket) do + try do + :mob_nif.iap_current_entitlements() + catch + :error, :undef -> :ok + end + + socket + end + + @doc """ + Open the OS-level subscription management UI. + + iOS: opens Settings → Subscriptions. + Android: opens Play Store → Subscriptions for this app. + + MobIap.manage_subscriptions(socket) + """ + @spec manage_subscriptions(Mob.Socket.t()) :: Mob.Socket.t() + def manage_subscriptions(socket) do + try do + :mob_nif.iap_manage_subscriptions() + catch + :error, :undef -> :ok + end + + socket + end + + @doc """ + Verify a transaction receipt through the configured server endpoint. + + Performs an async HTTP POST to `verification_endpoint` (set via plugin + settings or `Application.put_env(:mob_iap, :verification_endpoint, url)`). + Posts `{tx_id, receipt: receipt_data}` as JSON. + + Result arrives as `{:iap, :verified, tx_id, result}` where result is + `:valid` or `{:invalid, reason}`. + + If no endpoint is configured, falls back to the no-op NIF stub. + + # Configure endpoint in config/runtime.exs or via SettingsScreen + Application.put_env(:mob_iap, :verification_endpoint, + "https://api.example.com/verify-receipt") + + MobIap.verify(socket, tx.id, tx.original_json) + + # In handle_info/2: + def handle_info({:iap, :verified, tx_id, :valid}, socket) do + {:noreply, socket} + end + """ + @spec verify(Mob.Socket.t(), String.t(), String.t()) :: Mob.Socket.t() + def verify(socket, transaction_id, receipt_data) do + endpoint = Application.get_env(:mob_iap, :verification_endpoint, "") + + if endpoint == "" do + send(self(), {:iap, :verified, transaction_id, {:invalid, "no_endpoint_configured"}}) + else + caller = self() + + Task.start(fn -> + result = + try do + body = Jason.encode!(%{tx_id: transaction_id, receipt: receipt_data}) + + case :httpc.request( + :post, + {String.to_charlist(endpoint), [], ~c"application/json", body}, + [connect_timeout: 5_000, timeout: 10_000], + [] + ) do + {:ok, {{_, 200, _}, _headers, resp_body}} -> + case Jason.decode(IO.iodata_to_binary(resp_body)) do + {:ok, %{"valid" => true}} -> :valid + {:ok, %{"valid" => false, "reason" => reason}} -> {:invalid, reason} + _ -> {:invalid, "unrecognized_response"} + end + + {:ok, {{_, code, _}, _headers, _body}} -> + {:invalid, "http_#{code}"} + + {:error, reason} -> + {:invalid, inspect(reason)} + end + rescue + e -> {:invalid, Exception.message(e)} + end + + send(caller, {:iap, :verified, transaction_id, result}) + end) + end + + socket + end + + # ── JSON decode helpers ──────────────────────────────────────────────── + # The native bridges send product/transaction data as JSON binaries. + # Use these helpers in handle_info to decode structured messages. + + @product_types %{ + "consumable" => :consumable, + "non_consumable" => :non_consumable, + "auto_renewable" => :auto_renewable, + "non_renewing" => :non_renewing + } + + @ownership_types %{ + "purchased" => :purchased, + "family_shared" => :family_shared + } + + @environments %{ + "production" => :production, + "sandbox" => :sandbox + } + + @doc """ + Decode a JSON product list from `{:iap, :products, json}`. + + def handle_info({:iap, :products, json}, socket) do + products = MobIap.decode_products!(json) + {:noreply, assign(socket, :products, products)} + end + """ + @spec decode_products!(binary()) :: [MobIap.Product.t()] + def decode_products!(json) when is_binary(json) do + json + |> Jason.decode!() + |> Enum.map(&map_to_product/1) + end + + @doc """ + Decode a transaction list JSON from `{:iap, :entitlements, json}` + or `{:iap, :restored, json}`. + """ + @spec decode_transactions!(binary()) :: [MobIap.Transaction.t()] + def decode_transactions!(json) when is_binary(json) do + json + |> Jason.decode!() + |> Enum.map(&map_to_transaction/1) + end + + @doc """ + Decode a single transaction JSON from `{:iap, :purchased, json}`. + """ + @spec decode_transaction!(binary()) :: MobIap.Transaction.t() + def decode_transaction!(json) when is_binary(json) do + json + |> Jason.decode!() + |> map_to_transaction() + end + + # Convert JSON string keys to atoms for Product struct + defp map_to_product(map) do + %MobIap.Product{ + id: map["id"], + display_name: map["display_name"], + description: map["description"], + price: map["price"], + price_amount: map["price_amount"], + currency_code: map["currency_code"], + type: Map.get(@product_types, map["type"], :consumable), + subscription_period: map["subscription_period"], + introductory_offer: map["introductory_offer"], + trial_period: map["trial_period"] + } + end + + # Convert JSON string keys to atoms for Transaction struct + defp map_to_transaction(map) do + %MobIap.Transaction{ + id: map["id"], + product_id: map["product_id"], + purchase_date: map["purchase_date"], + expires_date: map["expires_date"], + original_json: map["original_json"], + is_upgraded: map["is_upgraded"] == 1, + ownership_type: Map.get(@ownership_types, map["ownership_type"], :purchased), + environment: Map.get(@environments, map["environment"], :production) + } + end +end diff --git a/plugins/mob_iap/lib/mob_iap/nif.ex b/plugins/mob_iap/lib/mob_iap/nif.ex new file mode 100644 index 0000000..0eb3bcc --- /dev/null +++ b/plugins/mob_iap/lib/mob_iap/nif.ex @@ -0,0 +1,19 @@ +defmodule MobIap.Nif do + @moduledoc false + + # Placeholder module for the plugin manifest's `:nifs` entry. + # + # The 6 IAP NIF functions (`iap_fetch_products/1`, `iap_purchase/1`, + # `iap_restore/0`, `iap_current_entitlements/0`, `iap_manage_subscriptions/0`, + # `iap_verify_receipt/2`) are registered under the existing `:mob_nif` module + # in `src/mob_nif.erl` — following the pattern of every other Mob device API + # (Camera, Biometric, Location, etc.). + # + # Native implementations live in: + # iOS: `ios/mob_nif.m` → `priv/native/ios/MobIapBridge.swift` (StoreKit 2) + # Android: `android/jni/mob_nif.zig` → `priv/native/android/MobIapBridge.kt` + # + # This module exists only so the plugin manifest declaration is satisfied. + # The NIF registration itself runs through the existing `driver_tab_*.zig` + # static NIF table, not through this module. +end diff --git a/plugins/mob_iap/lib/mob_iap/product.ex b/plugins/mob_iap/lib/mob_iap/product.ex new file mode 100644 index 0000000..d7175aa --- /dev/null +++ b/plugins/mob_iap/lib/mob_iap/product.ex @@ -0,0 +1,51 @@ +defmodule MobIap.Product do + @moduledoc """ + A product available for purchase from the app store. + + Returned by `MobIap.fetch_products/2`. Fields are populated from + StoreKit 2 / Play Billing responses translated through the native bridge. + """ + + @type t :: %__MODULE__{ + id: String.t(), + display_name: String.t(), + description: String.t(), + price: String.t(), + price_amount: float(), + currency_code: String.t(), + type: product_type(), + subscription_period: String.t() | nil, + introductory_offer: map() | nil, + trial_period: String.t() | nil + } + + @type product_type :: + :consumable + | :non_consumable + | :auto_renewable + | :non_renewing + + defstruct [ + :id, + :display_name, + :price, + description: "", + price_amount: 0.0, + currency_code: "USD", + type: :consumable, + subscription_period: nil, + introductory_offer: nil, + trial_period: nil + ] + + @doc """ + Create a Product struct from a keyword list. + + Used primarily in tests and for constructing mock data. The native + bridges produce JSON that is decoded via `MobIap.decode_products!/1`. + """ + @spec new(keyword()) :: t() + def new(fields) do + struct!(__MODULE__, fields) + end +end diff --git a/plugins/mob_iap/lib/mob_iap/settings_screen.ex b/plugins/mob_iap/lib/mob_iap/settings_screen.ex new file mode 100644 index 0000000..1016a35 --- /dev/null +++ b/plugins/mob_iap/lib/mob_iap/settings_screen.ex @@ -0,0 +1,118 @@ +defmodule MobIap.SettingsScreen do + @moduledoc """ + Plugin settings editor for mob_iap. + + Allows toggling receipt auto-verification and configuring the server-side + verification endpoint. + """ + + use Mob.Screen + + def mount(_params, _session, socket) do + socket = + socket + |> Mob.Socket.assign( + :auto_verify, + Application.get_env(:mob_iap, :auto_verify_receipts, true) + ) + |> Mob.Socket.assign( + :verification_endpoint, + Application.get_env(:mob_iap, :verification_endpoint, "") + ) + |> Mob.Socket.assign(:saved, false) + + {:ok, socket} + end + + def render(assigns) do + ~MOB""" + + + + + + {render_auto_verify_toggle(assigns)} + + + + {render_endpoint_field(assigns)} + + + + {render_save_button(assigns)} + + {if assigns.saved do + ~MOB() + end} + + """ + end + + defp render_auto_verify_toggle(assigns) do + tap = {self(), :toggle_auto_verify} + + ~MOB""" + + + + + + ';var nu='button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',M=null,Sn=null,xn=null,kn=!1;function ru(){M||(document.body.insertAdjacentHTML("beforeend",Ji),M=f(".modal"),M.addEventListener("keydown",e=>{e.key==="Escape"&&ue()}),M.querySelector(".modal-close").addEventListener("click",ue),M.addEventListener("click",e=>{e.target===M&&ue()}))}function Xi(e){if(!kn)if(M.contains(e.target))xn=e.target;else{kn=!0;let t=M.querySelectorAll(nu);xn===t[0]?t[t.length-1].focus():t[0].focus(),kn=!1,xn=document.activeElement}}function wt({title:e,body:t}){ru(),Sn=document.activeElement,document.addEventListener("focus",Xi,!0),M.querySelector(".modal-title").innerHTML=e,M.querySelector(".modal-body").innerHTML=t,M.classList.add("shown"),M.focus()}function ue(){M?.classList.remove("shown"),document.removeEventListener("focus",Xi,!0),Sn?.focus(),Sn=null}function Zi(){return!!M?.classList.contains("shown")}var es='
';var iu="https://hexdocs.pm/%%",su="https://www.erlang.org/doc/apps/%%",ou="https://hex.pm/api/packages?search=name:%%*",au=".display-quick-switch",ns="#quick-switch-input",rs="#quick-switch-results",lu=300,uu=9,is=["erts","asn1","common_test","compiler","crypto","debugger","dialyzer","diameter","edoc","eldap","erl_interface","et","eunit","ftp","inets","jinterface","kernel","megaco","mnesia","observer","odbc","os_mon","parsetools","public_key","reltool","runtime_tools","sasl","snmp","ssh","ssl","stdlib","syntax_tools","tftp","tools","wx","xmerl"],cu=["elixir","eex","ex_unit","hex","iex","logger","mix"].concat(is).map(e=>({name:e})),ss=2,D={autocompleteResults:[],selectedIdx:null};C||window.addEventListener("exdoc:loaded",du);function du(){A(au).forEach(e=>{e.addEventListener("click",Ln)})}function hu(e){if(e.key==="Enter"){let t=e.target.value;pu(t),e.preventDefault()}else e.key==="ArrowUp"?(ts(-1),e.preventDefault()):e.key==="ArrowDown"&&(ts(1),e.preventDefault())}function fu(e){let t=e.target.value;if(t.lengthn.json()).then(n=>{Array.isArray(n)&&(D.autocompleteResults=yu(e,n),D.selectedIdx=null,f(ns).value.length>=ss&&vu(D.autocompleteResults))})}function vu(e){f(rs).replaceChildren(...e.map(({name:t},n)=>{let r=E("div",{class:"quick-switch-result","data-index":n},[t]);return r.addEventListener("click",()=>_n(t)),r}))}function yu(e,t){return cu.concat(t).filter(n=>n.name.toLowerCase().includes(e.toLowerCase())).filter(n=>n.releases===void 0||n.releases[0].has_docs===!0).slice(0,uu)}function ts(e){D.selectedIdx=wu(e);let t=f(".quick-switch-result.selected"),n=f(`.quick-switch-result[data-index="${D.selectedIdx}"]`);t&&t.classList.remove("selected"),n&&n.classList.add("selected")}function wu(e){let t=D.autocompleteResults.length;if(D.selectedIdx===null){if(e>=0)return 0;if(e<0)return t-1}return(D.selectedIdx+e+t)%t}var bu="#settings-modal-content",On=[{key:"c",description:"Toggle sidebar",action:We},{key:"n",description:"Cycle themes",action:ar},{key:"s",description:"Focus search bar",displayAs:"/ or s",action:Tn},{key:"/",action:Tn},{key:"k",hasModifier:!0,action:Tn},{key:"v",description:"Open/focus version select",action:ku},{key:"g",description:"Go to package docs",displayAs:"g",action:Ln},{key:"?",displayAs:"?",description:"Bring up this modal",action:Su}],Pn={shortcutBeingPressed:null};C||(document.addEventListener("keydown",Eu),document.addEventListener("keyup",xu));function Eu(e){if(Pn.shortcutBeingPressed||e.target.matches("input, select, textarea"))return;let t=On.find(n=>n.hasModifier?Qe()&&e.metaKey||e.ctrlKey?n.key===e.key:!1:e.ctrlKey||e.metaKey||e.altKey?!1:n.key===e.key);t&&(Pn.shortcutBeingPressed=t,e.preventDefault(),t.action(e))}function xu(e){Pn.shortcutBeingPressed=null}function Tn(e){ue(),Ni()}function ku(){ue(),kr()?hn():_r().then(hn)}function Su(){_u()?ue():An()}function _u(){return Zi()&&f(bu)}var os=Y(ae());var as=os.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"description"):t,{name:"if",hash:{},fn:e.program(2,i,0),inverse:e.noop,data:i,loc:{start:{line:40,column:6},end:{line:53,column:13}}}))!=null?s:""},2:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
+
+ `+e.escapeExpression(e.lambda(t!=null?o(t,"description"):t,t))+` +
+
+`+((s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"displayAs"):t,{name:"if",hash:{},fn:e.program(3,i,0),inverse:e.program(5,i,0),data:i,loc:{start:{line:46,column:12},end:{line:50,column:19}}}))!=null?s:"")+`
+
+`},3:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" "+((s=e.lambda(t!=null?o(t,"displayAs"):t,t))!=null?s:"")+` +`},5:function(e,t,n,r,i){var s=e.lookupProperty||function(o,a){if(Object.prototype.hasOwnProperty.call(o,a))return o[a]};return" "+e.escapeExpression(e.lambda(t!=null?s(t,"key"):t,t))+` +`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
+
+ + + + +
+ +
+`},useData:!0});var Lu=".display-settings",Tu="#settings-modal-content",In="#modal-settings-tab",Cn="#modal-keyboard-shortcuts-tab",us="#settings-content",cs="#keyboard-shortcuts-content",Pu=[{title:"Settings",id:"modal-settings-tab"},{title:"Keyboard shortcuts",id:"modal-keyboard-shortcuts-tab"}];window.addEventListener("exdoc:loaded",Ou);function Ou(){A(Lu).forEach(e=>{e.addEventListener("click",An)})}function ls(){f(Cn).classList.remove("active"),f(In).classList.add("active"),f(us).classList.remove("hidden"),f(cs).classList.add("hidden")}function Au(){f(Cn).classList.add("active"),f(In).classList.remove("active"),f(cs).classList.remove("hidden"),f(us).classList.add("hidden")}function An(){wt({title:Pu.map(({id:s,title:o})=>``).join(""),body:as({shortcuts:On})});let e=f(Tu),t=e.querySelector('[name="theme"]'),n=e.querySelector('[name="tooltips"]'),r=e.querySelector('[name="direct_livebook_url"]'),i=e.querySelector('[name="livebook_url"]');R.getAndSubscribe(s=>{t.value=s.theme||"system",n.checked=s.tooltips,s.livebookUrl===null?(r.checked=!1,i.classList.add("hidden"),i.tabIndex=-1):(r.checked=!0,i.classList.remove("hidden"),i.tabIndex=0,i.value=s.livebookUrl)}),t.addEventListener("change",s=>{R.update({theme:s.target.value})}),n.addEventListener("change",s=>{R.update({tooltips:s.target.checked})}),r.addEventListener("change",s=>{let o=s.target.checked?i.value:null;R.update({livebookUrl:o})}),i.addEventListener("input",s=>{R.update({livebookUrl:s.target.value})}),f(In).addEventListener("click",s=>{ls()}),f(Cn).addEventListener("click",s=>{Au()}),ls()}var Rn=new WeakMap;function Mn(e,t,n,r){if(!e&&!Rn.has(t))return!1;let i=Rn.get(t)??new WeakMap;Rn.set(t,i);let s=i.get(n)??new Set;i.set(n,s);let o=s.has(r);return e?s.add(r):s.delete(r),o&&e}function Iu(e,t){let n=e.target;if(n instanceof Text&&(n=n.parentElement),n instanceof Element&&e.currentTarget instanceof Node){let r=n.closest(t);if(r&&e.currentTarget.contains(r))return r}}function Cu(e,t,n,r={}){let{signal:i,base:s=document}=r;if(i?.aborted)return;let{once:o,...a}=r,l=s instanceof Document?s.documentElement:s,u=!!(typeof r=="object"?r.capture:r),c=p=>{let g=Iu(p,String(e));if(g){let m=Object.assign(p,{delegateTarget:g});n.call(l,m),o&&(l.removeEventListener(t,c,a),Mn(!1,l,n,d))}},d=JSON.stringify({selector:e,type:t,capture:u});Mn(!0,l,n,d)||l.addEventListener(t,c,a),i?.addEventListener("abort",()=>{Mn(!1,l,n,d)})}var bt=Cu;function O(){return O=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).toLowerCase().replace(/[\s/_.]+/g,"-").replace(/[^\w-]+/g,"").replace(/--+/g,"-").replace(/^-+|-+$/g,"")||t||"",Oe=({hash:e}={})=>window.location.pathname+window.location.search+(e?window.location.hash:""),Ru=(e,t={})=>{let n=O({url:e=e||Oe({hash:!0}),random:Math.random(),source:"swup"},t);window.history.pushState(n,"",e)},Pe=(e=null,t={})=>{e=e||Oe({hash:!0});let n=O({},window.history.state||{},{url:e,random:Math.random(),source:"swup"},t);window.history.replaceState(n,"",e)},Mu=(e,t,n,r)=>{let i=new AbortController;return r=O({},r,{signal:i.signal}),bt(e,t,n,r),{destroy:()=>i.abort()}},N=class e extends URL{constructor(t,n=document.baseURI){super(t.toString(),n),Object.setPrototypeOf(this,e.prototype)}get url(){return this.pathname+this.search}static fromElement(t){let n=t.getAttribute("href")||t.getAttribute("xlink:href")||"";return new e(n)}static fromUrl(t){return new e(t)}};var ge=class extends Error{constructor(t,n){super(t),this.url=void 0,this.status=void 0,this.aborted=void 0,this.timedOut=void 0,this.name="FetchError",this.url=n.url,this.status=n.status,this.aborted=n.aborted||!1,this.timedOut=n.timedOut||!1}};async function Nu(e,t={}){var n;e=N.fromUrl(e).url;let{visit:r=this.visit}=t,i=O({},this.options.requestHeaders,t.headers),s=(n=t.timeout)!=null?n:this.options.timeout,o=new AbortController,{signal:a}=o;t=O({},t,{headers:i,signal:a});let l,u=!1,c=null;s&&s>0&&(c=setTimeout(()=>{u=!0,o.abort("timeout")},s));try{l=await this.hooks.call("fetch:request",r,{url:e,options:t},(v,{url:w,options:x})=>fetch(w,x)),c&&clearTimeout(c)}catch(v){throw u?(this.hooks.call("fetch:timeout",r,{url:e}),new ge(`Request timed out: ${e}`,{url:e,timedOut:u})):v?.name==="AbortError"||a.aborted?new ge(`Request aborted: ${e}`,{url:e,aborted:!0}):v}let{status:d,url:h}=l,p=await l.text();if(d===500)throw this.hooks.call("fetch:error",r,{status:d,response:l,url:h}),new ge(`Server error: ${h}`,{status:d,url:h});if(!p)throw new ge(`Empty response: ${h}`,{status:d,url:h});let{url:g}=N.fromUrl(h),m={url:g,html:p};return!r.cache.write||t.method&&t.method!=="GET"||e!==g||this.cache.set(m.url,m),m}var Hn=class{constructor(t){this.swup=void 0,this.pages=new Map,this.swup=t}get size(){return this.pages.size}get all(){let t=new Map;return this.pages.forEach((n,r)=>{t.set(r,O({},n))}),t}has(t){return this.pages.has(this.resolve(t))}get(t){let n=this.pages.get(this.resolve(t));return n&&O({},n)}set(t,n){n=O({},n,{url:t=this.resolve(t)}),this.pages.set(t,n),this.swup.hooks.callSync("cache:set",void 0,{page:n})}update(t,n){t=this.resolve(t);let r=O({},this.get(t),n,{url:t});this.pages.set(t,r)}delete(t){this.pages.delete(this.resolve(t))}clear(){this.pages.clear(),this.swup.hooks.callSync("cache:clear",void 0,void 0)}prune(t){this.pages.forEach((n,r)=>{t(r,n)&&this.delete(r)})}resolve(t){let{url:n}=N.fromUrl(t);return this.swup.resolveUrl(n)}},Dn=(e,t=document)=>t.querySelector(e),qn=(e,t=document)=>Array.from(t.querySelectorAll(e)),ms=()=>new Promise(e=>{requestAnimationFrame(()=>{requestAnimationFrame(()=>{e()})})});function gs(e){return!!e&&(typeof e=="object"||typeof e=="function")&&typeof e.then=="function"}function Hu(e,t=[]){return new Promise((n,r)=>{let i=e(...t);gs(i)?i.then(n,r):n(i)})}function ds(e,t){let n=e?.closest(`[${t}]`);return n!=null&&n.hasAttribute(t)?n?.getAttribute(t)||!0:void 0}var Bn=class{constructor(t){this.swup=void 0,this.swupClasses=["to-","is-changing","is-rendering","is-popstate","is-animating","is-leaving"],this.swup=t}get selectors(){let{scope:t}=this.swup.visit.animation;return t==="containers"?this.swup.visit.containers:t==="html"?["html"]:Array.isArray(t)?t:[]}get selector(){return this.selectors.join(",")}get targets(){return this.selector.trim()?qn(this.selector):[]}add(...t){this.targets.forEach(n=>n.classList.add(...t))}remove(...t){this.targets.forEach(n=>n.classList.remove(...t))}clear(){this.targets.forEach(t=>{let n=t.className.split(" ").filter(r=>this.isSwupClass(r));t.classList.remove(...n)})}isSwupClass(t){return this.swupClasses.some(n=>t.startsWith(n))}},kt=class{constructor(t,n){this.id=void 0,this.state=void 0,this.from=void 0,this.to=void 0,this.containers=void 0,this.animation=void 0,this.trigger=void 0,this.cache=void 0,this.history=void 0,this.scroll=void 0,this.meta=void 0;let{to:r,from:i,hash:s,el:o,event:a}=n;this.id=Math.random(),this.state=1,this.from={url:i??t.location.url,hash:t.location.hash},this.to={url:r,hash:s},this.containers=t.options.containers,this.animation={animate:!0,wait:!1,name:void 0,native:t.options.native,scope:t.options.animationScope,selector:t.options.animationSelector},this.trigger={el:o,event:a},this.cache={read:t.options.cache,write:t.options.cache},this.history={action:"push",popstate:!1,direction:void 0},this.scroll={reset:!0,target:void 0},this.meta={}}advance(t){this.state=7}};function Du(e){return new kt(this,e)}var Qn=class{constructor(t){this.swup=void 0,this.registry=new Map,this.hooks=["animation:out:start","animation:out:await","animation:out:end","animation:in:start","animation:in:await","animation:in:end","animation:skip","cache:clear","cache:set","content:replace","content:scroll","enable","disable","fetch:request","fetch:error","fetch:timeout","history:popstate","link:click","link:self","link:anchor","link:newtab","page:load","page:view","scroll:top","scroll:anchor","visit:start","visit:transition","visit:abort","visit:end"],this.swup=t,this.init()}init(){this.hooks.forEach(t=>this.create(t))}create(t){this.registry.has(t)||this.registry.set(t,new Map)}exists(t){return this.registry.has(t)}get(t){let n=this.registry.get(t);if(n)return n;console.error(`Unknown hook '${t}'`)}clear(){this.registry.forEach(t=>t.clear())}on(t,n,r={}){let i=this.get(t);if(!i)return console.warn(`Hook '${t}' not found.`),()=>{};let s=O({},r,{id:i.size+1,hook:t,handler:n});return i.set(n,s),()=>this.off(t,n)}before(t,n,r={}){return this.on(t,n,O({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,O({},r,{replace:!0}))}once(t,n,r={}){return this.on(t,n,O({},r,{once:!0}))}off(t,n){let r=this.get(t);r&&n?r.delete(n)||console.warn(`Handler for hook '${t}' not found.`):r&&r.clear()}async call(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);await this.run(l,s,o);let[d]=await this.run(u,s,o,!0);return await this.run(c,s,o),this.dispatchDomEvent(t,s,o),d}callSync(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);this.runSync(l,s,o);let[d]=this.runSync(u,s,o,!0);return this.runSync(c,s,o),this.dispatchDomEvent(t,s,o),d}parseCallArgs(t,n,r,i){return n instanceof kt||typeof n!="object"&&typeof r!="function"?[n,r,i]:[void 0,n,r]}async run(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=await Hu(a,[n,r,l]);s.push(c)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}runSync(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=a(n,r,l);s.push(c),gs(c)&&console.warn(`Swup will not await Promises in handler for synchronous hook '${o}'.`)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}getHandlers(t,n){let r=this.get(t);if(!r)return{found:!1,before:[],handler:[],after:[],replaced:!1};let i=Array.from(r.values()),s=this.sortRegistrations,o=i.filter(({before:d,replace:h})=>d&&!h).sort(s),a=i.filter(({replace:d})=>d).filter(d=>!0).sort(s),l=i.filter(({before:d,replace:h})=>!d&&!h).sort(s),u=a.length>0,c=[];if(n&&(c=[{id:0,hook:t,handler:n}],u)){let d=a.length-1,{handler:h,once:p}=a[d],g=m=>{let v=a[m-1];return v?(w,x)=>v.handler(w,x,g(m-1)):n};c=[{id:0,hook:t,once:p,handler:h,defaultHandler:g(d)}]}return{found:!0,before:o,handler:c,after:l,replaced:u}}sortRegistrations(t,n){var r,i;return((r=t.priority)!=null?r:0)-((i=n.priority)!=null?i:0)||t.id-n.id||0}dispatchDomEvent(t,n,r){if(n!=null&&n.done)return;let i={hook:t,args:r,visit:n||this.swup.visit};document.dispatchEvent(new CustomEvent("swup:any",{detail:i,bubbles:!0})),document.dispatchEvent(new CustomEvent(`swup:${t}`,{detail:i,bubbles:!0}))}parseName(t){let[n,...r]=t.split(".");return[n,r.reduce((i,s)=>O({},i,{[s]:!0}),{})]}},Bu=e=>{if(e&&e.charAt(0)==="#"&&(e=e.substring(1)),!e)return null;let t=decodeURIComponent(e),n=document.getElementById(e)||document.getElementById(t)||Dn(`a[name='${CSS.escape(e)}']`)||Dn(`a[name='${CSS.escape(t)}']`);return n||e!=="top"||(n=document.body),n},Et="transition",Nn="animation";async function Qu({selector:e,elements:t}){if(e===!1&&!t)return;let n=[];if(t)n=Array.from(t);else if(e&&(n=qn(e,document.body),!n.length))return void console.warn(`[swup] No elements found matching animationSelector \`${e}\``);let r=n.map(i=>(function(s){let{type:o,timeout:a,propCount:l}=(function(u){let c=window.getComputedStyle(u),d=xt(c,`${Et}Delay`),h=xt(c,`${Et}Duration`),p=hs(d,h),g=xt(c,`${Nn}Delay`),m=xt(c,`${Nn}Duration`),v=hs(g,m),w=Math.max(p,v),x=w>0?p>v?Et:Nn:null;return{type:x,timeout:w,propCount:x?x===Et?h.length:m.length:0}})(s);return!(!o||!a)&&new Promise(u=>{let c=`${o}end`,d=performance.now(),h=0,p=()=>{s.removeEventListener(c,g),u()},g=m=>{m.target===s&&((performance.now()-d)/1e3=l&&p())};setTimeout(()=>{h0?await Promise.all(r):e&&console.warn(`[swup] No CSS animation duration defined on elements matching \`${e}\``)}function xt(e,t){return(e[t]||"").split(", ")}function hs(e,t){for(;e.lengthfs(n)+fs(e[r])))}function fs(e){return 1e3*parseFloat(e)}function qu(e,t={},n={}){if(typeof e!="string")throw new Error("swup.navigate() requires a URL parameter");if(this.shouldIgnoreVisit(e,{el:n.el,event:n.event}))return void window.location.assign(e);let{url:r,hash:i}=N.fromUrl(e),s=this.createVisit(O({},n,{to:r,hash:i}));this.performNavigation(s,t)}async function Fu(e,t={}){if(this.navigating){if(this.visit.state>=6)return e.state=2,void(this.onVisitEnd=()=>this.performNavigation(e,t));await this.hooks.call("visit:abort",this.visit,void 0),delete this.visit.to.document,this.visit.state=8}this.navigating=!0,this.visit=e;let{el:n}=e.trigger;t.referrer=t.referrer||this.location.url,t.animate===!1&&(e.animation.animate=!1),e.animation.animate||this.classes.clear();let r=t.history||ds(n,"data-swup-history");typeof r=="string"&&["push","replace"].includes(r)&&(e.history.action=r);let i=t.animation||ds(n,"data-swup-animation");var s,o;typeof i=="string"&&(e.animation.name=i),e.meta=t.meta||{},typeof t.cache=="object"?(e.cache.read=(s=t.cache.read)!=null?s:e.cache.read,e.cache.write=(o=t.cache.write)!=null?o:e.cache.write):t.cache!==void 0&&(e.cache={read:!!t.cache,write:!!t.cache}),delete t.cache;try{await this.hooks.call("visit:start",e,void 0),e.state=3;let a=this.hooks.call("page:load",e,{options:t},async(u,c)=>{let d;return u.cache.read&&(d=this.cache.get(u.to.url)),c.page=d||await this.fetchPage(u.to.url,c.options),c.cache=!!d,c.page});a.then(({html:u})=>{e.advance(5),e.to.html=u,e.to.document=new DOMParser().parseFromString(u,"text/html")});let l=e.to.url+e.to.hash;if(e.history.popstate||(e.history.action==="replace"||e.to.url===this.location.url?Pe(l):(this.currentHistoryIndex++,Ru(l,{index:this.currentHistoryIndex}))),this.location=N.fromUrl(l),e.history.popstate&&this.classes.add("is-popstate"),e.animation.name&&this.classes.add(`to-${ps(e.animation.name)}`),e.animation.wait&&await a,e.done||(await this.hooks.call("visit:transition",e,void 0,async()=>{if(!e.animation.animate)return await this.hooks.call("animation:skip",void 0),void await this.renderPage(e,await a);e.advance(4),await this.animatePageOut(e),e.animation.native&&document.startViewTransition?await document.startViewTransition(async()=>await this.renderPage(e,await a)).finished:await this.renderPage(e,await a),await this.animatePageIn(e)}),e.done))return;await this.hooks.call("visit:end",e,void 0,()=>this.classes.clear()),e.state=7,this.navigating=!1,this.onVisitEnd&&(this.onVisitEnd(),this.onVisitEnd=void 0)}catch(a){if(!a||a!=null&&a.aborted)return void(e.state=8);e.state=9,console.error(a),this.options.skipPopStateHandling=()=>(window.location.assign(e.to.url+e.to.hash),!0),window.history.back()}finally{delete e.to.document}}var Vu=async function(e){await this.hooks.call("animation:out:start",e,void 0,()=>{this.classes.add("is-changing","is-animating","is-leaving")}),await this.hooks.call("animation:out:await",e,{skip:!1},(t,{skip:n})=>{if(!n)return this.awaitAnimations({selector:t.animation.selector})}),await this.hooks.call("animation:out:end",e,void 0)},$u=function(e){var t;let n=e.to.document;if(!n)return!1;let r=((t=n.querySelector("title"))==null?void 0:t.innerText)||"";document.title=r;let i=qn('[data-swup-persist]:not([data-swup-persist=""])'),s=e.containers.map(o=>{let a=document.querySelector(o),l=n.querySelector(o);return a&&l?(a.replaceWith(l.cloneNode(!0)),!0):(a||console.warn(`[swup] Container missing in current document: ${o}`),l||console.warn(`[swup] Container missing in incoming document: ${o}`),!1)}).filter(Boolean);return i.forEach(o=>{let a=o.getAttribute("data-swup-persist"),l=Dn(`[data-swup-persist="${a}"]`);l&&l!==o&&l.replaceWith(o)}),s.length===e.containers.length},Uu=function(e){let t={behavior:"auto"},{target:n,reset:r}=e.scroll,i=n??e.to.hash,s=!1;return i&&(s=this.hooks.callSync("scroll:anchor",e,{hash:i,options:t},(o,{hash:a,options:l})=>{let u=this.getAnchorElement(a);return u&&u.scrollIntoView(l),!!u})),r&&!s&&(s=this.hooks.callSync("scroll:top",e,{options:t},(o,{options:a})=>(window.scrollTo(O({top:0,left:0},a)),!0))),s},ju=async function(e){if(e.done)return;let t=this.hooks.call("animation:in:await",e,{skip:!1},(n,{skip:r})=>{if(!r)return this.awaitAnimations({selector:n.animation.selector})});await ms(),await this.hooks.call("animation:in:start",e,void 0,()=>{this.classes.remove("is-animating")}),await t,await this.hooks.call("animation:in:end",e,void 0)},Wu=async function(e,t){if(e.done)return;e.advance(6);let{url:n}=t;this.isSameResolvedUrl(Oe(),n)||(Pe(n),this.location=N.fromUrl(n),e.to.url=this.location.url,e.to.hash=this.location.hash),await this.hooks.call("content:replace",e,{page:t},(r,{})=>{if(this.classes.remove("is-leaving"),r.animation.animate&&this.classes.add("is-rendering"),!this.replaceContent(r))throw new Error("[swup] Container mismatch, aborting");r.animation.animate&&(this.classes.add("is-changing","is-animating","is-rendering"),r.animation.name&&this.classes.add(`to-${ps(r.animation.name)}`))}),await this.hooks.call("content:scroll",e,void 0,()=>this.scrollToContent(e)),await this.hooks.call("page:view",e,{url:this.location.url,title:document.title})},zu=function(e){var t;if(t=e,!!t?.isSwupPlugin){if(e.swup=this,!e._checkRequirements||e._checkRequirements())return e._beforeMount&&e._beforeMount(),e.mount(),this.plugins.push(e),this.plugins}else console.error("Not a swup plugin instance",e)};function Gu(e){let t=this.findPlugin(e);if(t)return t.unmount(),t._afterUnmount&&t._afterUnmount(),this.plugins=this.plugins.filter(n=>n!==t),this.plugins;console.error("No such plugin",t)}function Ku(e){return this.plugins.find(t=>t===e||t.name===e||t.name===`Swup${String(e)}`)}function Yu(e){if(typeof this.options.resolveUrl!="function")return console.warn("[swup] options.resolveUrl expects a callback function."),e;let t=this.options.resolveUrl(e);return t&&typeof t=="string"?t.startsWith("//")||t.startsWith("http")?(console.warn("[swup] options.resolveUrl needs to return a relative url"),e):t:(console.warn("[swup] options.resolveUrl needs to return a url"),e)}function Ju(e,t){return this.resolveUrl(e)===this.resolveUrl(t)}var Xu={animateHistoryBrowsing:!1,animationSelector:'[class*="transition-"]',animationScope:"html",cache:!0,containers:["#swup"],hooks:{},ignoreVisit:(e,{el:t}={})=>!(t==null||!t.closest("[data-no-swup]")),linkSelector:"a[href]",linkToSelf:"scroll",native:!1,plugins:[],resolveUrl:e=>e,requestHeaders:{"X-Requested-With":"swup",Accept:"text/html, application/xhtml+xml"},skipPopStateHandling:e=>{var t;return((t=e.state)==null?void 0:t.source)!=="swup"},timeout:0},St=class{get currentPageUrl(){return this.location.url}constructor(t={}){var n,r;this.version="4.8.2",this.options=void 0,this.defaults=Xu,this.plugins=[],this.visit=void 0,this.cache=void 0,this.hooks=void 0,this.classes=void 0,this.location=N.fromUrl(window.location.href),this.currentHistoryIndex=void 0,this.clickDelegate=void 0,this.navigating=!1,this.onVisitEnd=void 0,this.use=zu,this.unuse=Gu,this.findPlugin=Ku,this.log=()=>{},this.navigate=qu,this.performNavigation=Fu,this.createVisit=Du,this.delegateEvent=Mu,this.fetchPage=Nu,this.awaitAnimations=Qu,this.renderPage=Wu,this.replaceContent=$u,this.animatePageIn=ju,this.animatePageOut=Vu,this.scrollToContent=Uu,this.getAnchorElement=Bu,this.getCurrentUrl=Oe,this.resolveUrl=Yu,this.isSameResolvedUrl=Ju,this.options=O({},this.defaults,t),this.handleLinkClick=this.handleLinkClick.bind(this),this.handlePopState=this.handlePopState.bind(this),this.cache=new Hn(this),this.classes=new Bn(this),this.hooks=new Qn(this),this.visit=this.createVisit({to:""}),this.currentHistoryIndex=(n=(r=window.history.state)==null?void 0:r.index)!=null?n:1,this.enable()}async enable(){var t;let{linkSelector:n}=this.options;this.clickDelegate=this.delegateEvent(n,"click",this.handleLinkClick),window.addEventListener("popstate",this.handlePopState),this.options.animateHistoryBrowsing&&(window.history.scrollRestoration="manual"),this.options.native=this.options.native&&!!document.startViewTransition,this.options.plugins.forEach(r=>this.use(r));for(let[r,i]of Object.entries(this.options.hooks)){let[s,o]=this.hooks.parseName(r);this.hooks.on(s,i,o)}((t=window.history.state)==null?void 0:t.source)!=="swup"&&Pe(null,{index:this.currentHistoryIndex}),await ms(),await this.hooks.call("enable",void 0,void 0,()=>{let r=document.documentElement;r.classList.add("swup-enabled"),r.classList.toggle("swup-native",this.options.native)})}async destroy(){this.clickDelegate.destroy(),window.removeEventListener("popstate",this.handlePopState),this.cache.clear(),this.options.plugins.forEach(t=>this.unuse(t)),await this.hooks.call("disable",void 0,void 0,()=>{let t=document.documentElement;t.classList.remove("swup-enabled"),t.classList.remove("swup-native")}),this.hooks.clear()}shouldIgnoreVisit(t,{el:n,event:r}={}){let{origin:i,url:s,hash:o}=N.fromUrl(t);return i!==window.location.origin||!(!n||!this.triggerWillOpenNewWindow(n))||!!this.options.ignoreVisit(s+o,{el:n,event:r})}handleLinkClick(t){let n=t.delegateTarget,{href:r,url:i,hash:s}=N.fromElement(n);if(this.shouldIgnoreVisit(r,{el:n,event:t}))return;if(this.navigating&&i===this.visit.to.url)return void t.preventDefault();let o=this.createVisit({to:i,hash:s,el:n,event:t});t.metaKey||t.ctrlKey||t.shiftKey||t.altKey?this.hooks.callSync("link:newtab",o,{href:r}):t.button===0&&this.hooks.callSync("link:click",o,{el:n,event:t},()=>{var a;let l=(a=o.from.url)!=null?a:"";t.preventDefault(),i&&i!==l?this.isSameResolvedUrl(i,l)||this.performNavigation(o):s?this.hooks.callSync("link:anchor",o,{hash:s},()=>{Pe(i+s),this.scrollToContent(o)}):this.hooks.callSync("link:self",o,void 0,()=>{this.options.linkToSelf==="navigate"?this.performNavigation(o):(Pe(i),this.scrollToContent(o))})})}handlePopState(t){var n,r,i,s;let o=(n=(r=t.state)==null?void 0:r.url)!=null?n:window.location.href;if(this.options.skipPopStateHandling(t)||this.isSameResolvedUrl(Oe(),this.location.url))return;let{url:a,hash:l}=N.fromUrl(o),u=this.createVisit({to:a,hash:l,event:t});u.history.popstate=!0;let c=(i=(s=t.state)==null?void 0:s.index)!=null?i:0;c&&c!==this.currentHistoryIndex&&(u.history.direction=c-this.currentHistoryIndex>0?"forwards":"backwards",this.currentHistoryIndex=c),u.animation.animate=!1,u.scroll.reset=!1,u.scroll.target=!1,this.options.animateHistoryBrowsing&&(u.animation.animate=!0,u.scroll.reset=!0),this.hooks.callSync("history:popstate",u,{event:t},()=>{this.performNavigation(u)})}triggerWillOpenNewWindow(t){return!!t.matches('[download], [target="_blank"]')}};function Ae(){return Ae=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).split(".").map(t=>String(parseInt(t||"0",10))).concat(["0","0"]).slice(0,3).join("."),ve=class{constructor(){this.isSwupPlugin=!0,this.swup=void 0,this.version=void 0,this.requires={},this.handlersToUnregister=[]}mount(){}unmount(){this.handlersToUnregister.forEach(t=>t()),this.handlersToUnregister=[]}_beforeMount(){if(!this.name)throw new Error("You must define a name of plugin when creating a class.")}_afterUnmount(){}_checkRequirements(){return typeof this.requires!="object"||Object.entries(this.requires).forEach(([t,n])=>{if(!(function(r,i,s){let o=(function(a,l){var u;if(a==="swup")return(u=l.version)!=null?u:"";{var c;let d=l.findPlugin(a);return(c=d?.version)!=null?c:""}})(r,s);return!!o&&((a,l)=>l.every(u=>{let[,c,d]=u.match(/^([\D]+)?(.*)$/)||[];var h,p;return((g,m)=>{let v={"":w=>w===0,">":w=>w>0,">=":w=>w>=0,"<":w=>w<0,"<=":w=>w<=0};return(v[m]||v[""])(g)})((p=d,h=vs(h=a),p=vs(p),h.localeCompare(p,void 0,{numeric:!0})),c||">=")}))(o,i)})(t,n=Array.isArray(n)?n:[n],this.swup)){let r=`${t} ${n.join(", ")}`;throw new Error(`Plugin version mismatch: ${this.name} requires ${r}`)}}),!0}on(t,n,r={}){var i;n=!(i=n).name.startsWith("bound ")||i.hasOwnProperty("prototype")?n.bind(this):n;let s=this.swup.hooks.on(t,n,r);return this.handlersToUnregister.push(s),s}once(t,n,r={}){return this.on(t,n,Ae({},r,{once:!0}))}before(t,n,r={}){return this.on(t,n,Ae({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,Ae({},r,{replace:!0}))}off(t,n){return this.swup.hooks.off(t,n)}};(function(){if(!(typeof window>"u"||typeof document>"u"||typeof HTMLElement>"u")){var e=!1;try{var t=document.createElement("div");t.addEventListener("focus",function(s){s.preventDefault(),s.stopPropagation()},!0),t.focus(Object.defineProperty({},"preventScroll",{get:function(){if(navigator&&typeof navigator.userAgent<"u"&&navigator.userAgent&&navigator.userAgent.match(/Edge\/1[7-8]/))return e=!1;e=!0}}))}catch{}if(HTMLElement.prototype.nativeFocus===void 0&&!e){HTMLElement.prototype.nativeFocus=HTMLElement.prototype.focus;var n=function(s){for(var o=s.parentNode,a=[],l=document.scrollingElement||document.documentElement;o&&o!==l;)(o.offsetHeightn.replace(`{${r}}`,t[r]||""),e||"")}var Vn=class{constructor(){var t;this.id="swup-announcer",this.style="position:absolute;top:0;left:0;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden;white-space:nowrap;word-wrap:normal;width:1px;height:1px;",this.region=void 0,this.region=(t=this.getRegion())!=null?t:this.createRegion()}getRegion(){return document.getElementById(this.id)}createRegion(){let t=(function(n){let r=document.createElement("template");return r.innerHTML=n,r.content.children[0]})(`

`);return document.body.appendChild(t),t}announce(t,n=0){return new Promise(r=>{setTimeout(()=>{this.region.textContent===t&&(t=`${t}.`),this.region.textContent="",this.region.textContent=t,r()},n)})}};function ws(e){let t;if(t=typeof e=="string"?document.querySelector(e):e,!(t instanceof HTMLElement))return;let n=t.getAttribute("tabindex");t.setAttribute("tabindex","-1"),t.focus({preventScroll:!0}),n!==null&&t.setAttribute("tabindex",n)}var _t=class extends ve{constructor(t={}){super(),this.name="SwupA11yPlugin",this.requires={swup:">=4"},this.defaults={headingSelector:"h1",respectReducedMotion:!0,autofocus:!1,announcements:{visit:"Navigated to: {title}",url:"New page at {url}"}},this.options=void 0,this.announcer=void 0,this.announcementDelay=100,this.rootSelector="body",this.handleAnchorScroll=(n,{hash:r})=>{let i=this.swup.getAnchorElement(r);i instanceof HTMLElement&&ws(i)},this.options=Fn({},this.defaults,t),this.announcer=new Vn}mount(){this.swup.hooks.create("content:announce"),this.swup.hooks.create("content:focus"),this.before("visit:start",this.prepareVisit),this.on("visit:start",this.markAsBusy),this.on("visit:end",this.unmarkAsBusy),this.on("visit:end",this.focusContent),this.on("visit:end",this.announceContent),this.on("scroll:anchor",this.handleAnchorScroll),this.before("visit:start",this.disableAnimations),this.before("link:self",this.disableAnimations),this.before("link:anchor",this.disableAnimations),this.swup.announce=this.announce.bind(this)}unmount(){this.swup.announce=void 0}async announce(t){await this.announcer.announce(t)}markAsBusy(){document.documentElement.setAttribute("aria-busy","true")}unmarkAsBusy(){document.documentElement.removeAttribute("aria-busy")}prepareVisit(t){t.a11y={announce:void 0,focus:this.rootSelector}}announceContent(t){this.swup.hooks.callSync("content:announce",t,void 0,n=>{n.a11y.announce===void 0&&(n.a11y.announce=this.getPageAnnouncement()),n.a11y.announce&&this.announcer.announce(n.a11y.announce,this.announcementDelay)})}focusContent(t){this.swup.hooks.callSync("content:focus",t,void 0,n=>{n.a11y.focus&&(this.options.autofocus&&(function(){let r=(function(){let i=document.querySelector("body [autofocus]");if(i&&!i.closest('[inert], [aria-disabled], [aria-hidden="true"]'))return i})();return!!r&&(r!==document.activeElement&&r.focus(),!0)})()===!0||ws(n.a11y.focus))})}getPageAnnouncement(){let{headingSelector:t,announcements:n}=this.options;return(function({headingSelector:r="h1",announcements:i={}}){var s,o;let a=document.documentElement.lang||"*",{href:l,url:u,pathname:c}=N.fromUrl(window.location.href),d=(s=(o=i[a])!=null?o:i["*"])!=null?s:i;if(typeof d!="object")return;let h=document.querySelector(r);h||console.warn(`SwupA11yPlugin: No main heading (${r}) found on new page`);let p=h?.getAttribute("aria-label")||h?.textContent||document.title||ys(d.url,{href:l,url:u,path:c});return ys(d.visit,{title:p,href:l,url:u,path:c})})({headingSelector:t,announcements:n})}disableAnimations(t){this.options.respectReducedMotion&&window.matchMedia("(prefers-reduced-motion: reduce)").matches&&(t.animation.animate=!1,t.scroll.animate=!1)}};function $n(){return $n=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let a=Math.random()*this.trickleValue;this.setValue(this.value+a)},t!==void 0&&(this.className=String(t)),n!==void 0&&(this.styleAttr=String(n)),r!==void 0&&(this.animationDuration=Number(r)),i!==void 0&&(this.minValue=Number(i)),s!==void 0&&(this.initialValue=Number(s)),o!==void 0&&(this.trickleValue=Number(o)),this.styleElement=this.createStyleElement(),this.progressElement=this.createProgressElement()}get defaultStyles(){return` + .${this.className} { + position: fixed; + display: block; + top: 0; + left: 0; + width: 100%; + height: 3px; + background-color: black; + z-index: 9999; + transition: + transform ${this.animationDuration}ms ease-out, + opacity ${this.animationDuration/2}ms ${this.animationDuration/2}ms ease-in; + transform: translate3d(0, 0, 0) scaleX(var(--progress, 0)); + transform-origin: 0; + } + `}show(){this.visible||(this.visible=!0,this.installStyleElement(),this.installProgressElement(),this.startTrickling())}hide(){this.visible&&!this.hiding&&(this.hiding=!0,this.fadeProgressElement(()=>{this.uninstallProgressElement(),this.stopTrickling(),this.visible=!1,this.hiding=!1}))}setValue(t){this.value=Math.min(1,Math.max(this.minValue,t)),this.refresh()}installStyleElement(){document.head.prepend(this.styleElement)}installProgressElement(){this.progressElement.style.setProperty("--progress",String(0)),this.progressElement.style.opacity="1",document.body.prepend(this.progressElement),this.progressElement.scrollTop=0,this.setValue(Math.random()*this.initialValue)}fadeProgressElement(t){this.progressElement.style.opacity="0",setTimeout(t,1.5*this.animationDuration)}uninstallProgressElement(){this.progressElement.remove()}startTrickling(){this.trickleInterval||(this.trickleInterval=window.setInterval(this.trickle,this.animationDuration))}stopTrickling(){window.clearInterval(this.trickleInterval),delete this.trickleInterval}refresh(){requestAnimationFrame(()=>{this.progressElement.style.setProperty("--progress",String(this.value))})}createStyleElement(){let t=document.createElement("style");return this.styleAttr.split(" ").forEach(n=>t.setAttribute(n,"")),t.textContent=this.defaultStyles,t}createProgressElement(){let t=document.createElement("div");return t.className=this.className,t.setAttribute("aria-hidden","true"),t}},Lt=class extends ve{constructor(t={}){super(),this.name="SwupProgressPlugin",this.defaults={className:"swup-progress-bar",delay:300,transition:300,minValue:.1,initialValue:.25,finishAnimation:!0},this.options=void 0,this.progressBar=void 0,this.showProgressBarTimeout=void 0,this.hideProgressBarTimeout=void 0,this.options=$n({},this.defaults,t);let{className:n,minValue:r,initialValue:i,transition:s}=this.options;this.progressBar=new Un({className:n,minValue:r,initialValue:i,animationDuration:s})}mount(){this.on("visit:start",this.startShowingProgress),this.on("page:view",this.stopShowingProgress)}startShowingProgress(){this.progressBar.setValue(0),this.showProgressBarAfterDelay()}stopShowingProgress(){this.progressBar.setValue(1),this.options.finishAnimation?this.finishAnimationAndHideProgressBar():this.hideProgressBar()}showProgressBar(){this.cancelHideProgressBarTimeout(),this.progressBar.show()}showProgressBarAfterDelay(){this.cancelShowProgressBarTimeout(),this.cancelHideProgressBarTimeout(),this.showProgressBarTimeout=window.setTimeout(this.showProgressBar.bind(this),this.options.delay)}hideProgressBar(){this.cancelShowProgressBarTimeout(),this.progressBar.hide()}finishAnimationAndHideProgressBar(){this.cancelShowProgressBarTimeout(),this.hideProgressBarTimeout=window.setTimeout(this.hideProgressBar.bind(this),this.options.transition)}cancelShowProgressBarTimeout(){window.clearTimeout(this.showProgressBarTimeout),delete this.showProgressBarTimeout}cancelHideProgressBarTimeout(){window.clearTimeout(this.hideProgressBarTimeout),delete this.hideProgressBarTimeout}};var bs=()=>{window.dispatchEvent(new Event("exdoc:loaded"))},Zu=(e,{page:t})=>{//i.test(t.html)&&(e.abort(),window.location.reload())};window.addEventListener("DOMContentLoaded",bs);!C&&window.location.protocol!=="file:"&&new St({animationSelector:!1,containers:["#main"],ignoreVisit:e=>{let t=e.split("#")[0];return t===window.location.pathname||t===window.location.pathname+".html"},linkSelector:'a[href]:not([href^="/"]):not([href^="http"])[href$=".html"]',hooks:{"page:load":Zu,"page:view":bs},plugins:[new _t,new Lt({delay:500})]});})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-elixir-NHQLCD6Y.css b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-elixir-NHQLCD6Y.css new file mode 100644 index 0000000..d3a7bb7 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-elixir-NHQLCD6Y.css @@ -0,0 +1,6 @@ +:root{--main: hsl(250, 68%, 69%);--mainDark: hsl(250, 68%, 59%);--mainDarkest: hsl(250, 68%, 49%);--mainLight: hsl(250, 68%, 74%);--mainLightest: hsl(250, 68%, 79%);--searchBarFocusColor: #8E7CE6;--searchBarBorderColor: rgba(142, 124, 230, .25);--link-color: var(--mainDark);--link-visited-color: var(--mainDarkest)}body.dark{--link-color: var(--mainLightest);--link-visited-color: var(--mainLight)}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-ext-400-normal-N27NCBWW.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-400-normal-W7754I4D.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-ext-700-normal-Q2L5DVMW.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-700-normal-2XVSBPG4.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}body.dark{--background: var(--gray900);--contrast: var(--white);--textBody: var(--gray200);--textHeaders: var(--gray100);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--gray700);--iconAction: var(--coldGray-lightened-10);--iconActionHover: var(--white);--blockquoteBackground: var(--coldGray-opacity-10);--blockquoteBorder: var(--coldGrayDim);--tableHeadBorder: var(--gray600);--tableBodyBorder: var(--gray700);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray900);--admMixAmt: 40%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 40%, 14%) 100%);--warningHeading: hsl(var(--warningHue), 100%, 50%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 40%, 14%) 100%);--errorHeading: hsl(var(--errorHue), 100%, 52%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 60%, 15%) 100%);--infoHeading: hsl(var(--infoHue), 100%, 52%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 14%) 100%);--neutralHeading: hsl(var(--neutralHue), 50%, 80%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 14%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 48%);--fnSpecAttr: var(--gray400);--fnDeprecated: var(--yellowDark);--blink: var(--gray600);--codeBackground: var(--gray800);--codeBorder: var(--gray650);--codeScrollThumb: var(--gray500);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--black-opacity-30);--admCodeBorder: var(--white-opacity-10);--admCodeColor: var(--gray100);--tabBorder: var(--gray700);--tabBorderTop: var(--gray700);--tabShadow: var(--black);--bottomActionsBtnBorder: var(--white-opacity-10);--bottomActionsBtnTitle: var(--mainLightest);--modalBackground: var(--gray800);--settingsInput: var(--white);--settingsInputBackground: var(--gray700);--settingsInputBorder: var(--gray700);--settingsSectionBorder: var(--gray700);--quickSwitchInput: var(--gray300);--quickSwitchContour: var(--gray500);--success: var(--green-lightened-10);--progressBarColor: var(--gray300);--sidebarAccentMain: var(--gray50);--sidebarBackground: var(--gray800);--sidebarHeader: var(--gray700);--sidebarMuted: var(--gray300);--sidebarHover: var(--white);--sidebarStaleVersion: var(--orangeLight);--sidebarSubheadings: var(--gray400);--sidebarItem: var(--gray200);--sidebarInactiveItemBorder: var(--gray400);--sidebarInactiveItemMarker: var(--gray600);--sidebarLanguageAccentBar: var(--mainLight);--sidebarActiveItem: var(--mainLightest);--searchBarBorder: var(--gray500);--searchAccentMain: var(--gray300);--searchSearch: var(--gray900);--autocompleteBorder: rgba(28,42,60,.75);--autocompletePreview: var(--gray750);--autocompleteSelected: var(--gray750);--autocompleteHover: var(--gray700);--autocompleteBackground: var(--gray800);--suggestionBorder: var(--gray600);--autocompleteResults: var(--gray200);--autocompleteResultsBold: var(--gray100);--autocompleteLabelBack: var(--gray600);--autocompleteLabelFont: rgba(255, 255, 255, .8)}:root:has(body.dark){color-scheme:dark}*,:before,:after{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}@font-face{font-family:remixicon;src:url("./remixicon-ZWU5GQ4X.woff2") format("woff2");font-display:swap}[class^=ri-],[class*=" ri-"],.remix-icon{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--icon-arrow-up-s: "\ea78";--icon-arrow-down-s: "\ea4e";--icon-arrow-right-s: "\ea6e";--icon-add: "\ea13";--icon-subtract: "\f1af";--icon-error-warning: "\eca1";--icon-external-link-line: "\ecaf";--icon-information: "\ee59";--icon-alert: "\ea21";--icon-double-quotes-l: "\ec51";--icon-link-m: "\eeaf";--icon-close-line: "\eb99";--icon-code-s-slash-line: "\ebad";--icon-menu-line: "\ef3e";--icon-search-2-line: "\f0cd";--icon-settings-3-line: "\f0e6";--icon-printer-line: "\f029";--icon-markdown-line: "\ef1e"}.ri-lg{font-size:1.3333em;line-height:.75em;vertical-align:-.0667em}.ri-settings-3-line:before{content:var(--icon-settings-3-line)}.ri-add-line:before{content:var(--icon-add)}.ri-subtract-line:before{content:var(--icon-subtract)}.ri-arrow-up-s-line:before{content:var(--icon-arrow-up-s)}.ri-arrow-down-s-line:before{content:var(--icon-arrow-down-s)}.ri-arrow-right-s-line:before{content:var(--icon-arrow-right-s)}.ri-external-link-line:before{content:var(--icon-external-link-line)}.ri-search-2-line:before{content:var(--icon-search-2-line)}.ri-menu-line:before{content:var(--icon-menu-line)}.ri-close-line:before{content:var(--icon-close-line)}.ri-link-m:before{content:var(--icon-link-m)}.ri-code-s-slash-line:before{content:var(--icon-code-s-slash-line)}.ri-error-warning-line:before{content:var(--icon-error-warning)}.ri-information-line:before{content:var(--icon-information)}.ri-alert-line:before{content:var(--icon-alert)}.ri-double-quotes-l:before{content:var(--icon-double-quotes-l)}.ri-printer-line:before{content:var(--icon-printer-line)}.ri-markdown-line:before{content:var(--icon-markdown-line)}html,body{box-sizing:border-box;height:100%;width:100%}body{--sidebarWidth: 300px;--sidebarMinWidth: 300px;--sidebarTransitionDuration: .3s;background-color:var(--background);color:var(--textBody);font-size:var(--text-md);line-height:1.6875em;outline:none!important}*,*:before,*:after{box-sizing:inherit}.body-wrapper{display:flex;height:100%}.sidebar{display:none;flex-direction:column;width:var(--sidebarWidth);min-width:var(--sidebarMinWidth);max-width:50vw;height:100%;position:fixed;top:0;left:calc(-1 * var(--sidebarWidth));z-index:100;resize:horizontal}.sidebar-button{padding:20px 12px 18px 19px;position:fixed;z-index:200;top:0;left:0;will-change:transform;transform:translate(0)}.content{left:0;width:100%;height:100%;position:absolute}.content .content-inner{container:content / inline-size;max-width:var(--content-width);min-height:100%;margin:0 auto;padding:0 var(--content-gutter) 10px}.content-inner:focus{outline:none}.sidebar-transition .sidebar,.sidebar-transition .sidebar-button,.sidebar-transition .content{transition:all var(--sidebarTransitionDuration) ease-in-out allow-discrete}.sidebar-open .sidebar,.sidebar-transition .sidebar{display:flex}.sidebar-open .sidebar{left:0}.sidebar-open .sidebar-button{transform:translate(calc(var(--sidebarWidth) - 100%))}.sidebar-open .content{width:calc(100% - var(--sidebarWidth));left:var(--sidebarWidth)}@media screen and (max-width:768px){.sidebar-open .content{left:0;width:100%}.sidebar{max-width:90vw}body:not(.sidebar-open) .sidebar-button{position:absolute}}.swup-progress-bar{height:2px;background-color:var(--progressBarColor)}.sidebar{--sidebarFontSize: 16px;--sidebarLineHeight: 20px;font-family:var(--sansFontFamily);font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);background-color:var(--sidebarBackground);color:var(--sidebarAccentMain);overflow:hidden;.sidebar-tabpanel{scrollbar-width:thin}}.apple-os .sidebar{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar ul{list-style:none}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:var(--sidebarAccentMain);text-decoration:none;transition:var(--transition-colors)}.sidebar a:hover{color:var(--sidebarHover)}.sidebar .external-link{margin-left:2.5px}.sidebar .sidebar-header{background-color:var(--sidebarHeader);width:100%}.sidebar .sidebar-projectInfo{display:flex;justify-content:start;align-items:center;gap:8px;margin:8px 34px 12px 14px}.sidebar .sidebar-projectInfo>div{flex:1}.sidebar .sidebar-projectImage{align-self:flex-end}.sidebar .sidebar-projectImage img{display:block;max-width:48px;max-height:48px}.sidebar .sidebar-projectName{font-weight:700;font-size:var(--text-xl);line-height:24px;color:var(--sidebarAccentMain);margin:0;padding:0;word-wrap:break-word;display:block;width:calc(100% - 12px)}.sidebar .sidebar-projectVersion{display:block;position:relative;margin:0;padding:0;font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);color:var(--sidebarMuted);width:calc(100% - 12px)}.sidebar .sidebar-projectVersion form{display:flex}.sidebar .sidebar-projectVersion select{cursor:pointer;position:relative;margin:0;padding:0 0 0 10px;border:none;-webkit-appearance:none;appearance:none;background-color:transparent;color:var(--sidebarMuted);z-index:2}.sidebar .sidebar-projectVersion option{color:initial}.sidebar .sidebar-projectVersionsCaret{position:absolute;left:0;top:2px;z-index:1;font-size:8px;color:var(--sidebarMuted)}.sidebar .sidebar-projectVersion select::-ms-expand{display:none}.sidebar .sidebar-staleVersion{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--sidebarStaleVersion)}.sidebar .sidebar-staleVersion>a{color:var(--sidebarStaleVersion);font-weight:400}.sidebar .sidebar-staleIcon{font-size:var(--text-lg);line-height:0}.sidebar .sidebar-list-nav{display:flex;margin:0;padding:0;overflow:auto;scrollbar-width:thin}.sidebar .sidebar-list-nav :is(li,li button){text-transform:uppercase;letter-spacing:.02em;font-size:var(--text-sm);color:var(--sidebarSubheadings);white-space:nowrap}.sidebar .sidebar-list-nav li{display:inline-block;padding:0}.sidebar .sidebar-list-nav button{background:none;border:0;border-radius:0;-webkit-appearance:none;text-align:inherit;color:inherit;font-weight:inherit;cursor:pointer;display:inline-block;line-height:27px;padding:4px 14px;transition:var(--transition-all)}.sidebar .sidebar-list-nav button{border-bottom:var(--navTabBorderWidth) solid transparent}.sidebar .sidebar-list-nav button:not([aria-selected]):hover{border-bottom:var(--navTabBorderWidth) solid var(--sidebarInactiveItemBorder);color:var(--sidebarAccentMain);transition:var(--transition-all)}.sidebar .sidebar-list-nav button[aria-selected]{border-bottom:var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar);color:var(--sidebarAccentMain)}.sidebar .sidebar-tabpanel{flex:1 1 .01%;overflow-y:auto;overscroll-behavior:contain;position:relative;-webkit-overflow-scrolling:touch;padding-top:12px;scroll-padding-top:40px}.sidebar .full-list{margin:0;padding:0 0 20px;position:relative}.sidebar .full-list :is(li,a){display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar .full-list li{padding:0;line-height:27px}.sidebar .full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:1.5em 0 0;line-height:1.8em;color:var(--sidebarSubheadings);padding-left:15px}.sidebar .full-list li.nesting-context{font-weight:700;font-size:.9em;line-height:1.8em;color:var(--sidebarSubheadings);margin-top:10px;padding-left:15px}.sidebar .full-list a{margin-right:30px;padding:3px 0 3px 12px;border-left:var(--navTabBorderWidth) solid transparent;color:var(--sidebarItem)}.sidebar .full-list a[aria-selected]{color:var(--sidebarActiveItem)}.sidebar .full-list button{appearance:none;background-color:transparent;border:0;padding:0;cursor:pointer;color:inherit;width:20px;text-align:center;font-size:calc(1.2 * var(--sidebarFontSize));line-height:var(--sidebarLineHeight);position:absolute;display:block;right:10px;transform:translateY(-100%)}.sidebar .full-list a[aria-selected]+button{color:var(--sidebarActiveItem)}.sidebar .full-list button:after{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:var(--icon-arrow-down-s)}.sidebar .full-list button[aria-expanded=true]:after{content:var(--icon-arrow-up-s)}.sidebar .full-list ul{display:none;margin:10px 0 10px 10px;padding:0}.sidebar .full-list button[aria-expanded=true]+ul{display:block}.sidebar .full-list>li>a{height:27px;line-height:var(--sidebarLineHeight)}.sidebar .full-list>li>a:hover{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li>a[aria-selected]{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li:last-child{margin-bottom:30px}.sidebar .full-list>li.group:first-child{margin-top:0}.sidebar .full-list>li>ul>li:not(:has(li a[aria-selected=true]))>a[aria-selected=true]:before,.sidebar .full-list>li>ul>li>a:hover:before{content:"\2022";position:absolute;margin-left:-15px;color:var(--sidebarActiveItem)}.sidebar .full-list ul li{line-height:var(--sidebarFontSize);padding:0 8px}.sidebar .full-list ul a{padding-left:15px;height:24px}.sidebar .full-list ul button{font-size:var(--sidebarFontSize)}.sidebar .full-list ul button:after{content:var(--icon-add)}.sidebar .full-list ul button[aria-expanded=true]:after{content:var(--icon-subtract)}.sidebar .full-list ul ul{margin:9px 0 9px 10px}.sidebar .full-list ul ul li{height:20px;color:var(--sidebarAccentMain)}.sidebar .full-list ul ul a{border-left:1px solid var(--sidebarInactiveItemMarker);padding:0 10px;height:20px}.sidebar .full-list ul ul a:hover{border-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list ul ul a[aria-selected]{color:var(--sidebarActiveItem);border-color:var(--sidebarLanguageAccentBar)}.sidebar-button{cursor:pointer;background-color:transparent;border:none;font-size:var(--sidebarFontSize);color:var(--sidebarAccentMain)}.sidebar-button:hover{color:var(--sidebarHover)}body:not(.sidebar-open) .sidebar-button{color:var(--contrast)}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar .full-list{overflow:visible}}.top-search{background-color:var(--background);top:0;z-index:99;position:relative;width:100%;padding:10px 0}.search-settings{display:flex;column-gap:12px;align-items:center;width:100%;position:relative}.search-bar{border:1px solid var(--searchBarBorder);border-radius:var(--borderRadius-base);height:40px;position:relative;width:100%}.top-search .search-bar .search-input{background-color:var(--searchSearch);border:1px solid transparent;border-radius:var(--borderRadius-base);color:var(--searchAccentMain);position:relative;height:38px;padding:8px 40px 8px 12px;width:100%;transition:var(--transition-all)}.top-search .search-bar .search-input::placeholder{color:var(--searchAccentMain);opacity:.5}.top-search .search-bar .search-input:focus{border:1px solid var(--searchBarFocusColor);border-radius:calc(var(--borderRadius-base) - 1px);position:relative}.top-search .search-bar .search-label{position:relative}.top-search .search-bar .search-input-wrapper{position:relative;display:flex;align-items:center}.top-search .search-settings .engine-selector{position:relative}.top-search .search-settings .engine-selector[data-multiple=false]{display:none}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button{cursor:default}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button .ri-arrow-down-s-line{display:none}.top-search .search-settings .engine-button{display:flex;align-items:center;gap:6px;background-color:var(--autocompleteHover);border:none;color:var(--searchAccentMain);cursor:pointer;font-size:var(--text-sm);padding:8px 12px;border-radius:var(--borderRadius-base);transition:var(--transition-all);height:40px}.top-search .search-settings .engine-button:hover,.top-search .search-settings .engine-button[aria-expanded=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-name{font-size:var(--text-sm);white-space:nowrap}.top-search .search-settings .engine-dropdown{position:absolute;top:calc(100% + 10px);right:0;min-width:200px;max-width:50vh;width:max-content;background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;z-index:200}.top-search .search-settings .engine-dropdown[hidden]{display:none}.top-search .search-settings .engine-option{display:block;width:100%;text-align:left;background-color:transparent;border:0;color:var(--textHeaders);cursor:pointer;padding:10px 12px;transition:var(--transition-colors)}.top-search .search-settings .engine-option:hover{background-color:var(--autocompleteHover)}.top-search .search-settings .engine-option:focus{border-left:2px solid var(--main);padding-left:10px;background-color:var(--autocompleteHover);outline:none}.top-search .search-settings .engine-option[aria-checked=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-option .name{display:block;font-size:var(--text-sm);font-weight:500;margin-bottom:2px}.top-search .search-settings .engine-option .help{display:block;font-size:var(--text-xs);opacity:.7}.top-search .search-bar .search-close-button{font-size:var(--text-md);color:var(--searchAccentMain);background-color:transparent;border:none;cursor:pointer;margin:0;opacity:.5;padding:5px;transform:scaleY(0);transition:var(--transition-all);display:flex;align-items:center;position:absolute;right:0;top:50%;transform:translateY(-50%) scaleY(0)}.top-search .search-bar .search-close-button:hover{opacity:.7}.top-search .search-settings button.icon-settings{display:flex;align-items:center;justify-content:flex-end}.top-search .search-settings .icon-settings{font-size:var(--text-xl);float:right;color:var(--iconAction);text-decoration:none;border:none;transition:color .3s ease-in-out;background-color:transparent;cursor:pointer;padding:0}.top-search .search-settings .icon-settings:hover{color:var(--iconActionHover)}.top-search .search-settings .icon-settings:visited{color:var(--iconAction)}@media screen and (max-width:768px){.top-search{padding-left:calc(var(--content-gutter) + 36px);padding-right:var(--content-gutter);margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));width:calc(2 * var(--content-gutter) + 100%)}.search-settings{width:100%;box-sizing:border-box}.top-search .search-settings .engine-name{display:none}}body.search-focused .search-bar .search-close-button{transform:translateY(-50%) scaleY(1);transition:var(--transition-all)}@media screen and (hover:hover){body.search-focused .top-search{position:sticky!important}body.search-focused .sidebar-button{position:fixed!important}}@media screen and (hover:none){body.scroll-sticky .top-search{position:sticky!important}body.scroll-sticky .sidebar-button{position:fixed!important}}*:focus,button:focus,[type=button]:focus,[type=reset]:focus,[type=submit]:focus{outline:2px solid var(--main);outline-offset:-2px}*:focus:not(:focus-visible),button:focus:not(:focus-visible),[type=button]:focus:not(:focus-visible),[type=reset]:focus:not(:focus-visible),[type=submit]:focus:not(:focus-visible){outline:0}input[type=text],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=email],input[type=month],input[type=password],input[type=search],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{outline:0}.content-inner{font-size:1em;line-height:1.6875em;position:relative;background-color:var(--background);color:var(--textBody)}.content-inner .heading-with-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px}.content-inner .heading-with-actions>*:not(h1){flex-shrink:0}.content-inner .heading-with-actions h1{flex-grow:1;justify-self:flex-start;max-width:100%;margin:0;overflow-wrap:break-word}.content-inner .heading-with-actions .icon-action{width:20px;height:20px;display:flex;justify-content:center;align-items:center;font-weight:400;margin-left:3px}.content-inner .heading-with-actions.top-heading .icon-action{font-size:1.2rem}@container content (width > 600px){.content-inner .heading-with-actions.top-heading{flex-wrap:nowrap;align-items:flex-start;& h1{padding-right:32px}.icon-action{padding-top:1.7rem}}}.content-inner .top-heading{padding-top:1rem}.content-inner :is(h1,h2,h3,h4,h5,h6){font-family:var(--sansFontFamily);font-weight:700;line-height:1.5em;word-wrap:break-word;color:var(--textHeaders)}.content-inner div.group-description{margin:0 0 3em}.content-inner h1 small{font-weight:400}.content-inner{& h1{font-size:1.5rem;margin-top:1.75em}& h2{font-size:1.35rem;margin-top:1.5em;margin-bottom:.5em}& h3{font-size:1.25rem;margin-top:1.5em;margin-bottom:.5em}:is(h4,h5,h6){font-size:1.15rem;margin-top:1.25em;margin-bottom:.5em}:is(h5,h6){font-weight:400}& h6{font-size:1rem}}@container content (width > 600px){.content-inner{& h1{font-size:2rem}& h2{font-size:1.75rem}& h3{font-size:1.45rem}}}.content-inner li+li{margin-top:.25em}.content-inner :is(a,.a-main){color:var(--link-color);text-decoration:underline;text-decoration-skip-ink:auto}.content-inner :is(a:visited,.a-main:visited){color:var(--link-visited-color)}.content-inner .icon-action{color:var(--iconAction);text-decoration:none;border:none;transition:var(--transition-colors);background-color:transparent;cursor:pointer}.content-inner .icon-action:hover{color:var(--iconActionHover)}.content-inner .icon-action:visited{color:var(--iconAction)}.content-inner .livebook-badge-container{display:flex}.content-inner a.livebook-badge{display:inline-flex}.content-inner .note{color:var(--iconAction);font-size:var(--text-xs);font-weight:400}.content-inner{:is(blockquote,section.admonition){position:relative;margin:1.5625em 0;padding:0 1.2rem;overflow:auto;border-radius:var(--borderRadius-base);:is(h1,h2,h3,h4,h5,h6){margin:1.25rem 0}:is(h1,h2,h3){font-size:1.375rem}:is(h4,h5,h6){font-size:1rem}:is(code,pre code){background-color:var(--admCodeBackground);border:1px solid var(--admCodeBorder);color:var(--admCodeColor)}:is(a,a:visited){color:inherit}:is(h1,h2,h3,h4,h5,h6):first-child{padding-bottom:1rem;border-bottom:2px solid hsl(from currentColor h s l / 15%)}& p:last-child{padding-bottom:1em;margin-bottom:0}}& blockquote{border-left:3px solid var(--blockquoteBorder);background-color:var(--blockquoteBackground)}}@media print{.content-inner :is(blockquote,section.admonition){border:2px solid hsl(from var(--textBody) h s l / 15%);:is(code,pre code){border-color:var(--gray400)}& pre code.makeup{white-space:break-spaces;break-inside:avoid}}}.content-inner table{margin:2em 0;border-collapse:collapse;display:block;overflow:auto}.content-inner th{text-align:left;font-family:var(--sansFontFamily);font-weight:700;padding-bottom:.5em;white-space:nowrap}.content-inner thead tr{border-bottom:1px solid var(--tableHeadBorder)}.content-inner tbody tr{border-bottom:1px solid var(--tableBodyBorder)}.content-inner tbody tr:last-child{border-bottom:none}.content-inner tr{vertical-align:bottom;height:2.5em}.content-inner :is(td,th){padding:.25em .25em .25em 1em;line-height:2em;vertical-align:top}.content-inner .section-heading{--icon-size: 16px;--icon-spacing: 5px;display:grid;grid-template:1fr / 1fr}@media screen and (max-width:768px){.content-inner .section-heading{--icon-spacing: 2px}}.content-inner .section-heading>:is(.hover-link,.text){grid-row:1;grid-column:1}.content-inner .section-heading .hover-link{text-decoration:none}.content-inner .section-heading i{font-size:var(--icon-size);color:var(--mainLight);top:-2px;margin-left:calc(-1 * (var(--icon-size) + var(--icon-spacing)));padding-right:var(--icon-spacing);position:relative;opacity:0}.content-inner :is(blockquote,section.admonition) .section-heading i{display:none}.content-inner .section-heading:is(:hover,:focus,:target) i{opacity:1}.content-inner .app-vsn{display:none!important;font-size:.6em;line-height:1.5em}@media screen and (max-width:768px){.content-inner .app-vsn{display:block!important}}.content-inner img{max-width:100%}@media screen and (max-width:768px){.content-inner :is(ol,ul){padding-left:calc(1.5 * var(--content-gutter))}}.content-inner section.admonition{border-radius:var(--borderRadius-base)}.content-inner section.admonition>.admonition-title{color:var(--contrast);padding-left:2.2rem;font-weight:700;font-style:normal}.content-inner section.admonition>.admonition-title:before{color:var(--contrast);position:absolute;left:1rem;font-size:1.8rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.content-inner section.admonition{&.warning{background-color:var(--warningBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--warningHeading)}}}&.error{background-color:var(--errorBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--errorHeading)}}}&.info{background-color:var(--infoBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--infoHeading)}}}&.neutral{background-color:var(--neutralBackground);.admonition-title{&,&:before{content:var(--icon-double-quotes-l);color:var(--neutralHeading)}}}&.tip{background-color:var(--tipBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--tipHeading)}}}}@media print{.content-inner section.admonition{&.warning{border-color:hsl(from var(--warningHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--warningHue),90%,40%)}}}&.error{border-color:hsl(from var(--errorHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--errorHue),80%,40%)}}}&.info{border-color:hsl(from var(--infoHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--infoHue),80%,40%)}}}&.neutral{border-color:hsl(from var(--neutralHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--neutralHue),30%,40%)}}}&.tip{border-color:hsl(from var(--tipHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--tipHue),50%,40%)}}}}}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@media screen and (max-width:768px){.content-inner section.admonition{margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0}}.content-inner .summary h3 a{text-decoration:none;border:none;color:var(--textHeaders)!important}.content-inner .summary span.deprecated{color:var(--darkDeprecated);font-weight:400}.content-inner .summary .summary-row .summary-signature{font-family:var(--monoFontFamily);font-size:13px;font-weight:700}.content-inner .summary .summary-row .summary-signature a{text-decoration:none;border:none}.content-inner .summary .summary-row .summary-synopsis{padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.content-inner .footer{margin:4em auto 1em;text-align:center;font-size:var(--text-sm)}.content-inner .footer .line{display:inline-block}.content-inner .footer .line a,.content-inner .footer .line button{padding:0 4px}.content-inner .footer button{background-color:transparent;border:0;cursor:pointer}.content-inner .bottom-actions{display:flex;justify-content:space-between;margin-top:7em;gap:12px}.bottom-actions-item{flex:1 1 0%}.content-inner .bottom-actions .bottom-actions-button{display:flex;text-decoration:none;flex-direction:column;border-radius:var(--borderRadius-sm);border:1px solid var(--bottomActionsBtnBorder);padding:12px 16px;min-width:150px;transition:var(--transition-all)}.content-inner .bottom-actions .bottom-actions-button:hover{border-color:var(--mainLight)}.content-inner .bottom-actions .bottom-actions-button .subheader{font-size:.8em;color:var(--textHeaders);white-space:nowrap}.content-inner .bottom-actions .bottom-actions-button .title{color:var(--bottomActionsBtnTitle)}.content-inner .bottom-actions .bottom-actions-button[rel=prev]{text-align:start}.content-inner .bottom-actions .bottom-actions-button[rel=next]{text-align:end}@media screen and (max-width:768px){.content-inner .bottom-actions{flex-direction:column-reverse}}.page-cheatmd .content-inner{--horizontal-space: 1.5em;--vertical-space: 1em}@media(max-width:600px){.page-cheatmd .content-inner{--horizontal-space: 1em;--vertical-space: .75em}}.page-cheatmd .content-inner{max-width:1200px}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& h1{margin-bottom:var(--vertical-space)}& h2{font-size:1.6rem;margin:var(--vertical-space) 0;column-span:all;color:var(--gray700);font-weight:500}& h3{font-size:1.375rem;margin:0 0 1em;font-weight:400}& section.h3{min-width:300px;margin:0;padding:0 0 calc(var(--vertical-space) * 2) 0;break-inside:avoid}& h3 .text{overflow:hidden}& h3 .text:after{content:"";margin-left:calc(var(--horizontal-space) / 2);vertical-align:baseline;display:inline-block;width:100%;height:1px;margin-right:-100%;margin-bottom:5px;background-color:var(--codeBorder)}& h4{font-size:1rem;display:block;margin:0;padding:.25em var(--horizontal-space);font-weight:400;background:var(--gray100);color:#567;border:solid 1px 1px 0 1px var(--gray100)}.h2 p{margin:0;display:block;background:var(--gray50);padding:var(--vertical-space) var(--horizontal-space)}.h2 p>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}& pre code{padding:var(--vertical-space) var(--horizontal-space)}.h2 pre{margin:0}.h2 pre+pre{margin-top:-1px}& pre.wrap{white-space:break-spaces}@media screen and (max-width:768px){& pre code{border-left-width:1px!important;border-right-width:1px!important}}.h2 table{display:table;box-sizing:border-box;width:100%;border-collapse:collapse;margin:0}.h2 th{padding:var(--vertical-space) var(--horizontal-space);line-height:inherit;margin-bottom:-1px;vertical-align:middle;border-bottom:1px solid var(--codeBorder)}.h2 td{padding:var(--vertical-space) var(--horizontal-space);border:0;border-bottom:1px solid var(--codeBorder)}.h2 tr:first-child{border-top:1px solid var(--codeBorder)}.h2 td code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}.h2 thead{background-color:var(--gray50)}.h2 tbody{background-color:var(--codeBackground)}.h2 :is(ul,ol){margin:0;padding:0}.h2 li{list-style-position:inside;padding:.5em var(--horizontal-space);line-height:2em;vertical-align:middle;background-color:var(--codeBackground);border-bottom:1px solid var(--codeBorder);margin-top:0}.h2 :is(ul,ol)+pre code{border-top:0}.h2 li>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}}}@scope(.dark .page-cheatmd)to (.admonition){:scope .content-inner{& h2{color:var(--gray200)}& h4{background:#192f50;color:var(--textBody);border:1px solid #192f50;border-bottom:0}.h2 p{background:var(--gray700)}.h2 thead{background-color:var(--gray700)}}}.page-cheatmd .content-inner{& section.width-50{display:block;width:50%;margin:0;>section>table{width:100%}}& section:is(.col-2,.col-2-left,.col-3){column-gap:40px}& section.col-2{column-count:2;height:auto}& section.col-2-left{display:grid;grid-template-columns:calc(100% / 3) auto;>h2{grid-column-end:span 2}}& section.col-3{column-count:3;height:auto}& section.list-4>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 25%}}& section.list-6>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 calc(100% / 6)}}@media screen and (max-width:1400px){& section.col-3{column-count:2}& section.col-2-left{display:flex;flex-direction:column}}@media screen and (max-width:1200px){& section:is(.col-2,.col-3){display:flex;flex-direction:column}& section.list-6>ul>li{flex:0 0 25%}}@media screen and (max-width:1000px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 calc(100% / 3)}}@media screen and (max-width:600px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 50%}& section.width-50{width:100%}}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.page-search #search{min-height:200px;position:relative;.loading{height:64px;width:64px;position:absolute;top:50%;left:calc(50% - 32px)}.loading div{box-sizing:border-box;display:block;position:absolute;width:51px;height:51px;margin:6px;border:6px solid var(--coldGray);border-radius:50%;animation:loading 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:var(--coldGray) transparent transparent transparent}.loading div:nth-child(1){animation-delay:-.45s}.loading div:nth-child(2){animation-delay:-.3s}.loading div:nth-child(3){animation-delay:-.15s}.result{margin:2em 0}.result p{margin:0}.result-id{font-size:1.4em;margin:0}.result-id a{text-decoration:none;color:var(--textHeaders);transition:var(--transition-colors)}.result-id a:is(:visited,:active){color:var(--textHeaders)}.result-id a:is(:hover,:focus){color:var(--main)}:is(.result-id,.result-elem) em{font-style:normal;color:var(--main)}.result-id small{font-weight:400}}@keyframes keyboard-shortcuts-show{0%{opacity:0}to{opacity:1}}.modal{animation-duration:.15s;animation-name:keyboard-shortcuts-show;animation-iteration-count:1;animation-timing-function:ease-in-out;display:none;background-color:#000000bf;position:fixed;inset:0;z-index:300}.modal.shown{display:block}.modal .modal-contents{margin:75px auto 0;max-width:500px;background-color:var(--modalBackground);border-radius:var(--borderRadius-sm);box-shadow:2px 2px 8px #0003;padding:25px 35px 35px}@media screen and (max-width:768px){.modal .modal-contents{padding:20px}}.modal .modal-header{display:flex;align-items:start}.modal .modal-title{display:inline-block;flex-grow:1;font-size:1.2rem;font-weight:700;margin-bottom:20px}.modal .modal-title button{border:none;background-color:transparent;color:var(--textHeaders);font-weight:700;margin-right:30px;padding-left:0;text-align:left;transition:var(--transition-colors)}.modal .modal-title button:hover{color:var(--main);cursor:pointer}.modal .modal-title button.active{color:var(--main)}.modal .modal-close{cursor:pointer;display:block;font-size:1.5rem;margin:-8px -8px 0 0;padding:8px;opacity:.7;background-color:transparent;color:var(--textHeaders);border:none;transition:var(--transition-opacity)}.modal .modal-close:hover{opacity:1}#keyboard-shortcuts-content dl.shortcut-row{display:flex;align-items:center;justify-content:space-between;margin:0;padding:6px 0 8px;border-bottom:1px solid var(--settingsSectionBorder)}#keyboard-shortcuts-content dl.shortcut-row:last-of-type{border-bottom-style:none}#keyboard-shortcuts-content dl.shortcut-row:first-child{padding-top:0}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){display:inline-block}#keyboard-shortcuts-content kbd>kbd{background-color:var(--settingsInputBorder);color:var(--contrast);border-radius:var(--borderRadius-sm);font-family:inherit;font-weight:700;display:inline-block;line-height:1;padding:4px 7px 6px;min-width:26px;text-align:center;font-size:var(--text-sm)}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){margin:0}#quick-switch-modal-body{width:100%;position:relative}#quick-switch-modal-body .ri-search-2-line{position:absolute;left:0;top:0;padding:4px 10px;color:var(--quickSwitchContour);font-weight:700}#quick-switch-modal-body #quick-switch-input{width:100%;padding:8px 6px 8px 38px;border:none;color:var(--quickSwitchInput);background-color:transparent;border-bottom:1px solid var(--quickSwitchContour);box-sizing:border-box;transition:all .12s ease-out}#quick-switch-modal-body #quick-switch-results{margin:0}#quick-switch-modal-body .quick-switch-result{padding:2px 5px;border-bottom:1px dotted var(--quickSwitchContour);transition:all .12s ease-out}#quick-switch-modal-body .quick-switch-result:last-child{border-bottom:none}#quick-switch-modal-body .quick-switch-result:hover{cursor:pointer}#quick-switch-modal-body .quick-switch-result:is(:hover,.selected){border-left:4px solid var(--main);background-color:var(--codeBackground)}.autocomplete{display:none;position:absolute;width:calc(100% - 32px);top:55px}.autocomplete .triangle{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-bottom:12px solid var(--autocompleteBackground);position:absolute;top:8px;left:26px;transform:translate(-50%);z-index:100;background-color:transparent}.autocomplete-results{margin:0;padding:15px 20px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;color:var(--autocompleteResults);font-weight:300;font-size:.9rem;font-style:italic}.autocomplete-results .query{margin-right:auto}.autocomplete-results .bold{color:var(--autocompleteResultsBold);font-weight:400}.autocomplete.shown{display:block}.autocomplete-container{position:absolute;top:5px;width:100%;z-index:200}.autocomplete-suggestions{background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;overflow-y:auto;max-height:450px;white-space:normal;overflow-x:hidden;overscroll-behavior-y:contain;scrollbar-width:thin}.autocomplete-suggestion{color:var(--textHeaders)!important;display:block;padding:12px 20px;text-decoration:none!important;transition:var(--transition-colors);border-top:1px solid var(--suggestionBorder);font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.autocomplete-suggestion.selected{background-color:var(--autocompleteSelected)}.autocomplete-suggestion:hover{background-color:var(--autocompleteHover)}.autocomplete-suggestion em{font-style:normal;font-weight:700}.autocomplete-suggestion .separator{opacity:.4;margin:0 2px}.autocomplete-suggestion .description{opacity:.6}.autocomplete-suggestion .label{background-color:var(--autocompleteLabelBack);opacity:.6;color:var(--autocompleteLabelFont);padding:4px 8px;border-radius:4px;margin-left:5px;text-transform:uppercase;font-family:var(--sansFontFamily);font-size:.7rem}@media screen and (hover:none){.autocomplete-results .press-return{display:none!important}}.tooltip{box-shadow:0 0 10px var(--black-opacity-10);max-height:300px;max-width:500px;padding:0;position:absolute;pointer-events:none;margin:0;z-index:99;top:0;left:0;visibility:hidden;transform:translateY(20px);opacity:0;transition:.2s visibility ease-out,.2s transform ease-out,.2s opacity ease-out}.tooltip.tooltip-shown{visibility:visible;transform:translateY(0);opacity:1}.tooltip .tooltip-body{border:1px solid var(--codeBorder);border-radius:var(--borderRadius-sm);overflow:auto}.tooltip .tooltip-body .signature{min-width:320px;width:100%;line-height:1em!important;margin:.75em 0!important}.tooltip .tooltip-body .detail-header{border-left:0;margin-bottom:0;margin-top:0}.tooltip .tooltip-body .docstring{background-color:var(--background);padding:1.2em;margin:0;width:498px}.tooltip .tooltip-body .docstring-plain{max-width:498px;width:auto}.tooltip .tooltip-body .version-info{float:right;font-family:var(--monoFontFamily);font-weight:400;opacity:.3;padding-left:.3em}pre{position:relative}pre:hover .copy-button,pre .copy-button:focus{opacity:1}.copy-button{display:flex;opacity:0;position:absolute;top:7px;right:8px;padding:8px;background-color:transparent;backdrop-filter:blur(8px);border-radius:var(--borderRadius-sm);border:1px solid var(--codeBorder);cursor:pointer;transition:var(--transition-all);font-size:var(--text-sm);line-height:24px;color:currentColor;& svg[aria-live=polite]{display:none}}.copy-button svg{opacity:.5;transition:var(--transition-all)}pre .copy-button:hover svg,pre .copy-button:focus-visible svg{opacity:1}.copy-button svg{width:20px}.copy-button.clicked{opacity:1;color:var(--success);& svg[aria-live=polite]{display:block}}.copy-button.clicked svg{display:none;color:currentColor}#settings-modal-content{margin-top:10px}#settings-modal-content .hidden{display:none}#settings-modal-content .input{box-sizing:border-box;width:80%;padding:8px;font-size:var(--text-sm);background-color:var(--settingsInputBackground);color:var(--settingsInput);border:1px solid var(--settingsInputBorder);border-radius:var(--borderRadius-base);transition:var(--transition-all)}#settings-modal-content .input:focus{border-color:var(--main)}#settings-modal-content .input::placeholder{color:var(--gray400)}#settings-modal-content .switch-button-container{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--settingsSectionBorder);padding:10px 0}#settings-modal-content .switch-button-container:first-of-type{border-top-style:none;padding-top:0}#settings-modal-content .switch-button-container>div>span{font-size:var(--text-md)}#settings-modal-content .switch-button-container>div>p{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--text-sm);line-height:1.4;margin:0;padding-bottom:6px;padding-right:10px}#settings-modal-content .switch-button{position:relative;display:inline-block;flex-shrink:0;width:40px;height:20px;user-select:none;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox{appearance:none;position:absolute;display:block;width:20px;height:20px;border-radius:1000px;background-color:#91a4b7;border:3px solid #e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__bg{display:block;width:100%;height:100%;border-radius:1000px;background-color:#e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox:checked{background-color:#fff;border-color:var(--main);transform:translate(100%)}#settings-modal-content .switch-button__checkbox:checked+.switch-button__bg{background-color:var(--main)}#settings-modal-content .switch-button__checkbox:focus{outline:0}#settings-modal-content .switch-button__checkbox:focus+.switch-button__bg{outline:2px solid var(--main);outline-offset:2px}#settings-modal-content .switch-button__checkbox:focus:not(:focus-visible)+.switch-button__bg{outline:0}#settings-modal-content .settings-select{cursor:pointer;position:relative;border:none;background-color:transparent;color:var(--textBody)}#settings-modal-content .settings-select option{color:initial}#toast{visibility:hidden;opacity:0;position:fixed;z-index:1000;right:10px;top:0;font-size:.9rem;min-width:3rem;padding:.7rem 1.2rem .7rem 2.8rem;text-align:left;font-weight:400;border-radius:var(--borderRadius-base);border:1px solid hsl(from var(--tipHeading) h s l / 15%);background-color:var(--tipBackground);color:var(--tipHeading);transition:opacity .4s ease-in-out,transform .3s ease-out;cursor:default}#toast:before{content:var(--icon-information);position:absolute;left:1rem;font-size:1.2rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#toast.show{visibility:visible;opacity:1;transform:translateY(.75rem)}@media(prefers-reduced-motion:reduce){#toast{transition:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}@media print{.body-wrapper{display:block}.sidebar,.sidebar-button,.top-search{display:none}.content{padding-left:0;overflow:visible;left:0;width:100%}.summary-row{break-inside:avoid}#toast{display:none}.content-inner{padding:0}.content-inner .section-heading a.hover-link,.content-inner button.icon-action,.content-inner a.icon-action,.content-inner .bottom-actions{display:none}.footer p:first-of-type{display:none}.content-inner pre code.makeup{white-space:break-spaces;break-inside:avoid}}@media print{.page-cheatmd .content-inner{max-width:100%;width:100%;padding:0;font-size:.7em;& section:is(.col-2,.col-2-left,.col-3){column-gap:30px}& section.col-2{column-count:2}& section.col-2-left{display:grid}& section.col-3{column-count:3}}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& *{background-color:transparent!important;border-color:var(--gray400)!important}& h1{margin-top:0;margin-bottom:.5em}& h2.section-heading{font-weight:700;margin-top:1em;column-span:all}& section.h2{break-inside:avoid}& h3{font-weight:700;color:var(--mainDark)}& h3:after{height:2px;background-color:var(--gray400)}& section.h3{min-width:300px;break-inside:avoid}& h4{padding:.5em 0;border:none;font-weight:700;color:#000}.h2 p{padding-left:0;padding-right:0;border:none!important}& code{line-height:1.5em}.h2 table{font-variant-numeric:tabular-nums;break-inside:avoid}.h2 :is(th,td){vertical-align:top;padding-left:0;padding-right:0}.h2 thead{border-style:solid none;border-width:1px}.h2 tr{border-bottom:none}.h2 th{font-weight:700}.h2 li{padding-left:0;padding-right:0;vertical-align:middle;border-bottom:none}& pre:hover button.copy-button{display:none}& div.tooltip{display:none}& footer p:not(.built-using){display:none}}}}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}.tabset{--borderWidth: 1px;--tabsetPadding: var(--baseLineHeight);margin:var(--baseLineHeight) 0;border:var(--borderWidth) solid var(--tabBorder);padding:0 var(--tabsetPadding);border-radius:var(--borderRadius-lg)}.tabset-tablist{display:flex;overflow:auto;scrollbar-width:thin;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--tabBorderTop)}.tabset-tab{padding:1.1rem var(--tabsetPadding);font-family:var(--sansFontFamily);color:var(--textColor);margin-right:calc(-1 * var(--borderWidth));background-color:transparent;border:0;box-shadow:none;cursor:pointer;border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:transparent;transition:var(--transition-all)}:hover.tabset-tab{border-bottom-color:var(--tabBorderTop);color:var(--textHeaders)}.tabset-tab[aria-selected=true]{border-bottom-color:var(--mainLight);color:var(--textHeaders)}.tabset-tab[aria-selected=true]:focus-visible{background-color:var(--mainLight);border-color:var(--mainLight);color:var(--white)}@media screen and (max-width:768px){.tabset{--tabsetPadding: calc(var(--baseLineHeight) / 2)}.tabset-panel{padding-top:calc(var(--tabsetPadding) / 2);padding-bottom:calc(var(--tabsetPadding) / 2)}.tabset-panel pre,.tabset-panel blockquote,.tabset-panel section.admonition{margin-left:calc(-1 * var(--tabsetPadding))!important;margin-right:calc(-1 * var(--tabsetPadding))!important}.tabset-panel>pre code{border-left-width:0;border-right-width:0}}@media screen and (max-width:768px){.tabset-panel>:is(:first-child){&:is(table){margin:.5em 0}}}@media screen and (min-width:769px){.tabset-panel>:is(:first-child){&:is(blockquote,.admonition){margin-top:1.5em}&:is(p:has(img)){margin-top:1.25em}&:is(table){margin-top:.75em}}.tabset-panel>:is(:last-child){&:is(blockquote,.admonition){margin-bottom:1.5em}&:is(p:not(:has(img)),ul,ol){margin-bottom:1.25em}&:is(table){margin-bottom:.75em}}}body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"],body.dark .content-inner img[src*="#gh-light-mode-only"]{display:none} +/*! Bundled license information: + +modern-normalize/modern-normalize.css: + (*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize *) +*/ diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-erlang-KCHZLXSC.css b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-erlang-KCHZLXSC.css new file mode 100644 index 0000000..4dd7edb --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-erlang-KCHZLXSC.css @@ -0,0 +1,6 @@ +:root{--main: hsl(0, 100%, 44%);--mainDark: hsl(0, 100%, 34%);--mainDarkest: hsl(0, 100%, 24%);--mainLight: hsl(0, 100%, 64%);--mainLightest: hsl(0, 100%, 74%);--searchBarFocusColor: hsl(0, 100%, 50%);--searchBarBorderColor: rgb(255, 71, 71, .1);--link-color: hsl(212, 96%, 45%);--link-visited-color: hsl(212, 96%, 40%)}body.dark{--link-color: hsl(212, 56%, 72%);--link-visited-color: hsl(212, 56%, 67%)}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-ext-400-normal-N27NCBWW.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-400-normal-W7754I4D.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-ext-700-normal-Q2L5DVMW.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-700-normal-2XVSBPG4.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}body.dark{--background: var(--gray900);--contrast: var(--white);--textBody: var(--gray200);--textHeaders: var(--gray100);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--gray700);--iconAction: var(--coldGray-lightened-10);--iconActionHover: var(--white);--blockquoteBackground: var(--coldGray-opacity-10);--blockquoteBorder: var(--coldGrayDim);--tableHeadBorder: var(--gray600);--tableBodyBorder: var(--gray700);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray900);--admMixAmt: 40%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 40%, 14%) 100%);--warningHeading: hsl(var(--warningHue), 100%, 50%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 40%, 14%) 100%);--errorHeading: hsl(var(--errorHue), 100%, 52%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 60%, 15%) 100%);--infoHeading: hsl(var(--infoHue), 100%, 52%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 14%) 100%);--neutralHeading: hsl(var(--neutralHue), 50%, 80%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 14%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 48%);--fnSpecAttr: var(--gray400);--fnDeprecated: var(--yellowDark);--blink: var(--gray600);--codeBackground: var(--gray800);--codeBorder: var(--gray650);--codeScrollThumb: var(--gray500);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--black-opacity-30);--admCodeBorder: var(--white-opacity-10);--admCodeColor: var(--gray100);--tabBorder: var(--gray700);--tabBorderTop: var(--gray700);--tabShadow: var(--black);--bottomActionsBtnBorder: var(--white-opacity-10);--bottomActionsBtnTitle: var(--mainLightest);--modalBackground: var(--gray800);--settingsInput: var(--white);--settingsInputBackground: var(--gray700);--settingsInputBorder: var(--gray700);--settingsSectionBorder: var(--gray700);--quickSwitchInput: var(--gray300);--quickSwitchContour: var(--gray500);--success: var(--green-lightened-10);--progressBarColor: var(--gray300);--sidebarAccentMain: var(--gray50);--sidebarBackground: var(--gray800);--sidebarHeader: var(--gray700);--sidebarMuted: var(--gray300);--sidebarHover: var(--white);--sidebarStaleVersion: var(--orangeLight);--sidebarSubheadings: var(--gray400);--sidebarItem: var(--gray200);--sidebarInactiveItemBorder: var(--gray400);--sidebarInactiveItemMarker: var(--gray600);--sidebarLanguageAccentBar: var(--mainLight);--sidebarActiveItem: var(--mainLightest);--searchBarBorder: var(--gray500);--searchAccentMain: var(--gray300);--searchSearch: var(--gray900);--autocompleteBorder: rgba(28,42,60,.75);--autocompletePreview: var(--gray750);--autocompleteSelected: var(--gray750);--autocompleteHover: var(--gray700);--autocompleteBackground: var(--gray800);--suggestionBorder: var(--gray600);--autocompleteResults: var(--gray200);--autocompleteResultsBold: var(--gray100);--autocompleteLabelBack: var(--gray600);--autocompleteLabelFont: rgba(255, 255, 255, .8)}:root:has(body.dark){color-scheme:dark}*,:before,:after{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}@font-face{font-family:remixicon;src:url("./remixicon-ZWU5GQ4X.woff2") format("woff2");font-display:swap}[class^=ri-],[class*=" ri-"],.remix-icon{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--icon-arrow-up-s: "\ea78";--icon-arrow-down-s: "\ea4e";--icon-arrow-right-s: "\ea6e";--icon-add: "\ea13";--icon-subtract: "\f1af";--icon-error-warning: "\eca1";--icon-external-link-line: "\ecaf";--icon-information: "\ee59";--icon-alert: "\ea21";--icon-double-quotes-l: "\ec51";--icon-link-m: "\eeaf";--icon-close-line: "\eb99";--icon-code-s-slash-line: "\ebad";--icon-menu-line: "\ef3e";--icon-search-2-line: "\f0cd";--icon-settings-3-line: "\f0e6";--icon-printer-line: "\f029";--icon-markdown-line: "\ef1e"}.ri-lg{font-size:1.3333em;line-height:.75em;vertical-align:-.0667em}.ri-settings-3-line:before{content:var(--icon-settings-3-line)}.ri-add-line:before{content:var(--icon-add)}.ri-subtract-line:before{content:var(--icon-subtract)}.ri-arrow-up-s-line:before{content:var(--icon-arrow-up-s)}.ri-arrow-down-s-line:before{content:var(--icon-arrow-down-s)}.ri-arrow-right-s-line:before{content:var(--icon-arrow-right-s)}.ri-external-link-line:before{content:var(--icon-external-link-line)}.ri-search-2-line:before{content:var(--icon-search-2-line)}.ri-menu-line:before{content:var(--icon-menu-line)}.ri-close-line:before{content:var(--icon-close-line)}.ri-link-m:before{content:var(--icon-link-m)}.ri-code-s-slash-line:before{content:var(--icon-code-s-slash-line)}.ri-error-warning-line:before{content:var(--icon-error-warning)}.ri-information-line:before{content:var(--icon-information)}.ri-alert-line:before{content:var(--icon-alert)}.ri-double-quotes-l:before{content:var(--icon-double-quotes-l)}.ri-printer-line:before{content:var(--icon-printer-line)}.ri-markdown-line:before{content:var(--icon-markdown-line)}html,body{box-sizing:border-box;height:100%;width:100%}body{--sidebarWidth: 300px;--sidebarMinWidth: 300px;--sidebarTransitionDuration: .3s;background-color:var(--background);color:var(--textBody);font-size:var(--text-md);line-height:1.6875em;outline:none!important}*,*:before,*:after{box-sizing:inherit}.body-wrapper{display:flex;height:100%}.sidebar{display:none;flex-direction:column;width:var(--sidebarWidth);min-width:var(--sidebarMinWidth);max-width:50vw;height:100%;position:fixed;top:0;left:calc(-1 * var(--sidebarWidth));z-index:100;resize:horizontal}.sidebar-button{padding:20px 12px 18px 19px;position:fixed;z-index:200;top:0;left:0;will-change:transform;transform:translate(0)}.content{left:0;width:100%;height:100%;position:absolute}.content .content-inner{container:content / inline-size;max-width:var(--content-width);min-height:100%;margin:0 auto;padding:0 var(--content-gutter) 10px}.content-inner:focus{outline:none}.sidebar-transition .sidebar,.sidebar-transition .sidebar-button,.sidebar-transition .content{transition:all var(--sidebarTransitionDuration) ease-in-out allow-discrete}.sidebar-open .sidebar,.sidebar-transition .sidebar{display:flex}.sidebar-open .sidebar{left:0}.sidebar-open .sidebar-button{transform:translate(calc(var(--sidebarWidth) - 100%))}.sidebar-open .content{width:calc(100% - var(--sidebarWidth));left:var(--sidebarWidth)}@media screen and (max-width:768px){.sidebar-open .content{left:0;width:100%}.sidebar{max-width:90vw}body:not(.sidebar-open) .sidebar-button{position:absolute}}.swup-progress-bar{height:2px;background-color:var(--progressBarColor)}.sidebar{--sidebarFontSize: 16px;--sidebarLineHeight: 20px;font-family:var(--sansFontFamily);font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);background-color:var(--sidebarBackground);color:var(--sidebarAccentMain);overflow:hidden;.sidebar-tabpanel{scrollbar-width:thin}}.apple-os .sidebar{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar ul{list-style:none}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:var(--sidebarAccentMain);text-decoration:none;transition:var(--transition-colors)}.sidebar a:hover{color:var(--sidebarHover)}.sidebar .external-link{margin-left:2.5px}.sidebar .sidebar-header{background-color:var(--sidebarHeader);width:100%}.sidebar .sidebar-projectInfo{display:flex;justify-content:start;align-items:center;gap:8px;margin:8px 34px 12px 14px}.sidebar .sidebar-projectInfo>div{flex:1}.sidebar .sidebar-projectImage{align-self:flex-end}.sidebar .sidebar-projectImage img{display:block;max-width:48px;max-height:48px}.sidebar .sidebar-projectName{font-weight:700;font-size:var(--text-xl);line-height:24px;color:var(--sidebarAccentMain);margin:0;padding:0;word-wrap:break-word;display:block;width:calc(100% - 12px)}.sidebar .sidebar-projectVersion{display:block;position:relative;margin:0;padding:0;font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);color:var(--sidebarMuted);width:calc(100% - 12px)}.sidebar .sidebar-projectVersion form{display:flex}.sidebar .sidebar-projectVersion select{cursor:pointer;position:relative;margin:0;padding:0 0 0 10px;border:none;-webkit-appearance:none;appearance:none;background-color:transparent;color:var(--sidebarMuted);z-index:2}.sidebar .sidebar-projectVersion option{color:initial}.sidebar .sidebar-projectVersionsCaret{position:absolute;left:0;top:2px;z-index:1;font-size:8px;color:var(--sidebarMuted)}.sidebar .sidebar-projectVersion select::-ms-expand{display:none}.sidebar .sidebar-staleVersion{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--sidebarStaleVersion)}.sidebar .sidebar-staleVersion>a{color:var(--sidebarStaleVersion);font-weight:400}.sidebar .sidebar-staleIcon{font-size:var(--text-lg);line-height:0}.sidebar .sidebar-list-nav{display:flex;margin:0;padding:0;overflow:auto;scrollbar-width:thin}.sidebar .sidebar-list-nav :is(li,li button){text-transform:uppercase;letter-spacing:.02em;font-size:var(--text-sm);color:var(--sidebarSubheadings);white-space:nowrap}.sidebar .sidebar-list-nav li{display:inline-block;padding:0}.sidebar .sidebar-list-nav button{background:none;border:0;border-radius:0;-webkit-appearance:none;text-align:inherit;color:inherit;font-weight:inherit;cursor:pointer;display:inline-block;line-height:27px;padding:4px 14px;transition:var(--transition-all)}.sidebar .sidebar-list-nav button{border-bottom:var(--navTabBorderWidth) solid transparent}.sidebar .sidebar-list-nav button:not([aria-selected]):hover{border-bottom:var(--navTabBorderWidth) solid var(--sidebarInactiveItemBorder);color:var(--sidebarAccentMain);transition:var(--transition-all)}.sidebar .sidebar-list-nav button[aria-selected]{border-bottom:var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar);color:var(--sidebarAccentMain)}.sidebar .sidebar-tabpanel{flex:1 1 .01%;overflow-y:auto;overscroll-behavior:contain;position:relative;-webkit-overflow-scrolling:touch;padding-top:12px;scroll-padding-top:40px}.sidebar .full-list{margin:0;padding:0 0 20px;position:relative}.sidebar .full-list :is(li,a){display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar .full-list li{padding:0;line-height:27px}.sidebar .full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:1.5em 0 0;line-height:1.8em;color:var(--sidebarSubheadings);padding-left:15px}.sidebar .full-list li.nesting-context{font-weight:700;font-size:.9em;line-height:1.8em;color:var(--sidebarSubheadings);margin-top:10px;padding-left:15px}.sidebar .full-list a{margin-right:30px;padding:3px 0 3px 12px;border-left:var(--navTabBorderWidth) solid transparent;color:var(--sidebarItem)}.sidebar .full-list a[aria-selected]{color:var(--sidebarActiveItem)}.sidebar .full-list button{appearance:none;background-color:transparent;border:0;padding:0;cursor:pointer;color:inherit;width:20px;text-align:center;font-size:calc(1.2 * var(--sidebarFontSize));line-height:var(--sidebarLineHeight);position:absolute;display:block;right:10px;transform:translateY(-100%)}.sidebar .full-list a[aria-selected]+button{color:var(--sidebarActiveItem)}.sidebar .full-list button:after{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:var(--icon-arrow-down-s)}.sidebar .full-list button[aria-expanded=true]:after{content:var(--icon-arrow-up-s)}.sidebar .full-list ul{display:none;margin:10px 0 10px 10px;padding:0}.sidebar .full-list button[aria-expanded=true]+ul{display:block}.sidebar .full-list>li>a{height:27px;line-height:var(--sidebarLineHeight)}.sidebar .full-list>li>a:hover{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li>a[aria-selected]{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li:last-child{margin-bottom:30px}.sidebar .full-list>li.group:first-child{margin-top:0}.sidebar .full-list>li>ul>li:not(:has(li a[aria-selected=true]))>a[aria-selected=true]:before,.sidebar .full-list>li>ul>li>a:hover:before{content:"\2022";position:absolute;margin-left:-15px;color:var(--sidebarActiveItem)}.sidebar .full-list ul li{line-height:var(--sidebarFontSize);padding:0 8px}.sidebar .full-list ul a{padding-left:15px;height:24px}.sidebar .full-list ul button{font-size:var(--sidebarFontSize)}.sidebar .full-list ul button:after{content:var(--icon-add)}.sidebar .full-list ul button[aria-expanded=true]:after{content:var(--icon-subtract)}.sidebar .full-list ul ul{margin:9px 0 9px 10px}.sidebar .full-list ul ul li{height:20px;color:var(--sidebarAccentMain)}.sidebar .full-list ul ul a{border-left:1px solid var(--sidebarInactiveItemMarker);padding:0 10px;height:20px}.sidebar .full-list ul ul a:hover{border-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list ul ul a[aria-selected]{color:var(--sidebarActiveItem);border-color:var(--sidebarLanguageAccentBar)}.sidebar-button{cursor:pointer;background-color:transparent;border:none;font-size:var(--sidebarFontSize);color:var(--sidebarAccentMain)}.sidebar-button:hover{color:var(--sidebarHover)}body:not(.sidebar-open) .sidebar-button{color:var(--contrast)}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar .full-list{overflow:visible}}.top-search{background-color:var(--background);top:0;z-index:99;position:relative;width:100%;padding:10px 0}.search-settings{display:flex;column-gap:12px;align-items:center;width:100%;position:relative}.search-bar{border:1px solid var(--searchBarBorder);border-radius:var(--borderRadius-base);height:40px;position:relative;width:100%}.top-search .search-bar .search-input{background-color:var(--searchSearch);border:1px solid transparent;border-radius:var(--borderRadius-base);color:var(--searchAccentMain);position:relative;height:38px;padding:8px 40px 8px 12px;width:100%;transition:var(--transition-all)}.top-search .search-bar .search-input::placeholder{color:var(--searchAccentMain);opacity:.5}.top-search .search-bar .search-input:focus{border:1px solid var(--searchBarFocusColor);border-radius:calc(var(--borderRadius-base) - 1px);position:relative}.top-search .search-bar .search-label{position:relative}.top-search .search-bar .search-input-wrapper{position:relative;display:flex;align-items:center}.top-search .search-settings .engine-selector{position:relative}.top-search .search-settings .engine-selector[data-multiple=false]{display:none}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button{cursor:default}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button .ri-arrow-down-s-line{display:none}.top-search .search-settings .engine-button{display:flex;align-items:center;gap:6px;background-color:var(--autocompleteHover);border:none;color:var(--searchAccentMain);cursor:pointer;font-size:var(--text-sm);padding:8px 12px;border-radius:var(--borderRadius-base);transition:var(--transition-all);height:40px}.top-search .search-settings .engine-button:hover,.top-search .search-settings .engine-button[aria-expanded=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-name{font-size:var(--text-sm);white-space:nowrap}.top-search .search-settings .engine-dropdown{position:absolute;top:calc(100% + 10px);right:0;min-width:200px;max-width:50vh;width:max-content;background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;z-index:200}.top-search .search-settings .engine-dropdown[hidden]{display:none}.top-search .search-settings .engine-option{display:block;width:100%;text-align:left;background-color:transparent;border:0;color:var(--textHeaders);cursor:pointer;padding:10px 12px;transition:var(--transition-colors)}.top-search .search-settings .engine-option:hover{background-color:var(--autocompleteHover)}.top-search .search-settings .engine-option:focus{border-left:2px solid var(--main);padding-left:10px;background-color:var(--autocompleteHover);outline:none}.top-search .search-settings .engine-option[aria-checked=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-option .name{display:block;font-size:var(--text-sm);font-weight:500;margin-bottom:2px}.top-search .search-settings .engine-option .help{display:block;font-size:var(--text-xs);opacity:.7}.top-search .search-bar .search-close-button{font-size:var(--text-md);color:var(--searchAccentMain);background-color:transparent;border:none;cursor:pointer;margin:0;opacity:.5;padding:5px;transform:scaleY(0);transition:var(--transition-all);display:flex;align-items:center;position:absolute;right:0;top:50%;transform:translateY(-50%) scaleY(0)}.top-search .search-bar .search-close-button:hover{opacity:.7}.top-search .search-settings button.icon-settings{display:flex;align-items:center;justify-content:flex-end}.top-search .search-settings .icon-settings{font-size:var(--text-xl);float:right;color:var(--iconAction);text-decoration:none;border:none;transition:color .3s ease-in-out;background-color:transparent;cursor:pointer;padding:0}.top-search .search-settings .icon-settings:hover{color:var(--iconActionHover)}.top-search .search-settings .icon-settings:visited{color:var(--iconAction)}@media screen and (max-width:768px){.top-search{padding-left:calc(var(--content-gutter) + 36px);padding-right:var(--content-gutter);margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));width:calc(2 * var(--content-gutter) + 100%)}.search-settings{width:100%;box-sizing:border-box}.top-search .search-settings .engine-name{display:none}}body.search-focused .search-bar .search-close-button{transform:translateY(-50%) scaleY(1);transition:var(--transition-all)}@media screen and (hover:hover){body.search-focused .top-search{position:sticky!important}body.search-focused .sidebar-button{position:fixed!important}}@media screen and (hover:none){body.scroll-sticky .top-search{position:sticky!important}body.scroll-sticky .sidebar-button{position:fixed!important}}*:focus,button:focus,[type=button]:focus,[type=reset]:focus,[type=submit]:focus{outline:2px solid var(--main);outline-offset:-2px}*:focus:not(:focus-visible),button:focus:not(:focus-visible),[type=button]:focus:not(:focus-visible),[type=reset]:focus:not(:focus-visible),[type=submit]:focus:not(:focus-visible){outline:0}input[type=text],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=email],input[type=month],input[type=password],input[type=search],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{outline:0}.content-inner{font-size:1em;line-height:1.6875em;position:relative;background-color:var(--background);color:var(--textBody)}.content-inner .heading-with-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px}.content-inner .heading-with-actions>*:not(h1){flex-shrink:0}.content-inner .heading-with-actions h1{flex-grow:1;justify-self:flex-start;max-width:100%;margin:0;overflow-wrap:break-word}.content-inner .heading-with-actions .icon-action{width:20px;height:20px;display:flex;justify-content:center;align-items:center;font-weight:400;margin-left:3px}.content-inner .heading-with-actions.top-heading .icon-action{font-size:1.2rem}@container content (width > 600px){.content-inner .heading-with-actions.top-heading{flex-wrap:nowrap;align-items:flex-start;& h1{padding-right:32px}.icon-action{padding-top:1.7rem}}}.content-inner .top-heading{padding-top:1rem}.content-inner :is(h1,h2,h3,h4,h5,h6){font-family:var(--sansFontFamily);font-weight:700;line-height:1.5em;word-wrap:break-word;color:var(--textHeaders)}.content-inner div.group-description{margin:0 0 3em}.content-inner h1 small{font-weight:400}.content-inner{& h1{font-size:1.5rem;margin-top:1.75em}& h2{font-size:1.35rem;margin-top:1.5em;margin-bottom:.5em}& h3{font-size:1.25rem;margin-top:1.5em;margin-bottom:.5em}:is(h4,h5,h6){font-size:1.15rem;margin-top:1.25em;margin-bottom:.5em}:is(h5,h6){font-weight:400}& h6{font-size:1rem}}@container content (width > 600px){.content-inner{& h1{font-size:2rem}& h2{font-size:1.75rem}& h3{font-size:1.45rem}}}.content-inner li+li{margin-top:.25em}.content-inner :is(a,.a-main){color:var(--link-color);text-decoration:underline;text-decoration-skip-ink:auto}.content-inner :is(a:visited,.a-main:visited){color:var(--link-visited-color)}.content-inner .icon-action{color:var(--iconAction);text-decoration:none;border:none;transition:var(--transition-colors);background-color:transparent;cursor:pointer}.content-inner .icon-action:hover{color:var(--iconActionHover)}.content-inner .icon-action:visited{color:var(--iconAction)}.content-inner .livebook-badge-container{display:flex}.content-inner a.livebook-badge{display:inline-flex}.content-inner .note{color:var(--iconAction);font-size:var(--text-xs);font-weight:400}.content-inner{:is(blockquote,section.admonition){position:relative;margin:1.5625em 0;padding:0 1.2rem;overflow:auto;border-radius:var(--borderRadius-base);:is(h1,h2,h3,h4,h5,h6){margin:1.25rem 0}:is(h1,h2,h3){font-size:1.375rem}:is(h4,h5,h6){font-size:1rem}:is(code,pre code){background-color:var(--admCodeBackground);border:1px solid var(--admCodeBorder);color:var(--admCodeColor)}:is(a,a:visited){color:inherit}:is(h1,h2,h3,h4,h5,h6):first-child{padding-bottom:1rem;border-bottom:2px solid hsl(from currentColor h s l / 15%)}& p:last-child{padding-bottom:1em;margin-bottom:0}}& blockquote{border-left:3px solid var(--blockquoteBorder);background-color:var(--blockquoteBackground)}}@media print{.content-inner :is(blockquote,section.admonition){border:2px solid hsl(from var(--textBody) h s l / 15%);:is(code,pre code){border-color:var(--gray400)}& pre code.makeup{white-space:break-spaces;break-inside:avoid}}}.content-inner table{margin:2em 0;border-collapse:collapse;display:block;overflow:auto}.content-inner th{text-align:left;font-family:var(--sansFontFamily);font-weight:700;padding-bottom:.5em;white-space:nowrap}.content-inner thead tr{border-bottom:1px solid var(--tableHeadBorder)}.content-inner tbody tr{border-bottom:1px solid var(--tableBodyBorder)}.content-inner tbody tr:last-child{border-bottom:none}.content-inner tr{vertical-align:bottom;height:2.5em}.content-inner :is(td,th){padding:.25em .25em .25em 1em;line-height:2em;vertical-align:top}.content-inner .section-heading{--icon-size: 16px;--icon-spacing: 5px;display:grid;grid-template:1fr / 1fr}@media screen and (max-width:768px){.content-inner .section-heading{--icon-spacing: 2px}}.content-inner .section-heading>:is(.hover-link,.text){grid-row:1;grid-column:1}.content-inner .section-heading .hover-link{text-decoration:none}.content-inner .section-heading i{font-size:var(--icon-size);color:var(--mainLight);top:-2px;margin-left:calc(-1 * (var(--icon-size) + var(--icon-spacing)));padding-right:var(--icon-spacing);position:relative;opacity:0}.content-inner :is(blockquote,section.admonition) .section-heading i{display:none}.content-inner .section-heading:is(:hover,:focus,:target) i{opacity:1}.content-inner .app-vsn{display:none!important;font-size:.6em;line-height:1.5em}@media screen and (max-width:768px){.content-inner .app-vsn{display:block!important}}.content-inner img{max-width:100%}@media screen and (max-width:768px){.content-inner :is(ol,ul){padding-left:calc(1.5 * var(--content-gutter))}}.content-inner section.admonition{border-radius:var(--borderRadius-base)}.content-inner section.admonition>.admonition-title{color:var(--contrast);padding-left:2.2rem;font-weight:700;font-style:normal}.content-inner section.admonition>.admonition-title:before{color:var(--contrast);position:absolute;left:1rem;font-size:1.8rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.content-inner section.admonition{&.warning{background-color:var(--warningBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--warningHeading)}}}&.error{background-color:var(--errorBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--errorHeading)}}}&.info{background-color:var(--infoBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--infoHeading)}}}&.neutral{background-color:var(--neutralBackground);.admonition-title{&,&:before{content:var(--icon-double-quotes-l);color:var(--neutralHeading)}}}&.tip{background-color:var(--tipBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--tipHeading)}}}}@media print{.content-inner section.admonition{&.warning{border-color:hsl(from var(--warningHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--warningHue),90%,40%)}}}&.error{border-color:hsl(from var(--errorHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--errorHue),80%,40%)}}}&.info{border-color:hsl(from var(--infoHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--infoHue),80%,40%)}}}&.neutral{border-color:hsl(from var(--neutralHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--neutralHue),30%,40%)}}}&.tip{border-color:hsl(from var(--tipHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--tipHue),50%,40%)}}}}}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@media screen and (max-width:768px){.content-inner section.admonition{margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0}}.content-inner .summary h3 a{text-decoration:none;border:none;color:var(--textHeaders)!important}.content-inner .summary span.deprecated{color:var(--darkDeprecated);font-weight:400}.content-inner .summary .summary-row .summary-signature{font-family:var(--monoFontFamily);font-size:13px;font-weight:700}.content-inner .summary .summary-row .summary-signature a{text-decoration:none;border:none}.content-inner .summary .summary-row .summary-synopsis{padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.content-inner .footer{margin:4em auto 1em;text-align:center;font-size:var(--text-sm)}.content-inner .footer .line{display:inline-block}.content-inner .footer .line a,.content-inner .footer .line button{padding:0 4px}.content-inner .footer button{background-color:transparent;border:0;cursor:pointer}.content-inner .bottom-actions{display:flex;justify-content:space-between;margin-top:7em;gap:12px}.bottom-actions-item{flex:1 1 0%}.content-inner .bottom-actions .bottom-actions-button{display:flex;text-decoration:none;flex-direction:column;border-radius:var(--borderRadius-sm);border:1px solid var(--bottomActionsBtnBorder);padding:12px 16px;min-width:150px;transition:var(--transition-all)}.content-inner .bottom-actions .bottom-actions-button:hover{border-color:var(--mainLight)}.content-inner .bottom-actions .bottom-actions-button .subheader{font-size:.8em;color:var(--textHeaders);white-space:nowrap}.content-inner .bottom-actions .bottom-actions-button .title{color:var(--bottomActionsBtnTitle)}.content-inner .bottom-actions .bottom-actions-button[rel=prev]{text-align:start}.content-inner .bottom-actions .bottom-actions-button[rel=next]{text-align:end}@media screen and (max-width:768px){.content-inner .bottom-actions{flex-direction:column-reverse}}.page-cheatmd .content-inner{--horizontal-space: 1.5em;--vertical-space: 1em}@media(max-width:600px){.page-cheatmd .content-inner{--horizontal-space: 1em;--vertical-space: .75em}}.page-cheatmd .content-inner{max-width:1200px}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& h1{margin-bottom:var(--vertical-space)}& h2{font-size:1.6rem;margin:var(--vertical-space) 0;column-span:all;color:var(--gray700);font-weight:500}& h3{font-size:1.375rem;margin:0 0 1em;font-weight:400}& section.h3{min-width:300px;margin:0;padding:0 0 calc(var(--vertical-space) * 2) 0;break-inside:avoid}& h3 .text{overflow:hidden}& h3 .text:after{content:"";margin-left:calc(var(--horizontal-space) / 2);vertical-align:baseline;display:inline-block;width:100%;height:1px;margin-right:-100%;margin-bottom:5px;background-color:var(--codeBorder)}& h4{font-size:1rem;display:block;margin:0;padding:.25em var(--horizontal-space);font-weight:400;background:var(--gray100);color:#567;border:solid 1px 1px 0 1px var(--gray100)}.h2 p{margin:0;display:block;background:var(--gray50);padding:var(--vertical-space) var(--horizontal-space)}.h2 p>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}& pre code{padding:var(--vertical-space) var(--horizontal-space)}.h2 pre{margin:0}.h2 pre+pre{margin-top:-1px}& pre.wrap{white-space:break-spaces}@media screen and (max-width:768px){& pre code{border-left-width:1px!important;border-right-width:1px!important}}.h2 table{display:table;box-sizing:border-box;width:100%;border-collapse:collapse;margin:0}.h2 th{padding:var(--vertical-space) var(--horizontal-space);line-height:inherit;margin-bottom:-1px;vertical-align:middle;border-bottom:1px solid var(--codeBorder)}.h2 td{padding:var(--vertical-space) var(--horizontal-space);border:0;border-bottom:1px solid var(--codeBorder)}.h2 tr:first-child{border-top:1px solid var(--codeBorder)}.h2 td code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}.h2 thead{background-color:var(--gray50)}.h2 tbody{background-color:var(--codeBackground)}.h2 :is(ul,ol){margin:0;padding:0}.h2 li{list-style-position:inside;padding:.5em var(--horizontal-space);line-height:2em;vertical-align:middle;background-color:var(--codeBackground);border-bottom:1px solid var(--codeBorder);margin-top:0}.h2 :is(ul,ol)+pre code{border-top:0}.h2 li>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}}}@scope(.dark .page-cheatmd)to (.admonition){:scope .content-inner{& h2{color:var(--gray200)}& h4{background:#192f50;color:var(--textBody);border:1px solid #192f50;border-bottom:0}.h2 p{background:var(--gray700)}.h2 thead{background-color:var(--gray700)}}}.page-cheatmd .content-inner{& section.width-50{display:block;width:50%;margin:0;>section>table{width:100%}}& section:is(.col-2,.col-2-left,.col-3){column-gap:40px}& section.col-2{column-count:2;height:auto}& section.col-2-left{display:grid;grid-template-columns:calc(100% / 3) auto;>h2{grid-column-end:span 2}}& section.col-3{column-count:3;height:auto}& section.list-4>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 25%}}& section.list-6>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 calc(100% / 6)}}@media screen and (max-width:1400px){& section.col-3{column-count:2}& section.col-2-left{display:flex;flex-direction:column}}@media screen and (max-width:1200px){& section:is(.col-2,.col-3){display:flex;flex-direction:column}& section.list-6>ul>li{flex:0 0 25%}}@media screen and (max-width:1000px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 calc(100% / 3)}}@media screen and (max-width:600px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 50%}& section.width-50{width:100%}}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.page-search #search{min-height:200px;position:relative;.loading{height:64px;width:64px;position:absolute;top:50%;left:calc(50% - 32px)}.loading div{box-sizing:border-box;display:block;position:absolute;width:51px;height:51px;margin:6px;border:6px solid var(--coldGray);border-radius:50%;animation:loading 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:var(--coldGray) transparent transparent transparent}.loading div:nth-child(1){animation-delay:-.45s}.loading div:nth-child(2){animation-delay:-.3s}.loading div:nth-child(3){animation-delay:-.15s}.result{margin:2em 0}.result p{margin:0}.result-id{font-size:1.4em;margin:0}.result-id a{text-decoration:none;color:var(--textHeaders);transition:var(--transition-colors)}.result-id a:is(:visited,:active){color:var(--textHeaders)}.result-id a:is(:hover,:focus){color:var(--main)}:is(.result-id,.result-elem) em{font-style:normal;color:var(--main)}.result-id small{font-weight:400}}@keyframes keyboard-shortcuts-show{0%{opacity:0}to{opacity:1}}.modal{animation-duration:.15s;animation-name:keyboard-shortcuts-show;animation-iteration-count:1;animation-timing-function:ease-in-out;display:none;background-color:#000000bf;position:fixed;inset:0;z-index:300}.modal.shown{display:block}.modal .modal-contents{margin:75px auto 0;max-width:500px;background-color:var(--modalBackground);border-radius:var(--borderRadius-sm);box-shadow:2px 2px 8px #0003;padding:25px 35px 35px}@media screen and (max-width:768px){.modal .modal-contents{padding:20px}}.modal .modal-header{display:flex;align-items:start}.modal .modal-title{display:inline-block;flex-grow:1;font-size:1.2rem;font-weight:700;margin-bottom:20px}.modal .modal-title button{border:none;background-color:transparent;color:var(--textHeaders);font-weight:700;margin-right:30px;padding-left:0;text-align:left;transition:var(--transition-colors)}.modal .modal-title button:hover{color:var(--main);cursor:pointer}.modal .modal-title button.active{color:var(--main)}.modal .modal-close{cursor:pointer;display:block;font-size:1.5rem;margin:-8px -8px 0 0;padding:8px;opacity:.7;background-color:transparent;color:var(--textHeaders);border:none;transition:var(--transition-opacity)}.modal .modal-close:hover{opacity:1}#keyboard-shortcuts-content dl.shortcut-row{display:flex;align-items:center;justify-content:space-between;margin:0;padding:6px 0 8px;border-bottom:1px solid var(--settingsSectionBorder)}#keyboard-shortcuts-content dl.shortcut-row:last-of-type{border-bottom-style:none}#keyboard-shortcuts-content dl.shortcut-row:first-child{padding-top:0}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){display:inline-block}#keyboard-shortcuts-content kbd>kbd{background-color:var(--settingsInputBorder);color:var(--contrast);border-radius:var(--borderRadius-sm);font-family:inherit;font-weight:700;display:inline-block;line-height:1;padding:4px 7px 6px;min-width:26px;text-align:center;font-size:var(--text-sm)}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){margin:0}#quick-switch-modal-body{width:100%;position:relative}#quick-switch-modal-body .ri-search-2-line{position:absolute;left:0;top:0;padding:4px 10px;color:var(--quickSwitchContour);font-weight:700}#quick-switch-modal-body #quick-switch-input{width:100%;padding:8px 6px 8px 38px;border:none;color:var(--quickSwitchInput);background-color:transparent;border-bottom:1px solid var(--quickSwitchContour);box-sizing:border-box;transition:all .12s ease-out}#quick-switch-modal-body #quick-switch-results{margin:0}#quick-switch-modal-body .quick-switch-result{padding:2px 5px;border-bottom:1px dotted var(--quickSwitchContour);transition:all .12s ease-out}#quick-switch-modal-body .quick-switch-result:last-child{border-bottom:none}#quick-switch-modal-body .quick-switch-result:hover{cursor:pointer}#quick-switch-modal-body .quick-switch-result:is(:hover,.selected){border-left:4px solid var(--main);background-color:var(--codeBackground)}.autocomplete{display:none;position:absolute;width:calc(100% - 32px);top:55px}.autocomplete .triangle{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-bottom:12px solid var(--autocompleteBackground);position:absolute;top:8px;left:26px;transform:translate(-50%);z-index:100;background-color:transparent}.autocomplete-results{margin:0;padding:15px 20px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;color:var(--autocompleteResults);font-weight:300;font-size:.9rem;font-style:italic}.autocomplete-results .query{margin-right:auto}.autocomplete-results .bold{color:var(--autocompleteResultsBold);font-weight:400}.autocomplete.shown{display:block}.autocomplete-container{position:absolute;top:5px;width:100%;z-index:200}.autocomplete-suggestions{background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;overflow-y:auto;max-height:450px;white-space:normal;overflow-x:hidden;overscroll-behavior-y:contain;scrollbar-width:thin}.autocomplete-suggestion{color:var(--textHeaders)!important;display:block;padding:12px 20px;text-decoration:none!important;transition:var(--transition-colors);border-top:1px solid var(--suggestionBorder);font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.autocomplete-suggestion.selected{background-color:var(--autocompleteSelected)}.autocomplete-suggestion:hover{background-color:var(--autocompleteHover)}.autocomplete-suggestion em{font-style:normal;font-weight:700}.autocomplete-suggestion .separator{opacity:.4;margin:0 2px}.autocomplete-suggestion .description{opacity:.6}.autocomplete-suggestion .label{background-color:var(--autocompleteLabelBack);opacity:.6;color:var(--autocompleteLabelFont);padding:4px 8px;border-radius:4px;margin-left:5px;text-transform:uppercase;font-family:var(--sansFontFamily);font-size:.7rem}@media screen and (hover:none){.autocomplete-results .press-return{display:none!important}}.tooltip{box-shadow:0 0 10px var(--black-opacity-10);max-height:300px;max-width:500px;padding:0;position:absolute;pointer-events:none;margin:0;z-index:99;top:0;left:0;visibility:hidden;transform:translateY(20px);opacity:0;transition:.2s visibility ease-out,.2s transform ease-out,.2s opacity ease-out}.tooltip.tooltip-shown{visibility:visible;transform:translateY(0);opacity:1}.tooltip .tooltip-body{border:1px solid var(--codeBorder);border-radius:var(--borderRadius-sm);overflow:auto}.tooltip .tooltip-body .signature{min-width:320px;width:100%;line-height:1em!important;margin:.75em 0!important}.tooltip .tooltip-body .detail-header{border-left:0;margin-bottom:0;margin-top:0}.tooltip .tooltip-body .docstring{background-color:var(--background);padding:1.2em;margin:0;width:498px}.tooltip .tooltip-body .docstring-plain{max-width:498px;width:auto}.tooltip .tooltip-body .version-info{float:right;font-family:var(--monoFontFamily);font-weight:400;opacity:.3;padding-left:.3em}pre{position:relative}pre:hover .copy-button,pre .copy-button:focus{opacity:1}.copy-button{display:flex;opacity:0;position:absolute;top:7px;right:8px;padding:8px;background-color:transparent;backdrop-filter:blur(8px);border-radius:var(--borderRadius-sm);border:1px solid var(--codeBorder);cursor:pointer;transition:var(--transition-all);font-size:var(--text-sm);line-height:24px;color:currentColor;& svg[aria-live=polite]{display:none}}.copy-button svg{opacity:.5;transition:var(--transition-all)}pre .copy-button:hover svg,pre .copy-button:focus-visible svg{opacity:1}.copy-button svg{width:20px}.copy-button.clicked{opacity:1;color:var(--success);& svg[aria-live=polite]{display:block}}.copy-button.clicked svg{display:none;color:currentColor}#settings-modal-content{margin-top:10px}#settings-modal-content .hidden{display:none}#settings-modal-content .input{box-sizing:border-box;width:80%;padding:8px;font-size:var(--text-sm);background-color:var(--settingsInputBackground);color:var(--settingsInput);border:1px solid var(--settingsInputBorder);border-radius:var(--borderRadius-base);transition:var(--transition-all)}#settings-modal-content .input:focus{border-color:var(--main)}#settings-modal-content .input::placeholder{color:var(--gray400)}#settings-modal-content .switch-button-container{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--settingsSectionBorder);padding:10px 0}#settings-modal-content .switch-button-container:first-of-type{border-top-style:none;padding-top:0}#settings-modal-content .switch-button-container>div>span{font-size:var(--text-md)}#settings-modal-content .switch-button-container>div>p{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--text-sm);line-height:1.4;margin:0;padding-bottom:6px;padding-right:10px}#settings-modal-content .switch-button{position:relative;display:inline-block;flex-shrink:0;width:40px;height:20px;user-select:none;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox{appearance:none;position:absolute;display:block;width:20px;height:20px;border-radius:1000px;background-color:#91a4b7;border:3px solid #e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__bg{display:block;width:100%;height:100%;border-radius:1000px;background-color:#e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox:checked{background-color:#fff;border-color:var(--main);transform:translate(100%)}#settings-modal-content .switch-button__checkbox:checked+.switch-button__bg{background-color:var(--main)}#settings-modal-content .switch-button__checkbox:focus{outline:0}#settings-modal-content .switch-button__checkbox:focus+.switch-button__bg{outline:2px solid var(--main);outline-offset:2px}#settings-modal-content .switch-button__checkbox:focus:not(:focus-visible)+.switch-button__bg{outline:0}#settings-modal-content .settings-select{cursor:pointer;position:relative;border:none;background-color:transparent;color:var(--textBody)}#settings-modal-content .settings-select option{color:initial}#toast{visibility:hidden;opacity:0;position:fixed;z-index:1000;right:10px;top:0;font-size:.9rem;min-width:3rem;padding:.7rem 1.2rem .7rem 2.8rem;text-align:left;font-weight:400;border-radius:var(--borderRadius-base);border:1px solid hsl(from var(--tipHeading) h s l / 15%);background-color:var(--tipBackground);color:var(--tipHeading);transition:opacity .4s ease-in-out,transform .3s ease-out;cursor:default}#toast:before{content:var(--icon-information);position:absolute;left:1rem;font-size:1.2rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#toast.show{visibility:visible;opacity:1;transform:translateY(.75rem)}@media(prefers-reduced-motion:reduce){#toast{transition:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}@media print{.body-wrapper{display:block}.sidebar,.sidebar-button,.top-search{display:none}.content{padding-left:0;overflow:visible;left:0;width:100%}.summary-row{break-inside:avoid}#toast{display:none}.content-inner{padding:0}.content-inner .section-heading a.hover-link,.content-inner button.icon-action,.content-inner a.icon-action,.content-inner .bottom-actions{display:none}.footer p:first-of-type{display:none}.content-inner pre code.makeup{white-space:break-spaces;break-inside:avoid}}@media print{.page-cheatmd .content-inner{max-width:100%;width:100%;padding:0;font-size:.7em;& section:is(.col-2,.col-2-left,.col-3){column-gap:30px}& section.col-2{column-count:2}& section.col-2-left{display:grid}& section.col-3{column-count:3}}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& *{background-color:transparent!important;border-color:var(--gray400)!important}& h1{margin-top:0;margin-bottom:.5em}& h2.section-heading{font-weight:700;margin-top:1em;column-span:all}& section.h2{break-inside:avoid}& h3{font-weight:700;color:var(--mainDark)}& h3:after{height:2px;background-color:var(--gray400)}& section.h3{min-width:300px;break-inside:avoid}& h4{padding:.5em 0;border:none;font-weight:700;color:#000}.h2 p{padding-left:0;padding-right:0;border:none!important}& code{line-height:1.5em}.h2 table{font-variant-numeric:tabular-nums;break-inside:avoid}.h2 :is(th,td){vertical-align:top;padding-left:0;padding-right:0}.h2 thead{border-style:solid none;border-width:1px}.h2 tr{border-bottom:none}.h2 th{font-weight:700}.h2 li{padding-left:0;padding-right:0;vertical-align:middle;border-bottom:none}& pre:hover button.copy-button{display:none}& div.tooltip{display:none}& footer p:not(.built-using){display:none}}}}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}.tabset{--borderWidth: 1px;--tabsetPadding: var(--baseLineHeight);margin:var(--baseLineHeight) 0;border:var(--borderWidth) solid var(--tabBorder);padding:0 var(--tabsetPadding);border-radius:var(--borderRadius-lg)}.tabset-tablist{display:flex;overflow:auto;scrollbar-width:thin;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--tabBorderTop)}.tabset-tab{padding:1.1rem var(--tabsetPadding);font-family:var(--sansFontFamily);color:var(--textColor);margin-right:calc(-1 * var(--borderWidth));background-color:transparent;border:0;box-shadow:none;cursor:pointer;border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:transparent;transition:var(--transition-all)}:hover.tabset-tab{border-bottom-color:var(--tabBorderTop);color:var(--textHeaders)}.tabset-tab[aria-selected=true]{border-bottom-color:var(--mainLight);color:var(--textHeaders)}.tabset-tab[aria-selected=true]:focus-visible{background-color:var(--mainLight);border-color:var(--mainLight);color:var(--white)}@media screen and (max-width:768px){.tabset{--tabsetPadding: calc(var(--baseLineHeight) / 2)}.tabset-panel{padding-top:calc(var(--tabsetPadding) / 2);padding-bottom:calc(var(--tabsetPadding) / 2)}.tabset-panel pre,.tabset-panel blockquote,.tabset-panel section.admonition{margin-left:calc(-1 * var(--tabsetPadding))!important;margin-right:calc(-1 * var(--tabsetPadding))!important}.tabset-panel>pre code{border-left-width:0;border-right-width:0}}@media screen and (max-width:768px){.tabset-panel>:is(:first-child){&:is(table){margin:.5em 0}}}@media screen and (min-width:769px){.tabset-panel>:is(:first-child){&:is(blockquote,.admonition){margin-top:1.5em}&:is(p:has(img)){margin-top:1.25em}&:is(table){margin-top:.75em}}.tabset-panel>:is(:last-child){&:is(blockquote,.admonition){margin-bottom:1.5em}&:is(p:not(:has(img)),ul,ol){margin-bottom:1.25em}&:is(table){margin-bottom:.75em}}}body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"],body.dark .content-inner img[src*="#gh-light-mode-only"]{display:none} +/*! Bundled license information: + +modern-normalize/modern-normalize.css: + (*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize *) +*/ diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/inline_html-4XT25SPW.js b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/inline_html-4XT25SPW.js new file mode 100644 index 0000000..048b201 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/inline_html-4XT25SPW.js @@ -0,0 +1 @@ +(()=>{var t="ex_doc:settings",e="dark";var o="dark",s="light";var E="sidebar_state",n="closed";var r="sidebar_width";var a="sidebar-open";var i=new URLSearchParams(window.location.search),S=i.get("theme")||JSON.parse(localStorage.getItem(t)||"{}").theme;(S===o||S!==s&&window.matchMedia("(prefers-color-scheme: dark)").matches)&&document.body.classList.add(e);var d=sessionStorage.getItem(E),A=d!==n&&!window.matchMedia(`screen and (max-width: ${768}px)`).matches;document.body.classList.toggle(a,A);var c=sessionStorage.getItem(r);c&&document.body.style.setProperty("--sidebarWidth",`${c}px`);var p=/(Macintosh|iPhone|iPad|iPod)/.test(window.navigator.userAgent);document.documentElement.classList.toggle("apple-os",p);})(); diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-400-normal-MNITWADU.woff b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-400-normal-MNITWADU.woff new file mode 100644 index 0000000000000000000000000000000000000000..eac7dd1ca48064ec6ee9cbd72ab121c70d765d7c GIT binary patch literal 34020 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 literal 0 HcmV?d00001 diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-700-normal-XMT5XFBS.woff b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-700-normal-XMT5XFBS.woff new file mode 100644 index 0000000000000000000000000000000000000000..ac9382a811495707732baba8feb48e87b9768169 GIT binary patch literal 33296 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 QOFh<%JiLFgmWQ+r07g396aWAK literal 0 HcmV?d00001 diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-700-normal-2XVSBPG4.woff2 b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-700-normal-2XVSBPG4.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..11de83feaf0fb872c91fce89534fd4e2d8bbda28 GIT binary patch literal 23040 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 literal 0 HcmV?d00001 diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..edb9fa6f492362a9ab1f1d674b8f6e6f4e2085d0 GIT binary patch literal 5472 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 WhF)K4IGNT+hd8;|K8&%*Sz!P*WW1>W literal 0 HcmV?d00001 diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2c8aaa866eee8bee09ee05a9809c2fda7cd87bd1 GIT binary patch literal 5368 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 literal 0 HcmV?d00001 diff --git a/plugins/mob_iap/deps/ex_doc/hex_metadata.config b/plugins/mob_iap/deps/ex_doc/hex_metadata.config new file mode 100644 index 0000000..9dd57e3 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/hex_metadata.config @@ -0,0 +1,109 @@ +{<<"links">>, + [{<<"Changelog">>,<<"https://hexdocs.pm/ex_doc/changelog.html">>}, + {<<"GitHub">>,<<"https://github.com/elixir-lang/ex_doc">>}, + {<<"Writing documentation">>, + <<"https://hexdocs.pm/elixir/writing-documentation.html">>}]}. +{<<"name">>,<<"ex_doc">>}. +{<<"version">>,<<"0.40.3">>}. +{<<"description">>,<<"ExDoc is a documentation generation tool for Elixir">>}. +{<<"elixir">>,<<"~> 1.15">>}. +{<<"files">>, + [<<"CHANGELOG.md">>,<<"Cheatsheet.cheatmd">>,<<"formatters">>, + <<"formatters/html">>,<<"formatters/html/dist">>, + <<"formatters/html/dist/html-Y2MUTVIN.js">>, + <<"formatters/html/dist/lato-latin-700-normal-2XVSBPG4.woff2">>, + <<"formatters/html/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2">>, + <<"formatters/html/dist/lato-all-700-normal-XMT5XFBS.woff">>, + <<"formatters/html/dist/lato-all-400-normal-MNITWADU.woff">>, + <<"formatters/html/dist/remixicon-ZWU5GQ4X.woff2">>, + <<"formatters/html/dist/lato-latin-ext-400-normal-N27NCBWW.woff2">>, + <<"formatters/html/dist/lato-latin-400-normal-W7754I4D.woff2">>, + <<"formatters/html/dist/inline_html-4XT25SPW.js">>, + <<"formatters/html/dist/html-erlang-KCHZLXSC.css">>, + <<"formatters/html/dist/html-elixir-NHQLCD6Y.css">>,<<"formatters/epub">>, + <<"formatters/epub/dist">>, + <<"formatters/epub/dist/epub-erlang-IMUVAO3R.css">>, + <<"formatters/epub/dist/epub-elixir-A3GHWQ7R.css">>, + <<"formatters/epub/dist/epub-4WIP524F.js">>,<<"formatters/epub/metainfo">>, + <<"formatters/epub/metainfo/container.xml">>, + <<"formatters/epub/metainfo/com.apple.ibooks.display-options.xml">>, + <<"lib">>,<<"lib/ex_doc.ex">>,<<"lib/ex_doc">>,<<"lib/ex_doc/autolink.ex">>, + <<"lib/ex_doc/retriever.ex">>,<<"lib/ex_doc/extras.ex">>, + <<"lib/ex_doc/markdown">>,<<"lib/ex_doc/markdown/earmark.ex">>, + <<"lib/ex_doc/formatter.ex">>,<<"lib/ex_doc/config.ex">>, + <<"lib/ex_doc/language">>,<<"lib/ex_doc/language/erlang.ex">>, + <<"lib/ex_doc/language/source.ex">>,<<"lib/ex_doc/language/elixir.ex">>, + <<"lib/ex_doc/formatter">>,<<"lib/ex_doc/formatter/html.ex">>, + <<"lib/ex_doc/formatter/markdown">>, + <<"lib/ex_doc/formatter/markdown/templates">>, + <<"lib/ex_doc/formatter/markdown/templates/module_template.eex">>, + <<"lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex">>, + <<"lib/ex_doc/formatter/markdown/templates/api_reference_template.eex">>, + <<"lib/ex_doc/formatter/markdown/templates/detail_template.eex">>, + <<"lib/ex_doc/formatter/markdown/templates/nav_group_template.eex">>, + <<"lib/ex_doc/formatter/markdown/templates.ex">>, + <<"lib/ex_doc/formatter/config.ex">>,<<"lib/ex_doc/formatter/html">>, + <<"lib/ex_doc/formatter/html/search_data.ex">>, + <<"lib/ex_doc/formatter/html/templates">>, + <<"lib/ex_doc/formatter/html/templates/summary_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/sidebar_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/module_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/extra_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/search_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/redirect_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/head_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/api_reference_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/not_found_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/detail_template.eex">>, + <<"lib/ex_doc/formatter/html/templates/footer_template.eex">>, + <<"lib/ex_doc/formatter/html/templates.ex">>, + <<"lib/ex_doc/formatter/html/assets.ex">>, + <<"lib/ex_doc/formatter/epub.ex">>,<<"lib/ex_doc/formatter/markdown.ex">>, + <<"lib/ex_doc/formatter/epub">>,<<"lib/ex_doc/formatter/epub/templates">>, + <<"lib/ex_doc/formatter/epub/templates/module_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/extra_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/media-types.txt">>, + <<"lib/ex_doc/formatter/epub/templates/head_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/content_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/toc_item_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/nav_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates/title_template.eex">>, + <<"lib/ex_doc/formatter/epub/templates.ex">>, + <<"lib/ex_doc/formatter/epub/assets.ex">>,<<"lib/ex_doc/nodes.ex">>, + <<"lib/ex_doc/shell_lexer.ex">>,<<"lib/ex_doc/application.ex">>, + <<"lib/ex_doc/cli.ex">>,<<"lib/ex_doc/utils.ex">>, + <<"lib/ex_doc/language.ex">>,<<"lib/ex_doc/markdown.ex">>, + <<"lib/ex_doc/refs.ex">>,<<"lib/ex_doc/doc_ast.ex">>,<<"lib/mix">>, + <<"lib/mix/tasks">>,<<"lib/mix/tasks/docs.ex">>,<<"LICENSE">>,<<"mix.exs">>, + <<"README.md">>]}. +{<<"app">>,<<"ex_doc">>}. +{<<"licenses">>,[<<"Apache-2.0">>]}. +{<<"requirements">>, + [[{<<"name">>,<<"earmark_parser">>}, + {<<"app">>,<<"earmark_parser">>}, + {<<"optional">>,false}, + {<<"requirement">>,<<"~> 1.4.44">>}, + {<<"repository">>,<<"hexpm">>}], + [{<<"name">>,<<"makeup_elixir">>}, + {<<"app">>,<<"makeup_elixir">>}, + {<<"optional">>,false}, + {<<"requirement">>,<<"~> 0.14 or ~> 1.0">>}, + {<<"repository">>,<<"hexpm">>}], + [{<<"name">>,<<"makeup_erlang">>}, + {<<"app">>,<<"makeup_erlang">>}, + {<<"optional">>,false}, + {<<"requirement">>,<<"~> 0.1 or ~> 1.0">>}, + {<<"repository">>,<<"hexpm">>}], + [{<<"name">>,<<"makeup_c">>}, + {<<"app">>,<<"makeup_c">>}, + {<<"optional">>,true}, + {<<"requirement">>,<<">= 0.1.0">>}, + {<<"repository">>,<<"hexpm">>}], + [{<<"name">>,<<"makeup_html">>}, + {<<"app">>,<<"makeup_html">>}, + {<<"optional">>,true}, + {<<"requirement">>,<<">= 0.1.0">>}, + {<<"repository">>,<<"hexpm">>}]]}. +{<<"build_tools">>,[<<"mix">>]}. diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc.ex new file mode 100644 index 0000000..f9a98a6 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc.ex @@ -0,0 +1,549 @@ +defmodule ExDoc do + @moduledoc """ + Main entry point for generating docs. + """ + @ex_doc_version Mix.Project.config()[:version] + + @doc """ + Returns the ExDoc version (used in templates). + """ + @spec version :: String.t() + def version, do: @ex_doc_version + + @doc """ + Emits a warning. + """ + def warn(message, stacktrace_info) do + :persistent_term.put({__MODULE__, :warned?}, true) + IO.warn(message, stacktrace_info) + end + + defp unset_warned() do + warned? = :persistent_term.get({__MODULE__, :warned?}, false) + :persistent_term.erase({__MODULE__, :warned?}) + warned? + end + + @doc ~S""" + Generates documentation for the given `project`, `vsn` (version), + `source_beams` directories, and `options`. + + By default it generates HTML, Markdown, and EPUB documents. + + ## Options + + * `:annotations_for_docs` - a function that receives metadata and returns a list + of annotations to be added to the signature. The metadata received will also + contain `:module`, `:name`, `:arity` and `:kind` to help identify which entity is + currently being processed. + + * `:api_reference` - Whether to generate `api-reference.html`; default: `true`. + If this is set to false, `:main` must also be set. + + * `:assets` - A map of source => target directories that will be copied as is to + the output path. It defaults to an empty map. + + * `:authors` - List of authors for the generated docs or epub. + + * `:before_closing_body_tag` - a function that takes as argument an atom specifying + the formatter being used (`:html` or `:epub`) and returns a literal HTML string + to be included just before the closing body tag (``). + The atom given as argument can be used to include different content in both formats. + Useful to inject custom assets, such as Javascript. + + * `:before_closing_head_tag` - a function that takes as argument an atom specifying + the formatter being used (`:html` or `:epub`) and returns a literal HTML string + to be included just before the closing head tag (``). + The atom given as argument can be used to include different content in both formats. + Useful to inject custom assets, such as CSS stylesheets. + + * `:before_closing_footer_tag` - a function that takes as argument an atom specifying + the formatter being used (`:html`) and returns a literal HTML string + to be included just before the closing footer tag (``). + This option only has effect on the html formatter. + Useful if you want to inject an extra footer into the documentation. + + * `:canonical` - String that defines the preferred URL with the rel="canonical" + element; defaults to no canonical path. + + * `:cover` - Path to the epub cover image (only PNG or JPEG accepted) + The image size should be around 1600x2400. When specified, the cover will be placed under + the "assets" directory in the output path under the name "cover" and the + appropriate extension. This option has no effect when using the "html" formatter. + + * `:deps` - A keyword list application names and their documentation URL. + ExDoc will by default include all dependencies and assume they are hosted on + HexDocs. This can be overridden by your own values. Example: `[plug: "https://myserver/plug/"]` + + * `:description` - A brief description of the project, currently included in Hex.pm and the generated + `llms.txt` file + + * `:extra_section` - String that defines the section title of the additional + Markdown and plain text pages; default: "Pages". Example: "Guides" + + * `:extras` - List of paths to additional Markdown (`.md` extension), Live Markdown + (`.livemd` extension), Cheatsheets (`.cheatmd` extension), external urls (`:url` option), + and plain text pages to add to the documentation. You can also specify keyword pairs to + customize the generated filename, title and source file, and search content of each extra page; + default: `[]`. Example: `["README.md", "LICENSE", "CONTRIBUTING.md": [filename: "contributing", + title: "Contributing", source: "CONTRIBUTING.mdx"]]` See the Customizing Extras section for + more. + + * `:favicon` - Path to a favicon image file for the project. Must be PNG, JPEG or SVG. When + specified, the image file will be placed in the output "assets" directory, named + "favicon.EXTENSION". If using SVG, ensure appropriate width, height and viewBox attributes + are present in order to ensure predictable sizing and cropping. + + * `:filter_modules` - Include only modules that match the given value. The + value can be a regex, a string (representing a regex), or a two-arity + function that receives the module and its metadata and returns true if the + module must be included. If a string or a regex is given, it will be matched + against the complete module name (which includes the "Elixir." prefix for + Elixir modules). If a module has `@moduledoc false`, then it is always excluded. + + * `:formatters` - Formatter to use; default: ["html", "markdown", "epub"], options: "html", "markdown", "epub". + + * `:footer` - When false, does not render the footer on all pages, except for + the required "Built with ExDoc" note. + + * `:groups_for_extras`, `:groups_for_modules`, `:groups_for_docs`, and `:default_group_for_doc` - + See the "Groups" section. + + * `:ignore_apps` - Apps to be ignored when generating documentation in an umbrella project. + Receives a list of atoms. Example: `[:first_app, :second_app]`. + + * `:language` - Identify the primary language of the documents, its value must be + a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag. Default: "en". + + * `:logo` - Path to a logo image file for the project. Must be PNG, JPEG or SVG. When + specified, the image file will be placed in the output "assets" directory, named + "logo.EXTENSION". The image will be shown within a 48x48px area. If using SVG, ensure + appropriate width, height and viewBox attributes are present in order to ensure + predictable sizing and cropping. + + * `:main` - Main page of the documentation. It may be a module or a + generated page, like "Plug" or "api-reference". Default: "api-reference". + + * `:markdown_processor` - The markdown processor to use, + either `module()` or `{module(), keyword()}` to provide configuration options. + + * `:nest_modules_by_prefix` - See the "Nesting" section. + + * `:output` - Output directory for the generated docs. Default: "doc". + May be overridden by command line argument. + + * `:redirects` - A map or list of tuples, where the key is the path to redirect from and the + value is the path to redirect to. The extension is omitted in both cases, i.e `%{"old-readme" => "readme"}`. + The destination may include an anchor, i.e `%{"old-readme" => "readme#section"}`. + See the "Changing documentation over time" section below for more. + + * `:search` - A list of search engine configurations. See the "Search engines" section. + + * `:skip_undefined_reference_warnings_on` - ExDoc warns when it can't create a `Mod.fun/arity` + reference in the current project docs (for example, because of a typo). This option controls when to + skip such warnings. This option can be a list of strings that will be checked for exact matches, + or a function that takes a *reference* and must return a boolean (`true` means "skip this"). + *References* that are checked against this option (either whether they're in the given + list or whether they match the given function) are the relative filename, the "ID" of + the node (like `User.exists?/1`), or the module name. Examples for this option: + * `["Foo", "Bar.baz/0"]` - skip warnings for `Foo` and `Bar.baz/0` + * `&String.match?(&1, ~r/Foo/)` - skip warnings for any reference that matches the regex + * `["pages/deprecations.md"]` - skip warnings for any reference in the + `pages/deprecations.md` file + + * `:skip_code_autolink_to` - Similar to `:skip_undefined_reference_warnings_on`, this option + controls which terms will be skipped by ExDoc when building documentation. + Useful for example if you want to highlight private modules or functions without warnings. + This option can be a function from a term to a boolean (e.g.: `&String.match?(&1, ~r/PrivateModule/)`) + or a list of terms (e.g.:`["PrivateModule", "PrivateModule.func/1"]`); + default is nothing to be skipped. + + * `:source_beam` - Path to the beam directory; default: mix's compile path. + + * `:source_url` - The source URL fallback if `:source_url` is not given at the project + configuration. See the following sections. + + * `:source_ref` - The branch/commit/tag used for source link inference. + Default: "main". + + * `:source_url_pattern` - Public URL of the project for source links. This is derived + automatically from the project's `:source_url` and `:source_ref` when using one of + the supported public hosting services (currently GitHub, GitLab, or Bitbucket). If + you are using one of those services with their default public hostname, you do not + need to set this configuration. + + However, if using a different solution, or self-hosting, you will need to set this + configuration variable to a pattern for source code links. The value must be a string or + a function. + + If a string, then it should be the full URI to use for links with the following + variables available for interpolation: + + * `%{path}`: the path of a file in the repo + * `%{line}`: the line number in the file + + For self-hosted GitLab/GitHub: + + ```text + https://mydomain.org/user_or_team/repo_name/blob/main/%{path}#L%{line} + ``` + + For self-hosted Bitbucket: + + ```text + https://mydomain.org/user_or_team/repo_name/src/main/%{path}#cl-%{line} + ``` + + If a function, then it must be a function that takes two arguments, path and line, + where path is either an relative path from the cwd, or an absolute path. The function + must return the full URI as it should be placed in the documentation. + + ### Using `:source_url` and `:source_ref` together + + A common setup for a project or library is to set both `:source_url` and `:source_ref`. Setting + both of them will allow ExDoc to link to a specific version of the code for a function or module + that matches the version of the docs. If the docs have been generated for version 1.0.5, + clicking on the source link in the docs will take the browser to the source code for the 1.0.5 + version of the code instead of the primary ref (for example, `main`). + + An example setup: + + @version "0.30.10" + def project do + [ + ... + version: @version, + source_url: "https://github.com/USER/PROJECT", + docs: &docs/0 + ] + end + + def docs do + [ + ... + source_ref: "v#{@version}" + ] + end + + If you use `source_ref: "v#{@version}"`, you should run `git tag vVERSION` and push the tag when + publishing a new version of your package; ExDoc will generate links to the specific version for + which the docs were generated. + + ## Additional pages (extras) + + It is possible to attach additional pages to the documentation. Markdown extensions (and variations) + such as `.md`, `.livemd`, and `.cheatmd` rendered by extension. Other extensions are added as is. + + When specifying extras, the allowed configuration is: + + * `:title` - The title of the extra page. If not provided, the title will be inferred from the filename. + * `:filename` - The name of the generated file. If not provided, the filename will be inferred from + the source file. + * `:source` - The source file of the extra page. This is useful if you want to customize the filename or + title but keep the source file unchanged. + * `:search_data` - A list of terms to be indexed for autocomplete and search. If not provided, the content + of the extra page will be indexed for search. See the section below for more. + + It is also possible to specify URLs, which are added as links to the sidebar: + + * `:title` - The title of the extra page. If not provided, the title will be inferred from the extra name. + * `:url` - The external url to link to from the sidebar. + + ### Customizing search data + + It is possible to fully customize the way a given extra is indexed, both in autocomplete and in search. + In most cases, this makes sense for _generated_ documentation. If `search_data` is provided, it completely + overrides the built in logic for indexing your document based on the headers and content of the document. + The following fields can be provided in a list of maps for `search_data`. + + * `:anchor` - The anchor link for the search result. Use `""` to point to the top of the page. + * `:title` - The title of the result. + * `:type` - The type of the search result, such as "module", "function" or "section". + * `:body` - The main content or body of the search result, _as markdown_. Used in search, not autocomplete. + + ## Groups + + ExDoc content can be organized in groups. This is done via the `:groups_for_extras` + and `:groups_for_modules`. For example, imagine you are storing extra guides in + your documentation which are organized per directory. In the extras section you + have: + + extras: [ + "guides/introduction/foo.md", + "guides/introduction/bar.md", + + ... + + "guides/advanced/baz.md", + "guides/advanced/bat.md" + ] + + You can have those grouped as follows: + + groups_for_extras: [ + "Introduction": Path.wildcard("guides/introduction/*.md"), + "Advanced": Path.wildcard("guides/advanced/*.md") + ] + + Or via a regex: + + groups_for_extras: [ + "Introduction": ~r"/introduction/", + "Advanced": ~r"/advanced/" + ] + + External extras from a URL can also be grouped: + + groups_for_extras: [ + "Elixir": ~r"https://elixir-lang.org/", + "Erlang": ~r"https://www.erlang.org/" + ] + + Similar can be done for modules: + + groups_for_modules: [ + "Data types": [Atom, Regex, URI], + "Collections": [Enum, MapSet, Stream] + ] + + A regex or the string name of the module is also supported. + + ### Grouping functions, types, and callbacks + + Types, functions, and callbacks inside a module can also be organized in groups. + + #### Group metadata + + By default, ExDoc respects the `:group` metadata field to determine in which + group an element belongs: + + @doc group: "Queries" + def get_by(schema, fields) + + The function above will be automatically listed under the "Queries" section in + the sidebar. The benefit of using `:group` is that it can also be used by tools + such as IEx during autocompletion. These groups are then displayed in the sidebar. + + It is also possible to tell ExDoc to either enrich the group metadata or lookup a + different field via the `:default_group_for_doc` configuration. The default is: + + default_group_for_doc: fn metadata -> metadata[:group] end + + The `metadata` received contains all of the documentation metadata, such as `:group`, + but also `:module`, `:name`, `:arity` and `:kind` to help identify which entity is + currently being processed. For example, projects like Nx have a custom function that + converts "Queries" into "Function: Queries": + + default_group_for_doc: fn metadata -> + if group = metadata[:group] do + "Functions: #{group}" + end + end + + Finally, you can also use the `:groups_for_docs` which works similarly as the + one for modules/extra pages. + + `:groups_for_docs` is a keyword list of group titles and filtering functions + that receive the documentation metadata and must return a boolean. + For example, imagine that you have an API client library with a large surface + area for all the API endpoints you need to support. It would be helpful to + group the functions with similar responsibilities together. In this case in + your module you might have: + + defmodule APIClient do + @doc section: :auth + def refresh_token(params \\ []) + + @doc subject: :object + def update_status(id, new_status) + + @doc permission: :grant + def grant_privilege(resource, privilege) + end + + And then in the configuration you can group these with: + + groups_for_docs: [ + Authentication: & &1[:section] == :auth, + Resource: & &1[:subject] == :object, + Admin: & &1[:permission] in [:grant, :write] + ] + + A function can belong to a single group only. The first group that matches + will be the one used. In case no group is found in `:groups_for_docs`, + the `:default_group_for_doc` callback is invoked. If it returns nil, it + then falls back to the appropriate "Functions", "Types" or "Callbacks" + section respectively. + + #### Group descriptions + + It is possible to display a description for each group under its respective section + in a module's page. This helps to better explain what is the intended usage of each + group elements instead of describing everything in the displayed `@moduledoc`. + + Descriptions can be provided as `@moduledoc` metadata. Groups without descriptions are + also supported to define group ordering. + + @moduledoc groups: [ + "Main API", + %{title: "Helpers", description: "Functions shared with other modules."} + ] + + Descriptions can also be given in the `:default_group_for_doc` configuration: + + default_group_for_doc: fn metadata -> + case metadata[:group] do + :main_api -> "Main API" + :helpers -> [title: "Helpers", description: "Functions shared with other modules."] + _ -> nil + end + end + + Keyword lists or maps are supported in either case. + + When using `:groups_for_docs`, if all the elements for a given group are matched then the + `:default_group_for_doc` is never invoked and ExDoc will not know about the description. + In that case, the description should be provided in the `@moduledoc` `:groups` metadata. + + Whenever using the `:group` key, the groups will be ordered alphabetically. + If you also want control over the group order, you can also use the `:groups_for_docs` + which works similarly as the one for modules/extra pages. + + #### Group ordering + + Groups in the sidebar and main page body are ordered according to the following + rules: + + * First, groups defined as `@moduledoc groups: [...]` in the given order. + * Then groups defined as keys in the `:groups_for_docs` configuration. + * Then default groups: Types, Callbacks and Functions. + * Finally, other groups returned by `:default_group_for_doc` by alphabetical order. + + ## Nesting + + ExDoc also allows module names in the sidebar to appear nested under a given + prefix. The `:nest_modules_by_prefix` expects a list of module names, such as + `[Foo.Bar, Bar.Baz]`. In this case, a module named `Foo.Bar.Baz` will appear + nested within `Foo.Bar` and only the name `Baz` will be shown in the sidebar. + Note the `Foo.Bar` module itself is not affected. + + This option is mainly intended to improve the display of long module names in + the sidebar, particularly when they are too long for the sidebar or when many + modules share a long prefix. If you mean to group modules logically or call + attention to them in the docs, you should probably use `:groups_for_modules` + (which can be used in conjunction with `:nest_modules_by_prefix`). + + ## HTML-specific configuration + + ### Search engines + + ExDoc allows custom search engines via the `:search` key. Each search engine + is a map with the following keys: + + * `:name` - The display name of the search engine (required) + * `:help` - A help text describing what the search engine does (required) + * `:url` - The optional search URL template, usually ending with `q=` + * `:packages` - An optional list of packages (or package-versions) to search on + https://hexdocs.pm. For example: `[:plug, :phoenix, ecto: "3.0.0", ecto_sql: "3.0.0"]`. + If no version is specified, it uses the package latest + + If none of `:url` or `:packages` are given, ExDoc will use its default search engine + powered by Lunr. + + When multiple search engines are configured, a dropdown selector will appear next to + the search bar allowing users to choose which engine to use. For example: + + search: [ + %{name: "FooBar", help: "Search on FooBar", url: "https://example.com/?q="}, + %{name: "Local", help: "In-browser search"} + ] + + If only one search engine is configured, the dropdown selector will be hidden. + If no search engine is configured, only the built-in Lunr's is shown. + + ### Redirects and changing documentation over time + + As your project grows, your documentation may change, perhaps even structurally. + There are a few important things to consider in this regard: + + * Links to your *extras* will break if you change or move file names. + * Links to your *modules, and mix tasks* will change if you change their name. + * Links to *functions* are actually links to modules with anchor links. + If you change the function name, the link does not break but will leave users + at the top of the module's documentation. + + Because these docs are static files, the behaviour of a missing page will depend on where + they are hosted. In particular, [hexdocs.pm](https://hexdocs.pm) will show a 404 page. + + You can improve the developer experience on everything but function names changing + by using the `redirects` configuration. For example, if you changed the module `MyApp.MyModule` + to `MyApp.My.Module` and the extra `get-started.md` to `quickstart.md`, you can + setup the following redirects: + + redirects: %{ + "MyApp.MyModule" => "MyApp.My.Module", + "get-started" => "quickstart" + } + + The destination may also include an anchor to redirect to a specific section: + + redirects: %{ + "old-page" => "new-page#relevant-section" + } + + ### Meta tags configuration + + It is also possible to configure some of ExDoc's behaviour using meta tags. + + * `exdoc:autocomplete` - when set to "off", disables autocompletion. + + * `exdoc:autocomplete-limit` - Set to an integer to configure how many results + appear in the autocomplete dropdown. Defaults to 10. + + You can insert meta tags using the `before_closing_head_tag` option. + + ### Version dropdown menu + + ExDoc will automatically render a version dropdown on HTML pages if a + `docs_config.js` file is placed within the documentation. + This file may define the following global variables in JavaScript: + + * `versionNodes` - an array of `{"version":"vNUMBER", "url":url}` listing + all documented versions and their URLs. ExDoc will automatically match + the version of the package with the one in the array to mark as current. + """ + @spec generate(String.t(), String.t(), [Path.t()], Keyword.t()) :: + [%{entrypoint: String.t(), warned?: boolean(), formatter: module()}] + def generate(project, version, source_beams, options) + when is_binary(project) and is_binary(version) and is_list(source_beams) and + is_list(options) do + # Clear it up for tests + _ = unset_warned() + + retriever = Keyword.get(options, :retriever, ExDoc.Retriever) + extras_input = Keyword.get(options, :extras, []) + + # Build configs independently (build both upfront for validation) + retriever_config = ExDoc.Config.build(options) + formatter_config = ExDoc.Formatter.Config.build(project, version, options) + + # Retriever phase (run once for all formatters) + {modules, filtered} = retriever.docs_from_dir(source_beams, retriever_config) + extras = ExDoc.Extras.build(extras_input, retriever_config) + + for formatter <- formatter_config.formatters do + formatter = find_formatter(formatter) + entrypoint = ExDoc.Formatter.run(formatter, formatter_config, modules, filtered, extras) + %{entrypoint: entrypoint, warned?: unset_warned(), formatter: formatter} + end + end + + defp find_formatter(modname) when is_atom(modname), + do: modname + + defp find_formatter("ExDoc.Formatter." <> _ = name), + do: Module.concat([name]) + + defp find_formatter(name), + do: Module.concat([ExDoc.Formatter, String.upcase(name)]) +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/application.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/application.ex new file mode 100644 index 0000000..2b74f53 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/application.ex @@ -0,0 +1,27 @@ +defmodule ExDoc.Application do + @moduledoc false + use Application + + def start(_type, _args) do + Makeup.Registry.register_lexer(ExDoc.ShellLexer, + options: [], + names: ["shell", "console", "sh", "bash", "zsh"], + extensions: [] + ) + + # Load applications so we can find their modules in docs + Enum.each([:eex, :ex_unit, :iex, :logger, :mix], &Application.load/1) + + # Start all applications with the makeup prefix + for {app, _, _} <- Application.loaded_applications(), + match?("makeup_" <> _, Atom.to_string(app)) do + Application.ensure_all_started(app) + end + + children = [ + ExDoc.Refs + ] + + Supervisor.start_link(children, strategy: :one_for_one) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/autolink.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/autolink.ex new file mode 100644 index 0000000..9a1c4c0 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/autolink.ex @@ -0,0 +1,660 @@ +defmodule ExDoc.Autolink do + # Encapsulates all functionality related to autolinking, + # decoupled from language and ExDoc structs. + @moduledoc false + + # * `:apps` - the apps that the docs are being generated for. When linking modules they are + # checked if they are part of the app and based on that the links are relative or absolute. + # + # * `:current_module` - the module that the docs are being generated for. Used to link local + # calls and see if remote calls are in the same app. + # + # * `:current_kfa` - the kind, function, arity that the docs are being generated for. Is nil + # if there is no such thing. Used to generate more accurate warnings. + # + # * `:module_id` - id of the module being documented (e.g.: `"String"`) + # + # * `:file` - source file location + # + # * `:line` - line number of the beginning of the documentation + # + # * `:language` - the language call-back module to use + # + # * `:id` - a module/function/etc being documented (e.g.: `"String.upcase/2"`) + # + # * `:ext` - the extension (`".html"`, "`.xhtml"`, etc) + # + # * `:extras` - map of extras + # + # * `:skip_undefined_reference_warnings_on` - function that will be called with + # a module/function/file/etc and return a boolean whether to skip warning on it. + # + # * `:skip_code_autolink_to` - function that will be called with a term and return a boolean + # whether to skip autolinking to it. + # + # * `:filtered_modules` - A list of module nodes that were filtered by the retriever + # + # * `:warnings` - one of: + # + # * `:emit` (default) + # + # * `:raise` (useful for tests) + # + # * `:send` - send back to caller (useful for tests) + + alias ExDoc.Refs + + defstruct [ + :current_module, + :module_id, + :id, + :line, + :language, + file: "nofile", + apps: [], + extras: [], + deps: [], + ext: ".html", + current_kfa: nil, + siblings: [], + skip_undefined_reference_warnings_on: + &ExDoc.Formatter.Config.skip_undefined_reference_warnings_on/1, + skip_code_autolink_to: &ExDoc.Formatter.Config.skip_code_autolink_to/1, + force_module_prefix: nil, + filtered_modules: [], + assets: %{}, + warnings: :emit + ] + + @hexdocs "https://hexdocs.pm/" + @otpappdocs "https://www.erlang.org/doc/apps/" + + def app_module_url(tool, module, anchor \\ "#content", config) + + def app_module_url(:no_tool, _, _, _), do: nil + + def app_module_url(tool, module, anchor, config) do + base_url = + case tool do + :ex_doc -> @hexdocs + :otp -> @otpappdocs + end + + path = module |> inspect() |> String.trim_leading(":") + app_url(base_url, module, config, path, config.ext, "#{anchor}") + end + + defp string_app_module_url(tool, module, anchor, config) do + if Enum.any?(config.filtered_modules, &(&1.module == module)) do + warn(config, "reference to a filtered module") + nil + else + app_module_url(tool, module, anchor, config) + end + end + + @doc false + def ex_doc_app_url(module, config, path, ext, suffix) do + app_url(@hexdocs, module, config, path, ext, suffix) + end + + defp app_url(base_url, module, config, path, ext, suffix) do + if app = app(module) do + if app in config.apps do + path <> ext <> suffix + else + config.deps + |> Keyword.get_lazy(app, fn -> base_url <> "#{app}" end) + |> String.trim_trailing("/") + |> Kernel.<>("/" <> path <> ".html" <> suffix) + end + else + path <> ext <> suffix + end + end + + defp app(module) do + case :code.which(module) do + :preloaded -> + :erts + + maybe_path -> + case :application.get_application(module) do + {:ok, app} -> + app + + _ -> + with true <- is_list(maybe_path), + [_, "ebin", app, "lib" | _] <- maybe_path |> Path.split() |> Enum.reverse() do + String.split(app, "-") |> hd() |> String.to_atom() + else + _ -> nil + end + end + end + end + + @doc false + def tool(module, config) do + if match?("Elixir." <> _, Atom.to_string(module)) do + :ex_doc + else + app = app(module) + + if is_app_otp(app) and app not in config.apps and not Keyword.has_key?(config.deps, app) do + :otp + else + :ex_doc + end + end + end + + defp is_app_otp(app) do + maybe_lib_dir_path = :code.lib_dir(app) + is_list(maybe_lib_dir_path) and List.starts_with?(maybe_lib_dir_path, :code.root_dir()) + end + + def maybe_warn(config, ref, visibility, metadata) do + file = Path.relative_to_cwd(config.file) + + unless Enum.any?( + [config.id, config.module_id, file], + config.skip_undefined_reference_warnings_on + ) do + warn(config, ref, visibility, metadata) + end + end + + defp ref_regex, do: ~r/^`(.+)`$/ + + def custom_link(attrs, config) do + case Keyword.fetch(attrs, :href) do + {:ok, href} -> + case Regex.scan(ref_regex(), href) do + [[_, custom_link]] -> + custom_link + |> url(:custom_link, config) + |> remove_and_warn_if_invalid(custom_link, config) + + [] -> + build_extra_link(href, config) + end + + _ -> + nil + end + end + + def url(string = "mix help " <> name, mode, config) do + name |> mix_task(string, mode, config) |> maybe_remove_link(mode) + end + + def url(string = "mix " <> name, mode, config) do + name |> mix_task(string, mode, config) |> maybe_remove_link(mode) + end + + def url(string, mode, config) do + if config.skip_code_autolink_to.(string) do + nil + else + parse_url(string, mode, config) + end + end + + defp remove_and_warn_if_invalid(nil, reference, config) do + warn( + config, + ~s[documentation references "#{reference}" but it is invalid] + ) + + :remove_link + end + + defp remove_and_warn_if_invalid(result, _, _), do: result + + @builtin_ext [".livemd", ".cheatmd", ".md", ".txt", ""] + + defp build_extra_link(link, config) do + with %{scheme: nil, host: nil, path: path} = uri <- URI.parse(link), + true <- is_binary(path) and path != "" and not (path =~ ref_regex()), + true <- Path.extname(path) in @builtin_ext do + cond do + file = config.extras[Path.basename(path)] -> + append_fragment(file <> config.ext, uri.fragment) + + asset_file?(path, config.assets) -> + nil + + true -> + maybe_warn(config, nil, nil, %{file_path: path, original_text: link}) + nil + end + else + _ -> nil + end + end + + defp asset_file?(path, assets) do + Enum.any?(assets, fn {source_dir, target_dir} -> + prefix = String.trim_trailing(target_dir, "/") <> "/" + + if String.starts_with?(path, prefix) do + path + |> String.trim_leading(prefix) + |> Path.expand(source_dir) + |> File.exists?() + end + end) + end + + defp maybe_remove_link(nil, :custom_link) do + :remove_link + end + + defp maybe_remove_link(result, _mode) do + result + end + + defp mix_task(name, string, mode, config) do + {module, url, visibility} = + if name =~ ~r/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/ do + parts = name |> String.split(".") |> Enum.map(&Macro.camelize/1) + module = Module.concat([Mix, Tasks | parts]) + + {module, module_url(module, :regular_link, config, string), + Refs.get_visibility({:module, module})} + else + {nil, nil, :undefined} + end + + if url in [nil, :remove_link] and mode == :custom_link do + maybe_warn(config, {:module, module}, visibility, %{ + mix_task: true, + original_text: string + }) + end + + url + end + + defp module_url(module, anchor \\ nil, mode, config, string) do + ref = {:module, module} + + case {mode, Refs.get_visibility(ref)} do + {_link_type, visibility} when visibility in [:public, :limited] -> + string_app_module_url(tool(module, config), module, anchor, config) + + {:regular_link, :undefined} -> + nil + + {:custom_link, visibility} when visibility in [:hidden, :undefined] -> + maybe_warn(config, ref, visibility, %{original_text: string}) + :remove_link + + {_link_type, visibility} -> + maybe_warn(config, ref, visibility, %{original_text: string}) + nil + end + end + + defp extra_url(string, config) do + case String.split(string, ":", parts: 2) do + [app, extra] -> + {extra, anchor} = + case String.split(extra, "#", parts: 2) do + [extra] -> + {extra, ""} + + [extra, anchor] -> + {extra, "#" <> anchor} + end + + app = String.to_atom(app) + + config.deps + |> Keyword.get_lazy(app, fn -> + if Application.ensure_loaded(app) != :ok do + maybe_warn( + config, + "documentation references \"e:#{string}\" but #{app} cannot be found.", + nil, + %{} + ) + end + + prefix = + cond do + app in config.apps -> "" + is_app_otp(app) -> @otpappdocs + true -> @hexdocs + end + + prefix <> "#{app}" + end) + |> String.trim_trailing("/") + |> Kernel.<>("/" <> convert_extra_extension(extra, config) <> anchor) + + _ -> + nil + end + end + + defp convert_extra_extension(extra, config) do + if Path.extname(extra) in @builtin_ext do + Path.rootname(extra) <> config.ext + else + extra + end + end + + defp parse_url(string, mode, config) do + case Regex.run(~r{^(.+)/(\d+)(#.*)?$}, string) do + [_, left, right | maybe_fragment] -> + with {:ok, arity} <- parse_arity(right) do + {kind, rest} = kind(left) + + case config.language.parse_module_function(rest) do + {:local, function} -> + kind + |> local_url(function, arity, config, string, mode: mode) + |> maybe_append_nested_fragment(maybe_fragment) + |> maybe_remove_link(mode) + + {:remote, module, function} -> + {kind, module, function, arity} + |> remote_url(config, string, mode: mode) + |> maybe_append_nested_fragment(maybe_fragment) + |> maybe_remove_link(mode) + + :error -> + nil + end + else + _ -> + nil + end + + nil -> + case string do + "m:" <> rest -> + parse_module_with_anchor(rest, config) + + "e:" <> rest -> + extra_url(rest, config) + + string when mode == :custom_link -> + parse_module_with_anchor(string, config) + + string when not config.force_module_prefix -> + case config.language.parse_module(string, mode) do + {:module, module} -> + module_url(module, mode, config, string) + + :error -> + nil + end + + _ -> + nil + end + + _ -> + nil + end + end + + defp parse_module_with_anchor(string, config) do + destructure [rest, fragment], String.split(string, "#", parts: 2) + # TODO: rename :custom_link to :strict i.e. we expect ref to be valid + # force custom_link mode because of m: prefix. + case config.language.parse_module(rest, :custom_link) do + {:module, module} -> + module_url(module, fragment && "#" <> fragment, :custom_link, config, rest) + + :error -> + nil + end + end + + defp parse_arity(string) do + case Integer.parse(string) do + {arity, ""} -> {:ok, arity} + _ -> :error + end + end + + def kind("c:" <> rest), do: {:callback, rest} + def kind("t:" <> rest), do: {:type, rest} + ## \\ does not work for :custom_url as Earmark strips the \... + def kind("\\" <> rest), do: {:function, rest} + def kind(rest), do: {:function, rest} + + def local_url(kind, name, arity, config, original_text, options \\ []) + + def local_url(kind, name, arity, config, original_text, options) do + module = config.current_module + ref = {kind, module, name, arity} + mode = Keyword.get(options, :mode, :regular_link) + visibility = Refs.get_visibility(ref) + + case {kind, visibility} do + {_kind, :public} -> + fragment(kind, name, arity) + + {:function, _visibility} -> + case config.language.try_autoimported_function(name, arity, mode, config, original_text) do + nil -> + if mode == :custom_link do + maybe_warn(config, ref, visibility, %{original_text: original_text}) + end + + nil + + url -> + url + end + + {:type, _visibility} -> + case config.language.try_builtin_type(name, arity, mode, config, original_text) do + nil -> + if mode == :custom_link or config.language == ExDoc.Language.Erlang do + maybe_warn(config, ref, visibility, %{original_text: original_text}) + end + + nil + + url -> + url + end + + _ -> + maybe_warn(config, ref, visibility, %{original_text: original_text}) + nil + end + end + + def fragment(kind, nil, arity) do + fragment(kind, "nil", arity) + end + + def fragment(kind, name, arity) do + "#" <> prefix(kind) <> "#{encode_fragment_name(name)}/#{arity}" + end + + defp encode_fragment_name(name) when is_atom(name) do + encode_fragment_name(Atom.to_string(name)) + end + + defp encode_fragment_name(name) when is_binary(name) do + URI.encode(name) + end + + defp prefix(kind) + defp prefix(:function), do: "" + defp prefix(:callback), do: "c:" + defp prefix(:type), do: "t:" + + def remote_url({kind, module, name, arity} = ref, config, original_text, opts \\ []) do + warn? = Keyword.get(opts, :warn?, true) + mode = Keyword.get(opts, :mode, :regular_link) + same_module? = module == config.current_module + + case {mode, Refs.get_visibility({:module, module}), Refs.get_visibility(ref)} do + {_mode, _module_visibility, :public} -> + tool = tool(module, config) + + if same_module? do + fragment(kind, name, arity) + else + url = string_app_module_url(tool, module, nil, config) + url && url <> fragment(kind, name, arity) + end + + {:regular_link, module_visibility, :undefined} + when module_visibility == :public + when module_visibility == :limited and kind != :type -> + if warn? do + maybe_warn(config, ref, :undefined, %{original_text: original_text}) + end + + nil + + {:regular_link, _module_visibility, :undefined} + when not same_module? and + (config.language != ExDoc.Language.Erlang or kind == :function) -> + nil + + {:regular_link, :hidden, :hidden} + when not same_module? -> + if warn? do + maybe_warn(config, ref, :hidden, %{ + original_text: original_text, + module_visibility: :hidden, + same_module?: false + }) + end + + nil + + {_mode, _module_visibility, visibility} -> + if warn? do + maybe_warn(config, ref, visibility, %{original_text: original_text}) + end + + nil + end + end + + @doc false + def warn(config, message) do + f = + case config.current_kfa do + {:function, f, a} -> + [function: {f, a}] + + _ -> + [] + end + + stacktrace_info = [file: config.file, line: config.line, module: config.current_module] ++ f + + case config.warnings do + :emit -> + ExDoc.warn(message, stacktrace_info) + + :raise -> + ExDoc.warn(message, stacktrace_info) + raise "fail due to warnings" + + :send -> + send(self(), {:warn, message, file: config.file, line: config.line}) + end + end + + defp warn(config, ref, visibility, metadata) + + defp warn( + config, + {:module, _module}, + visibility, + %{mix_task: true, original_text: original_text} + ) do + message = + "documentation references \"#{original_text}\" but it is " <> + format_visibility(visibility, :module) + + warn(config, message) + end + + defp warn( + config, + {:module, _module}, + visibility, + %{original_text: original_text} + ) do + message = + "documentation references module \"#{original_text}\" but it is " <> + format_visibility(visibility, :module) + + warn(config, message) + end + + defp warn( + config, + nil, + _visibility, + %{file_path: _file_path, original_text: original_text} + ) do + message = "documentation references file \"#{original_text}\" but it does not exist" + + warn(config, message) + end + + defp warn( + config, + {:type, module, _name, _arity}, + :hidden, + %{original_text: original_text, module_visibility: :hidden, same_module?: false} + ) do + message = + "documentation references type \"#{original_text}\" but the module " <> + "#{inspect(module)} is #{format_visibility(:hidden, :module)}" + + warn(config, message) + end + + defp warn( + config, + {kind, _module, _name, _arity}, + visibility, + %{original_text: original_text} + ) do + message = + "documentation references #{kind} \"#{original_text}\" but it is " <> + format_visibility(visibility, kind) + + warn(config, message) + end + + defp warn(config, message, _, _) when is_binary(message) do + warn(config, message) + end + + # there is not such a thing as private callback or private module + def format_visibility(visibility, kind) when kind in [:module, :callback], do: "#{visibility}" + + # typep is defined as :hidden, since there is no :private visibility value + # but type defined with @doc false also is the stored the same way. + def format_visibility(:hidden, :type), do: "hidden or private" + + # for the rest, it can either be undefined or private + def format_visibility(:undefined, _kind), do: "undefined or private" + def format_visibility(visibility, _kind), do: "#{visibility}" + + defp maybe_append_nested_fragment(nil, _), do: nil + defp maybe_append_nested_fragment(url, []), do: url + defp maybe_append_nested_fragment(url, ["#" <> fragment]), do: url <> "-" <> fragment + + defp append_fragment(url, nil), do: url + defp append_fragment(url, fragment), do: url <> "#" <> fragment +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/cli.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/cli.ex new file mode 100644 index 0000000..66fa698 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/cli.ex @@ -0,0 +1,275 @@ +defmodule ExDoc.CLI do + @moduledoc false + + @doc """ + Handles the command line parsing for the escript. + """ + def main(args, generator \\ &ExDoc.generate/4) do + {:ok, _} = Application.ensure_all_started(:ex_doc) + + {opts, args} = + OptionParser.parse!(args, + aliases: [ + c: :config, + f: :formatter, + l: :logo, + m: :main, + o: :output, + q: :quiet, + u: :source_url, + v: :version + ], + switches: [ + canonical: :string, + config: :string, + favicon: :string, + footer: :boolean, + formatter: [:keep, :string], + homepage_url: :string, + language: :string, + logo: :string, + main: :string, + output: :string, + package: :string, + package: :string, + paths: [:keep, :string], + proglang: :string, + quiet: :boolean, + source_ref: :string, + source_url: :string, + version: :boolean, + warnings_as_errors: :boolean + ] + ) + + if List.keymember?(opts, :version, 0) do + IO.puts("ExDoc v#{ExDoc.version()}") + else + generate(args, opts, generator) + end + end + + defp generate(args, opts, generator) do + [project, version | source_beams] = parse_args(args) + Code.prepend_paths(source_beams) + + for path <- Keyword.get_values(opts, :paths), + path <- Path.wildcard(path) do + Code.prepend_path(path) + app(path) |> Application.load() + end + + # Start all applications with the makeup prefix + for {app, _, _} <- Application.loaded_applications(), + match?("makeup_" <> _, Atom.to_string(app)) do + Application.ensure_all_started(app) + end + + opts = + opts + |> Keyword.put(:apps, Enum.map(source_beams, &app/1)) + |> merge_config() + |> normalize_formatters() + + quiet? = Keyword.get(opts, :quiet, false) + generated_docs = generator.(project, version, source_beams, opts) + + unless quiet? do + Enum.each(generated_docs, fn %{entrypoint: entrypoint, formatter: formatter} -> + extension = formatter_module_to_extension(formatter) + IO.puts(IO.ANSI.format([:green, "View #{extension} docs at #{inspect(entrypoint)}"])) + end) + end + + warned = Enum.filter(generated_docs, & &1.warned?) + + if opts[:warnings_as_errors] == true and warned != [] do + formatters = Enum.map(warned, &formatter_module_to_extension(&1.formatter)) + + format_message = + case formatters do + [formatter] -> "#{formatter} format" + _ -> "#{Enum.join(formatters, ", ")} formats" + end + + message = + "Documents have been generated, but generation for #{format_message} failed " <> + "due to warnings while using the --warnings-as-errors option" + + message_formatted = IO.ANSI.format([:red, message, :reset]) + IO.puts(:stderr, message_formatted) + exit({:shutdown, 1}) + else + Enum.map(generated_docs, & &1.entrypoint) + end + end + + defp formatter_module_to_extension(module) do + module |> Module.split() |> List.last() |> String.downcase() + end + + defp normalize_formatters(opts) do + formatters = + case Keyword.get_values(opts, :formatter) do + [] -> opts[:formatters] || ["html", "markdown", "epub"] + values -> values + end + + opts + |> Keyword.delete(:formatter) + |> Keyword.put(:formatters, formatters) + end + + defp app(source_beam) do + case Path.wildcard(Path.join([source_beam, "*.app"])) do + [path] -> + path |> Path.basename(".app") |> String.to_atom() + + _ -> + raise "cannot find .app file in #{inspect(source_beam)}" + end + end + + defp merge_config(opts) do + case Keyword.fetch(opts, :config) do + {:ok, config} -> + opts_without_config = Keyword.delete(opts, :config) + Keyword.merge(read_config(config), opts_without_config) + + _ -> + opts + end + end + + defp read_config(path) do + case Path.extname(path) do + ".exs" -> + read_config_exs(path) + + ".config" -> + read_config_erl(path) + + other -> + raise "expected config to have .exs or .config extension, got: #{inspect(other)}" + end + end + + defp read_config_exs(path) do + config = File.read!(path) + {result, _} = Code.eval_string(config) + + unless is_list(result) do + raise "expected a keyword list from config file: #{inspect(path)}" + end + + result + end + + defp read_config_erl(path) do + case :file.consult(path) do + {:ok, config} -> + config + + {:error, reason} -> + raise "error parsing #{path}: #{inspect(reason)}" + end + end + + defp parse_args([_project, _version | _source_beams] = args), do: args + + defp parse_args(_) do + IO.puts("Too few arguments.\n") + print_usage() + exit({:shutdown, 1}) + end + + defp print_usage do + IO.puts(~S""" + Usage: + ex_doc PROJECT VERSION BEAMS [OPTIONS] + + Examples: + ex_doc "Ecto" "0.8.0" "_build/dev/lib/ecto/ebin" -u "https://github.com/elixir-ecto/ecto" + ex_doc "Project" "1.0.0" "_build/dev/lib/project/ebin" -c "docs.exs" + + Options: + PROJECT Project name + VERSION Version number + BEAMS Path to compiled beam files + --canonical Indicate the preferred URL with rel="canonical" link element + -c, --config Give configuration through a file instead of a command line. + See "Custom config" section below for more information. + --favicon Path to a favicon image for the project. Must be PNG, JPEG or SVG. The image + will be placed in the output "assets" directory. + -f, --formatter Docs formatter to use (html or epub), default: html and epub + --no-footer Do not render the footer (except for the required "Built with ExDoc" note) + --homepage-url URL to link to for the site name + --language Identify the primary language of the documents, its value must be + a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag, default: "en" + -l, --logo Path to a logo image for the project. Must be PNG, JPEG or SVG. The image will + be placed in the output "assets" directory. + -m, --main The entry-point page in docs, default: "api-reference" + -o, --output Path to output docs, default: "doc" + --package Hex package name + --paths Prepends the given path to Erlang code path. The path might contain a glob + pattern but in that case, remember to quote it: --paths "_build/dev/lib/*/ebin". + This option can be given multiple times. + --proglang The project's programming language, default: "elixir". + -q, --quiet Only output warnings and errors. + --source-ref Branch/commit/tag used for source link inference, default: "master". + -u, --source-url URL to the source code. + -v, --version Print ExDoc version. + --warnings-as-errors Exit with non-zero status if doc generation produces warnings. + + ## Custom config + + A custom config can be given with the --config option. + + The file must either have ".exs" or ".config" extension. + + The file with the ".exs" extension must be an Elixir script that returns + a keyword list with the same options declares in `ExDoc.generate/4`. + Here is an example: + + [ + extras: Path.wildcard("lib/elixir/pages/*.md"), + groups_for_docs: [ + Guards: & &1[:guard] == true + ], + skip_undefined_reference_warnings_on: ["compatibility-and-deprecations"], + groups_for_modules: [ + ... + ] + ] + + The file with the ".config" extension must contain Erlang terms separated by ".". + See `:file.consult/1` for more information. Here is an example: + + {extras, [<<"README.md">>, <<"CHANGELOG.md">>]}. + {main, <<"readme">>}. + {proglang, erlang}. + + ## Source linking + + ExDoc by default provides links to the source code implementation as + long as --source-url or --source-url-pattern is provided. If you + provide --source-url, ExDoc will inflect the url pattern automatically + for GitHub, GitLab, and Bitbucket URLs. For example: + + --source-url "https://github.com/elixir-ecto/ecto" + + Will be inflected as: + + https://github.com/elixir-ecto/ecto/blob/master/%{path}#L%{line} + + To specify a particular branch or commit, use the `--source-ref` option: + + --source-url "https://github.com/elixir-ecto/ecto" --source-ref "v1.0" + + will result in the following URL pattern: + + https://github.com/elixir-ecto/ecto/blob/v1.0/%{path}#L%{line} + + """) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/config.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/config.ex new file mode 100644 index 0000000..b968b44 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/config.ex @@ -0,0 +1,269 @@ +defmodule ExDoc.Config do + # Config used by retriever and extras + @moduledoc false + + # Defaults + @default_source_ref "main" + def default_group_for_doc(metadata), do: metadata[:group] + def filter_modules(_module, _metadata), do: true + def annotations_for_docs(_), do: [] + def source_url_pattern(_, _), do: nil + + defstruct filter_modules: &__MODULE__.filter_modules/2, + groups_for_modules: [], + group_for_doc: &__MODULE__.default_group_for_doc/1, + annotations_for_docs: &__MODULE__.annotations_for_docs/1, + docs_groups: [], + source_ref: @default_source_ref, + groups_for_extras: [], + source_url_pattern: &__MODULE__.source_url_pattern/2, + nest_modules_by_prefix: [], + proglang: :elixir, + markdown_processor: {ExDoc.Markdown.Earmark, []} + + @type t :: %__MODULE__{ + filter_modules: (module, map -> boolean), + groups_for_modules: [{binary(), term()}], + group_for_doc: (keyword() -> String.t() | nil), + annotations_for_docs: (map() -> list()), + docs_groups: [String.t()], + source_ref: nil | String.t(), + groups_for_extras: [{binary(), term()}], + source_url_pattern: (String.t(), integer() -> String.t() | nil), + nest_modules_by_prefix: [String.t()], + proglang: :elixir | :erlang, + markdown_processor: {module(), keyword()} | module() + } + + def build(options) do + proglang = Keyword.get(options, :proglang, :elixir) + filter_modules = Keyword.get(options, :filter_modules, &filter_modules/2) + nest_modules_by_prefix = Keyword.get(options, :nest_modules_by_prefix, []) + + options = + if groups_for_functions = options[:groups_for_functions] do + IO.warn(":groups_for_functions is deprecated, please use :groups_for_docs instead") + Keyword.put_new(options, :groups_for_docs, groups_for_functions) + else + options + end + + apps = Keyword.get(options, :apps, []) + + groups_for_docs = Keyword.get(options, :groups_for_docs, []) + groups_for_extras = Keyword.get(options, :groups_for_extras, []) + + groups_for_modules = + Keyword.get(options, :groups_for_modules, default_groups_for_modules(apps)) + + default_group_for_doc = + Keyword.get(options, :default_group_for_doc, &default_group_for_doc/1) + + source_url_pattern = + Keyword.get_lazy(options, :source_url_pattern, fn -> + guess_url(options[:source_url], options[:source_ref] || @default_source_ref) + end) + + preconfig = %__MODULE__{ + filter_modules: normalize_filter_modules(filter_modules), + docs_groups: for({group, _} <- groups_for_docs, do: to_string(group)), + group_for_doc: normalize_groups_for_docs(groups_for_docs, default_group_for_doc), + groups_for_extras: normalize_groups(groups_for_extras), + groups_for_modules: + normalize_groups( + # TODO: The default module groups must be returned by the language + groups_for_modules ++ + [Deprecated: &deprecated?/1, Exceptions: &exception?/1] + ), + nest_modules_by_prefix: normalize_nest_modules_by_prefix(nest_modules_by_prefix), + proglang: normalize_proglang(proglang), + source_url_pattern: normalize_source_url_pattern(source_url_pattern) + } + + retriever_options = + Keyword.take(options, [ + :annotations_for_docs, + :markdown_processor, + :source_ref + ]) + + struct!(preconfig, retriever_options) + end + + # Helper functions + + defp normalize_proglang(binary) when is_binary(binary) do + binary |> String.to_atom() |> normalize_proglang() + end + + defp normalize_proglang(proglang) when proglang in [:elixir, :erlang] do + proglang + end + + defp normalize_proglang(proglang) do + raise ArgumentError, "#{inspect(proglang)} is not supported" + end + + defp normalize_filter_modules(string) when is_binary(string), + do: normalize_filter_modules(Regex.compile!(string)) + + defp normalize_filter_modules(%Regex{} = regex), + do: fn module, _ -> Atom.to_string(module) =~ regex end + + defp normalize_filter_modules(fun) when is_function(fun, 2), + do: fun + + defp normalize_groups_for_docs(groups, default) do + groups = normalize_groups(groups) + + fn metadata -> + Enum.find_value(groups, fn {group, function} -> + function.(metadata) && group + end) || default.(metadata) + end + end + + defp normalize_groups(groups) do + for {k, v} <- groups, do: {to_string(k), v} + end + + defp normalize_nest_modules_by_prefix(nest_modules_by_prefix) do + nest_modules_by_prefix + |> Enum.map(&inspect_atoms/1) + |> Enum.sort() + |> Enum.reverse() + end + + defp inspect_atoms(atom) when is_atom(atom), do: inspect(atom) + defp inspect_atoms(binary) when is_binary(binary), do: binary + + defp deprecated?(metadata), do: metadata[:deprecated] != nil + defp exception?(metadata), do: metadata[:kind] == :exception + + defp normalize_source_url_pattern(function) when is_function(function, 2), do: function + defp normalize_source_url_pattern(nil), do: &source_url_pattern/2 + + defp normalize_source_url_pattern(binary) when is_binary(binary) do + case :binary.split(binary, "%{path}") do + [left, right] -> + case :binary.split(left, "%{line}") do + [line_left, line_right] -> + fn path, line -> + line_left <> Integer.to_string(line) <> line_right <> path <> right + end + + [_] -> + case :binary.split(right, "%{line}") do + [line_left, line_right] -> + fn path, line -> + left <> path <> line_left <> Integer.to_string(line) <> line_right + end + + [_] -> + fn path, _ -> left <> path <> right end + end + end + + [_] -> + case :binary.split(binary, "%{line}") do + [left, right] -> + fn _, line -> left <> Integer.to_string(line) <> right end + + [_] -> + fn _, _ -> binary end + end + end + end + + defp normalize_source_url_pattern(other) do + raise ArgumentError, + ":source_url_pattern must be a string, a two-arity function or nil, got: #{inspect(other)}" + end + + defp guess_url(url, ref) do + with {:ok, host_with_path} <- http_or_https(url), + {:ok, pattern} <- known_pattern(host_with_path, ref) do + "https://" <> append_slash(host_with_path) <> pattern + else + _ -> url + end + end + + defp http_or_https("http://" <> rest), + do: {:ok, rest} + + defp http_or_https("https://" <> rest), + do: {:ok, rest} + + defp http_or_https(_), + do: :error + + defp known_pattern("github.com/" <> _, ref), + do: {:ok, "blob/#{ref}/%{path}#L%{line}"} + + defp known_pattern("gitlab.com/" <> _, ref), + do: {:ok, "blob/#{ref}/%{path}#L%{line}"} + + defp known_pattern("bitbucket.org/" <> _, ref), + do: {:ok, "src/#{ref}/%{path}#cl-%{line}"} + + defp known_pattern(_host_with_path, _ref), + do: :error + + defp append_slash(url) do + if :binary.last(url) == ?/, do: url, else: url <> "/" + end + + defp default_groups_for_modules([_app]) do + [] + end + + defp default_groups_for_modules(apps) do + Enum.map(apps, fn app -> + Application.load(app) + {app, Application.spec(app, :modules)} + end) + end + + # Group matching helpers + + @doc """ + Finds the index of a given group. + """ + def index(groups, group) do + Enum.find_index(groups, fn {k, _v} -> k == group end) || -1 + end + + @doc """ + Finds a matching group for the given module name, id, and metadata. + """ + def match_module(group_patterns, module, id, metadata) do + match_group_patterns(group_patterns, fn pattern -> + case pattern do + %Regex{} = regex -> Regex.match?(regex, id) + string when is_binary(string) -> id == string + atom when is_atom(atom) -> atom == module + function when is_function(function) -> function.(metadata) + end + end) + end + + @doc """ + Finds a matching group for the given filename or url. + """ + def match_extra(group_patterns, path) do + match_group_patterns(group_patterns, fn pattern -> + case pattern do + %Regex{} = regex -> Regex.match?(regex, path) + string when is_binary(string) -> path == string + end + end) + end + + defp match_group_patterns(group_patterns, matcher) do + Enum.find_value(group_patterns, fn {group, patterns} -> + patterns = List.wrap(patterns) + Enum.any?(patterns, matcher) && group + end) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/doc_ast.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/doc_ast.ex new file mode 100644 index 0000000..c7f7ece --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/doc_ast.ex @@ -0,0 +1,336 @@ +defmodule ExDoc.DocAST do + # General helpers for dealing with the documentation AST + # (which is the Markdown -> HTML AST). + @moduledoc false + + @type t :: term() + + alias ExDoc.Markdown + + @doc """ + Parses given `doc_content` according to `doc_format`. + """ + def parse!(doc_content, doc_format, options \\ []) + + def parse!(markdown, "text/markdown", opts) do + parse_markdown(markdown, opts) + end + + def parse!(ast, "application/erlang+html", _options) do + parse_erl_ast(ast) + end + + def parse!(_ast, other, _opts) do + raise "content type #{inspect(other)} is not supported" + end + + # https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-element + @void_elements ~W(area base br col command embed hr img input keygen link + meta param source track wbr)a + + @doc """ + Transform AST into HTML string. + """ + def to_html(binary) do + IO.iodata_to_binary(to_iodata(binary)) + end + + defp to_iodata(binary) when is_binary(binary) do + ExDoc.Utils.h(binary) + end + + defp to_iodata(list) when is_list(list) do + Enum.map(list, &to_iodata/1) + end + + defp to_iodata({:comment, _attrs, inner, _meta}) do + [""] + end + + defp to_iodata({tag, attrs, _inner, _meta}) when tag in @void_elements do + "<#{tag}#{ast_attributes_to_string(attrs)}/>" + end + + defp to_iodata({tag, attrs, inner, %{verbatim: true}}) do + ["<#{tag}#{ast_attributes_to_string(attrs)}>", inner, ""] + end + + defp to_iodata({tag, attrs, inner, _meta}) do + ["<#{tag}#{ast_attributes_to_string(attrs)}>", to_iodata(inner), ""] + end + + defp ast_attributes_to_string(attrs) do + Enum.map(attrs, fn {key, val} -> " #{key}=\"#{ExDoc.Utils.h(val)}\"" end) + end + + ## parse markdown + + defp parse_markdown(markdown, opts) do + Markdown.to_ast(markdown, opts) + end + + ## parse erlang+html + + defp parse_erl_ast(binary) when is_binary(binary) do + binary + end + + defp parse_erl_ast(list) when is_list(list) do + Enum.map(list, &parse_erl_ast/1) + end + + defp parse_erl_ast({:pre, attrs, content}) do + case content do + # if we already have
...
, carry on + [{:code, _, _}] -> + {:pre, attrs, parse_erl_ast(content), %{}} + + # otherwise, turn
...
into
...
+ _ -> + content = [{:code, [], parse_erl_ast(content), %{}}] + {:pre, attrs, content, %{}} + end + end + + defp parse_erl_ast({tag, attrs, content}) when is_atom(tag) do + {tag, attrs, parse_erl_ast(content), %{}} + end + + @doc """ + Extracts leading title element from the given AST. + + If found, the title element is stripped from the resulting AST. + """ + def extract_title(ast) + + def extract_title([{:comment, _, _, _} | ast]), do: extract_title(ast) + def extract_title([{:h1, _attrs, inner, _meta} | ast]), do: {:ok, inner, ast} + def extract_title(_ast), do: :error + + @doc """ + Extracts the headers which have anchors (aka ids) in them. + """ + def extract_headers_with_ids(ast, headers) do + ast + |> reduce_tags([], fn {tag, attrs, inner, _}, acc -> + with true <- tag in headers, + id = Keyword.get(attrs, :id, ""), + text = ExDoc.DocAST.text(inner), + true <- id != "" and text != "" do + [{tag, text, id} | acc] + else + _ -> acc + end + end) + |> Enum.reverse() + end + + @doc """ + Adds an id attribute to the given headers. + + A prefix for the id attribute can be given, + which is automatically URL encoded to avoid + issues. + """ + def add_ids_to_headers(doc_ast, headers, prefix \\ "") do + prefix = URI.encode(prefix) + + doc_ast + |> map_reduce_tags(%{}, fn {tag, attrs, inner, meta} = ast, seen -> + if tag in headers and not Keyword.has_key?(attrs, :id) do + possible_id = inner |> text() |> ExDoc.Utils.text_to_id() + id_count = Map.get(seen, possible_id, 0) + partial_id = if id_count >= 1, do: "#{possible_id}-#{id_count}", else: possible_id + seen = Map.put(seen, possible_id, id_count + 1) + {{tag, [id: prefix <> partial_id] ++ attrs, inner, meta}, seen} + else + {ast, seen} + end + end) + |> elem(0) + end + + @doc """ + Compute a synopsis from a document by looking at its first paragraph. + """ + def synopsis({:p, _attrs, [_ | _] = inner, meta}) do + inner = + case Enum.split(inner, -1) do + {pre, [post]} when is_binary(post) -> + pre ++ [String.trim_trailing(post, ":")] + + _ -> + inner + end + + to_html({:p, [], remove_ids(inner), meta}) + end + + def synopsis([{:comment, _, _, _} | rest]), do: synopsis(rest) + def synopsis([head | _]), do: synopsis(head) + def synopsis(_other), do: "" + + defp remove_ids(ast) do + map_tags(ast, fn {tag, attrs, inner, meta} -> + {tag, Keyword.delete(attrs, :id), inner, meta} + end) + end + + @doc """ + Returns text content from the given AST. + """ + def text(ast, joiner \\ "") do + ast + |> do_text(joiner) + |> IO.iodata_to_binary() + |> String.trim() + end + + defp do_text(ast, joiner) when is_list(ast), + do: Enum.map_intersperse(ast, joiner, &do_text(&1, joiner)) + + defp do_text(ast, _joiner) when is_binary(ast), + do: ast + + defp do_text({_tag, _attr, ast, _meta}, joiner), + do: do_text(ast, joiner) + + @doc """ + Wraps a list of HTML nodes into `
` tags whenever `headers` returns true. + """ + def sectionize(list, headers), do: sectionize(list, headers, []) + + defp sectionize(list, headers, acc) do + case pivot(list, acc, headers) do + {acc, {header_tag, header_attrs, _, _} = header, rest} -> + {inner, rest} = Enum.split_while(rest, ¬_tag?(&1, header_tag)) + class = String.trim_trailing("#{header_tag} #{header_attrs[:class]}") + section = {:section, [class: class], [header | sectionize(inner, headers, [])], %{}} + sectionize(rest, headers, [section | acc]) + + acc -> + acc + end + end + + defp not_tag?({tag, _, _, _}, tag), do: false + defp not_tag?(_, _tag), do: true + + defp pivot([{tag, _, _, _} = head | tail], acc, headers) do + if tag in headers do + {acc, head, tail} + else + pivot(tail, [head | acc], headers) + end + end + + defp pivot([head | tail], acc, headers), do: pivot(tail, [head | acc], headers) + defp pivot([], acc, _headers), do: Enum.reverse(acc) + + @doc """ + Highlights the code blocks in the AST. + """ + def highlight(ast, language, opts \\ []) do + highlight_info = language.highlight_info() + + map_tags(ast, fn + {:pre, pre_attrs, [{:code, code_attrs, [code], code_meta}], pre_meta} = ast + when is_binary(code) -> + {lang, code_attrs} = Keyword.pop(code_attrs, :class, "") + + case pick_language_and_lexer(lang, highlight_info, code) do + {_lang, nil, _lexer_opts} -> + ast + + {lang, lexer, lexer_opts} -> + try do + Makeup.highlight_inner_html(code, + lexer: lexer, + lexer_options: lexer_opts, + formatter_options: opts + ) + rescue + exception -> + ExDoc.warn( + [ + "crashed while highlighting #{lang} snippet:\n\n", + ExDoc.DocAST.to_html(ast), + "\n\n", + Exception.format_banner(:error, exception, __STACKTRACE__) + ], + __STACKTRACE__ + ) + + ast + else + highlighted -> + code_attrs = [class: "makeup #{lang}", translate: "no"] ++ code_attrs + code_meta = Map.put(code_meta, :verbatim, true) + {:pre, pre_attrs, [{:code, code_attrs, [highlighted], code_meta}], pre_meta} + end + end + + ast -> + ast + end) + end + + defp pick_language_and_lexer("", _highlight_info, "$ " <> _) do + {"shell", ExDoc.ShellLexer, []} + end + + defp pick_language_and_lexer("output", highlight_info, _code) do + {"output", highlight_info.lexer, highlight_info.opts} + end + + defp pick_language_and_lexer("", highlight_info, _code) do + {highlight_info.language_name, highlight_info.lexer, highlight_info.opts} + end + + defp pick_language_and_lexer(lang, _highlight_info, _code) do + case Makeup.Registry.fetch_lexer_by_name(lang) do + {:ok, {lexer, opts}} -> {lang, lexer, opts} + :error -> {lang, nil, []} + end + end + + ## Traversal helpers + + @doc """ + Maps the tags in the AST, first mapping children tags, then the tag itself. + """ + def map_tags({tag, attrs, inner, meta}, fun), + do: fun.({tag, attrs, Enum.map(inner, &map_tags(&1, fun)), meta}) + + def map_tags(list, fun) when is_list(list), + do: Enum.map(list, &map_tags(&1, fun)) + + def map_tags(other, _fun), + do: other + + @doc """ + Reduces the tags in the AST, first reducing children tags, then the tag itself. + """ + def reduce_tags({tag, attrs, inner, meta}, acc, fun), + do: fun.({tag, attrs, inner, meta}, Enum.reduce(inner, acc, &reduce_tags(&1, &2, fun))) + + def reduce_tags(list, acc, fun) when is_list(list), + do: Enum.reduce(list, acc, &reduce_tags(&1, &2, fun)) + + def reduce_tags(_other, acc, _fun), + do: acc + + @doc """ + Map-reduces the tags in the AST, first mapping children tags, then the tag itself. + """ + def map_reduce_tags({tag, attrs, inner, meta}, acc, fun) do + {inner, acc} = Enum.map_reduce(inner, acc, &map_reduce_tags(&1, &2, fun)) + fun.({tag, attrs, inner, meta}, acc) + end + + def map_reduce_tags(list, acc, fun) when is_list(list), + do: Enum.map_reduce(list, acc, &map_reduce_tags(&1, &2, fun)) + + def map_reduce_tags(other, acc, _fun), + do: {other, acc} +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/extras.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/extras.ex new file mode 100644 index 0000000..fdf1228 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/extras.ex @@ -0,0 +1,210 @@ +defmodule ExDoc.Extras do + @moduledoc false + + alias ExDoc.{Config, Markdown, Utils} + + @reserved_filenames ~w(404.html search.html) + + @doc """ + Build a list of `ExDoc.ExtraNode` and `ExDoc.URLNode`. + """ + def build(extras_input, config) do + groups = config.groups_for_extras + + extras = + extras_input + |> Enum.map(&build_extra(&1, groups, config)) + + validate_no_reserved_filenames!(extras) + validate_no_duplicate_extras!(extras) + + ids_count = Enum.reduce(extras, %{}, &Map.update(&2, &1.id, 1, fn c -> c + 1 end)) + + extras + |> Enum.map_reduce(1, fn extra, idx -> + if ids_count[extra.id] > 1, do: {disambiguate_id(extra, idx), idx + 1}, else: {extra, idx} + end) + |> elem(0) + |> Enum.sort_by(fn extra -> Config.index(groups, extra.group) end) + end + + defp validate_no_reserved_filenames!(extras) do + conflicts = + extras + |> Enum.filter(&match?(%ExDoc.ExtraNode{}, &1)) + |> Enum.filter(fn extra -> "#{extra.id}.html" in @reserved_filenames end) + + if conflicts != [] do + entries = + Enum.map_join(conflicts, ", ", fn extra -> + "#{inspect(extra.source_path)} would generate #{extra.id}.html" + end) + + raise ArgumentError, + "extra(s) would conflict with built-in ExDoc page(s): #{entries}; " <> + "rename your extra file(s) or use the :filename option to set different output filename(s)" + end + end + + # Detects duplicate ExtraNode entries by checking for collisions on the + # {source_path, id} pair. + # - URLNodes are excluded since they don't produce output files. + # - Two nodes are duplicates when they share both source_path and ID — meaning + # the same file would generate the same output page. + # - Nodes with different source paths or different IDs (e.g. via the + # :filename option) are not duplicates. + # - Nodes from different source files that happen to share an ID are handled + # by disambiguate_id/2 instead. + defp validate_no_duplicate_extras!(extras) do + duplicates = + extras + |> Enum.filter(&match?(%ExDoc.ExtraNode{}, &1)) + |> Enum.frequencies_by(fn extra -> {extra.source_path, extra.id} end) + |> Enum.filter(fn {_, count} -> count > 1 end) + + if duplicates != [] do + entries = Enum.map_join(duplicates, ", ", fn {{source, _}, _} -> inspect(source) end) + + raise ArgumentError, + "duplicate extras: #{entries}" + end + end + + defp disambiguate_id(extra, discriminator) do + Map.put(extra, :id, "#{extra.id}-#{discriminator}") + end + + defp build_extra(input, groups, config) when is_binary(input) do + build_extra({input, %{}}, groups, config) + end + + defp build_extra({input, opts}, groups, config) when is_list(opts) do + build_extra({input, Map.new(opts)}, groups, config) + end + + defp build_extra({input, %{url: _} = input_options}, groups, _config) do + input = to_string(input) + title = validate_extra_string!(input_options, :title) || input + url = validate_extra_string!(input_options, :url) + group = Config.match_extra(groups, url) + + %ExDoc.URLNode{ + group: group, + id: Utils.text_to_id(title), + title: title, + url: url + } + end + + defp build_extra({input, input_options}, groups, config) do + input = to_string(input) + + id = + validate_extra_string!(input_options, :filename) || + input |> filename_to_title() |> Utils.text_to_id() + + source_file = validate_extra_string!(input_options, :source) || input + opts = [file: source_file, line: 1, markdown_processor: config.markdown_processor] + + {extension, source, ast} = + case extension_name(input) do + extension when extension in ["", ".txt"] -> + source = File.read!(input) + ast = [{:pre, [], ["\n" <> source], %{}}] + {extension, source, ast} + + extension when extension in [".md", ".livemd", ".cheatmd"] -> + source = File.read!(input) + + ast = + source + |> Markdown.to_ast(opts) + |> ExDoc.DocAST.add_ids_to_headers([:h2, :h3]) + + {extension, source, ast} + + _ -> + raise ArgumentError, + "file extension not recognized, allowed extension is either .cheatmd, .livemd, .md, .txt or no extension" + end + + {title_doc, title_text, ast} = + case ExDoc.DocAST.extract_title(ast) do + {:ok, title_doc, ast} -> {title_doc, ExDoc.DocAST.text(title_doc), ast} + :error -> {nil, nil, ast} + end + + title = + validate_extra_string!(input_options, :title) || title_text || filename_to_title(input) + + group = Config.match_extra(groups, input) + source_path = source_file |> Path.relative_to(File.cwd!()) |> String.replace_leading("./", "") + source_url = config.source_url_pattern.(source_path, 1) + search_data = validate_search_data!(input_options[:search_data]) + + %ExDoc.ExtraNode{ + type: extra_type(extension), + source_doc: source, + group: group, + id: id, + doc: ast, + source_path: source_path, + source_url: source_url, + search_data: search_data, + title: title, + title_doc: title_doc || title + } + end + + defp validate_extra_string!(input_options, key) do + case input_options[key] do + nil -> + nil + + binary when is_binary(binary) -> + binary + + other -> + raise ArgumentError, + "extra field #{inspect(key)} must be a string, got: #{inspect(other)}" + end + end + + @search_data_keys [:anchor, :body, :title, :type] + + defp validate_search_data!(nil), do: nil + + defp validate_search_data!(search_data) when is_list(search_data) do + Enum.each(search_data, fn search_data -> + has_keys = Map.keys(search_data) + + if Enum.sort(has_keys) != @search_data_keys do + raise ArgumentError, + "expected search data to be a list of maps with the keys: #{inspect(@search_data_keys)}, " <> + "found keys: #{inspect(has_keys)}" + end + end) + + search_data + end + + defp validate_search_data!(search_data) do + raise ArgumentError, + "expected search data to be a list of maps with the keys: #{inspect(@search_data_keys)}, " <> + "found: #{inspect(search_data)}" + end + + defp extension_name(input) do + input + |> Path.extname() + |> String.downcase() + end + + defp filename_to_title(input) do + input |> Path.basename() |> Path.rootname() + end + + defp extra_type(".cheatmd"), do: :cheatmd + defp extra_type(".livemd"), do: :livemd + defp extra_type(_), do: :extra +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter.ex new file mode 100644 index 0000000..f8e2dca --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter.ex @@ -0,0 +1,324 @@ +defmodule ExDoc.Formatter do + @moduledoc """ + Specifies the custom formatter API. + """ + + @doc """ + The callback that must be implemented by formatters. + + It receives the configuration, a list of nodes, + and it must return the documentation entrypoint + plus a list of files built inside the output folder. + """ + @callback run( + ExDoc.Formatter.Config.t(), + [ExDoc.ModuleNode.t()], + [ExDoc.ExtraNode.t() | ExDoc.URLNode.t()] + ) :: %{entrypoint: String.t(), build: [String.t()]} + + @doc """ + A list of options to configure autolinking behaviour. + """ + @callback autolink_options() :: [highlight_tag: String.t(), extension: String.t()] + + @optional_callbacks autolink_options: 0 + + @doc false + def run(formatter, formatter_config, module_nodes, filtered_nodes, extras) do + if not Code.ensure_loaded?(formatter) do + raise "formatter module #{inspect(formatter)} not found" + end + + build_file = build_file_path(formatter, formatter_config) + cleanup_build_file(build_file, formatter_config) + + {module_nodes, extras} = + if function_exported?(formatter, :autolink_options, 0) do + autolink_opts = formatter.autolink_options() + autolink(formatter_config, module_nodes, filtered_nodes, extras, autolink_opts) + else + {module_nodes, extras} + end + + %{entrypoint: entrypoint, build: build} = + formatter.run(formatter_config, module_nodes, extras) + + write_build_file(build_file, build) + entrypoint + end + + defp build_file_path(formatter, config) do + extension = + case formatter do + # For backwards compatibility + ExDoc.Formatter.HTML -> + "" + + _ -> + formatter + |> Module.split() + |> List.last() + |> String.downcase() + |> then(&".#{&1}") + end + + Path.join(config.output, ".build#{extension}") + end + + defp cleanup_build_file(build_file, config) do + if File.exists?(build_file) do + build_file + |> File.read!() + |> String.split("\n", trim: true) + |> Enum.map(&Path.join(config.output, &1)) + |> Enum.each(&File.rm/1) + end + end + + defp write_build_file(_build_file, []) do + :ok + end + + defp write_build_file(build_file, files) do + entries = + files + |> Enum.uniq() + |> Enum.sort() + |> Enum.map(&[&1, "\n"]) + + File.mkdir_p!(Path.dirname(build_file)) + File.write!(build_file, entries) + end + + ## Assets/image helpers + + @doc """ + Copy `assets` to the given `output` folder. + """ + def copy_assets(assets, output) do + Enum.flat_map(assets, fn {dir_or_files, relative_target_dir} -> + target_dir = Path.join(output, relative_target_dir) + File.mkdir_p!(target_dir) + + cond do + is_list(dir_or_files) -> + Enum.map(dir_or_files, fn {name, content} -> + target = Path.join(target_dir, name) + File.write(target, content) + Path.relative_to(target, output) + end) + + is_binary(dir_or_files) and File.dir?(dir_or_files) -> + dir_or_files + |> File.cp_r!(target_dir, dereference_symlinks: true) + |> Enum.reduce([], fn path, acc -> + # Omit directories in .build file + if File.dir?(path) do + acc + else + [Path.relative_to(path, output) | acc] + end + end) + |> Enum.reverse() + + is_binary(dir_or_files) -> + [] + + true -> + raise ":assets must be a map of source directories to target directories" + end + end) + end + + @doc """ + Copies the logo to the given location in the output directory. + """ + def copy_logo(%{logo: nil}, _target) do + [] + end + + def copy_logo(%{output: output, logo: logo}, target) do + copy_image(output, logo, target) + end + + @doc """ + Copies the favicon to the given location in the output directory. + """ + def copy_favicon(%{favicon: nil}, _target) do + [] + end + + def copy_favicon(%{output: output, favicon: favicon}, target) do + copy_image(output, favicon, target) + end + + @doc """ + Copies the cover to the given location in the output directory. + """ + def copy_cover(%{cover: nil}, _target) do + [] + end + + def copy_cover(%{output: output, cover: cover}, target) do + copy_image(output, cover, target) + end + + defp copy_image(output, source, target) do + extname = source |> Path.extname() |> String.downcase() + + if extname in ~w(.png .jpg .jpeg .svg) do + filename = "#{target}#{extname}" + target = Path.join(output, filename) + File.mkdir_p!(Path.dirname(target)) + File.copy!(source, target) + [filename] + else + raise ArgumentError, "image format not recognized, allowed formats are: .png, .jpg, .svg" + end + end + + ## Autolinking + + @doc false + def autolink(config, nodes, filtered_nodes, extras, opts) do + {ext, highlight_opts} = Keyword.pop!(opts, :extension) + + language = + case config.proglang do + :erlang -> ExDoc.Language.Erlang + _ -> ExDoc.Language.Elixir + end + + base_config = %ExDoc.Autolink{ + apps: config.apps, + deps: config.deps, + ext: ext, + extras: extra_paths(extras), + skip_undefined_reference_warnings_on: config.skip_undefined_reference_warnings_on, + skip_code_autolink_to: config.skip_code_autolink_to, + filtered_modules: filtered_nodes, + assets: config.assets + } + + extras = + extras + |> Task.async_stream( + &autolink_extra(&1, language, base_config, highlight_opts), + timeout: :infinity + ) + |> Enum.map(fn {:ok, res} -> res end) + + # Render project nodes with autolinked extras + nodes = + nodes + |> Task.async_stream( + &autolink_node(&1, base_config, highlight_opts), + timeout: :infinity + ) + |> Enum.map(&elem(&1, 1)) + + {nodes, extras} + end + + defp autolink_node(node, base_config, highlight_opts) do + language = node.language + + autolink_config = %{ + base_config + | current_module: node.module, + module_id: node.id, + language: language + } + + docs_groups = + for group <- node.docs_groups do + docs = + for child_node <- group.docs do + child_config = %{ + autolink_config + | id: id(node, child_node), + line: child_node.doc_line, + file: child_node.doc_file, + current_kfa: {child_node.type, child_node.name, child_node.arity} + } + + specs = + Enum.map(child_node.source_specs, &language.autolink_spec(&1, child_config)) + + child_node = %{child_node | specs: specs} + autolink_doc(child_node, language, child_config, highlight_opts) + end + + %{autolink_doc(group, language, autolink_config, highlight_opts) | docs: docs} + end + + module_config = %{ + autolink_config + | id: node.id, + file: node.moduledoc_file, + line: node.moduledoc_line + } + + %{ + autolink_doc(node, language, module_config, highlight_opts) + | docs_groups: docs_groups + } + end + + defp autolink_doc(%{doc: nil} = node, _language, _autolink_opts, _opts), + do: node + + defp autolink_doc(%{doc: doc} = node, language, autolink_opts, opts) do + doc = autolink_and_highlight(doc, language, autolink_opts, opts) + %{node | doc: doc} + end + + defp autolink_extra(%ExDoc.URLNode{} = extra, _language, _autolink_opts, _opts), + do: extra + + defp autolink_extra(%ExDoc.ExtraNode{doc: nil} = extra, _language, _autolink_opts, _opts), + do: extra + + defp autolink_extra( + %ExDoc.ExtraNode{doc: doc, source_path: source_path, id: id} = extra, + language, + base_config, + opts + ) do + extra_config = %{base_config | file: source_path, id: id, language: language} + doc = autolink_and_highlight(doc, language, extra_config, opts) + %{extra | doc: doc} + end + + defp id(%{id: mod_id}, %{id: "c:" <> id}) do + "c:" <> mod_id <> "." <> id + end + + defp id(%{id: mod_id}, %{id: "t:" <> id}) do + "t:" <> mod_id <> "." <> id + end + + defp id(%{id: mod_id}, %{id: id}) do + mod_id <> "." <> id + end + + defp autolink_and_highlight(doc, language, autolink_opts, opts) do + doc + |> language.autolink_doc(autolink_opts) + |> ExDoc.DocAST.highlight(language, opts) + end + + defp extra_paths(extras) do + Enum.reduce(extras, %{}, fn + %ExDoc.URLNode{}, acc -> + acc + + %ExDoc.ExtraNode{source_path: source_path, id: id}, acc when is_binary(source_path) -> + base = Path.basename(source_path) + Map.put(acc, base, id) + + _extra, acc -> + acc + end) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/config.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/config.ex new file mode 100644 index 0000000..284ad1c --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/config.ex @@ -0,0 +1,276 @@ +defmodule ExDoc.Formatter.Config do + @moduledoc """ + Configuration used by formatters. + + Contains project metadata, output settings, and various + customization options for generating documentation. + """ + + @doc false + def before_closing_head_tag(_), do: "" + @doc false + def before_closing_footer_tag(_), do: "" + @doc false + def before_closing_body_tag(_), do: "" + @doc false + def skip_undefined_reference_warnings_on(_string), do: false + @doc false + def skip_code_autolink_to(_string), do: false + + defstruct output: "./doc", + project: nil, + version: nil, + main: "api-reference", + api_reference: true, + canonical: nil, + redirects: %{}, + assets: %{}, + logo: nil, + cover: nil, + favicon: nil, + extra_section: "Pages", + footer: true, + formatters: [], + search: [%{name: "Default", help: "In-browser search", url: "search.html?q="}], + homepage_url: nil, + source_url: nil, + language: "en", + proglang: :elixir, + authors: nil, + package: nil, + title: nil, + description: nil, + nest_modules_by_prefix: [], + before_closing_head_tag: &__MODULE__.before_closing_head_tag/1, + before_closing_body_tag: &__MODULE__.before_closing_body_tag/1, + before_closing_footer_tag: &__MODULE__.before_closing_footer_tag/1, + # Autolink fields + apps: [], + deps: [], + skip_undefined_reference_warnings_on: + &__MODULE__.skip_undefined_reference_warnings_on/1, + skip_code_autolink_to: &__MODULE__.skip_code_autolink_to/1 + + @type t :: %__MODULE__{ + output: Path.t(), + project: nil | String.t(), + version: nil | String.t(), + main: nil | String.t(), + api_reference: boolean(), + canonical: nil | String.t(), + redirects: %{optional(String.t()) => String.t()} | [{String.t(), String.t()}], + assets: %{binary() => binary()}, + logo: nil | Path.t(), + cover: nil | Path.t(), + favicon: nil | Path.t(), + extra_section: String.t(), + footer: boolean(), + formatters: [String.t()], + search: [%{name: String.t(), help: String.t(), url: String.t()}], + homepage_url: nil | String.t(), + source_url: nil | String.t(), + language: String.t(), + authors: nil | [String.t()], + package: :atom | nil, + title: nil | String.t(), + description: nil | String.t(), + nest_modules_by_prefix: [String.t()], + before_closing_head_tag: (atom() -> String.t()) | mfa() | map(), + before_closing_body_tag: (atom() -> String.t()) | mfa() | map(), + before_closing_footer_tag: (atom() -> String.t()) | mfa() | map(), + apps: [atom()], + deps: [{ebin_path :: String.t(), doc_url :: String.t()}], + skip_undefined_reference_warnings_on: (String.t() -> boolean), + skip_code_autolink_to: (String.t() -> boolean), + proglang: :elixir | :erlang + } + + @doc false + def build(project, version, options) do + output = Keyword.get(options, :output, "./doc") + nest_modules_by_prefix = Keyword.get(options, :nest_modules_by_prefix, []) + proglang = Keyword.get(options, :proglang, :elixir) + + skip_undefined_reference_warnings_on = + Keyword.get( + options, + :skip_undefined_reference_warnings_on, + &skip_undefined_reference_warnings_on/1 + ) + + skip_code_autolink_to = + Keyword.get(options, :skip_code_autolink_to, &skip_code_autolink_to/1) + + search = Keyword.get(options, :search, []) + + before_closing_head_tag = + Keyword.get(options, :before_closing_head_tag, &before_closing_head_tag/1) + + before_closing_body_tag = + Keyword.get(options, :before_closing_body_tag, &before_closing_body_tag/1) + + before_closing_footer_tag = + Keyword.get(options, :before_closing_footer_tag, &before_closing_footer_tag/1) + + preconfig = %__MODULE__{ + nest_modules_by_prefix: normalize_nest_modules_by_prefix(nest_modules_by_prefix), + output: normalize_output(output), + proglang: normalize_proglang(proglang), + project: project, + search: normalize_search(search), + skip_undefined_reference_warnings_on: + normalize_skip_list_function(skip_undefined_reference_warnings_on), + skip_code_autolink_to: normalize_skip_list_function(skip_code_autolink_to), + before_closing_head_tag: normalize_callback(before_closing_head_tag), + before_closing_body_tag: normalize_callback(before_closing_body_tag), + before_closing_footer_tag: normalize_callback(before_closing_footer_tag), + version: version + } + + formatter_options = + Keyword.take(options, [ + :main, + :api_reference, + :canonical, + :redirects, + :assets, + :logo, + :cover, + :favicon, + :extra_section, + :footer, + :formatters, + :homepage_url, + :source_url, + :language, + :authors, + :package, + :title, + :description, + :apps, + :deps + ]) + + config = struct!(preconfig, formatter_options) + + if not is_map(config.assets) do + raise ArgumentError, + ":assets configuration in ExDoc expects a map from %{source => target}, got: #{config.assets}" + end + + config + end + + # Helper functions + + defp normalize_output(output) do + Path.expand(String.trim_trailing(output, "/")) + end + + defp normalize_proglang(binary) when is_binary(binary) do + binary |> String.to_atom() |> normalize_proglang() + end + + defp normalize_proglang(proglang) when proglang in [:elixir, :erlang] do + proglang + end + + defp normalize_proglang(proglang) do + raise ArgumentError, "#{inspect(proglang)} is not supported" + end + + defp normalize_nest_modules_by_prefix(nest_modules_by_prefix) do + nest_modules_by_prefix + |> Enum.map(&inspect_atoms/1) + |> Enum.sort() + |> Enum.reverse() + end + + defp inspect_atoms(atom) when is_atom(atom), do: inspect(atom) + defp inspect_atoms(binary) when is_binary(binary), do: binary + + defp normalize_skip_list_function(strings) when is_list(strings), + do: &(&1 in strings) + + defp normalize_skip_list_function(fun) when is_function(fun, 1), + do: fun + + defp normalize_callback({m, f, a}) do + fn module -> apply(m, f, [module | a]) end + end + + defp normalize_callback(callback) when is_map(callback) do + fn module -> Map.get(callback, module, "") end + end + + defp normalize_callback(callback) when is_function(callback, 1) do + callback + end + + defp normalize_search([]) do + [%{name: "Default", help: "In-browser search", url: "search.html?q="}] + end + + defp normalize_search(search) when is_list(search) do + Enum.map(search, fn + %{packages: _, url: _} -> + raise ArgumentError, "search must provide either :url or :packages, but not both" + + %{url: url} = engine when not is_binary(url) -> + bad_search!(engine) + + %{name: name, help: help} = engine + when is_binary(name) and is_binary(help) -> + engine + |> Map.delete(:packages) + |> Map.put_new_lazy(:url, fn -> + if packages = engine[:packages] do + "https://hexdocs.pm/?packages=#{normalize_package_search(packages)}&q=" + else + "search.html?q=" + end + end) + + other -> + bad_search!(other) + end) + end + + defp normalize_search(other) do + raise ArgumentError, "search must be a list of maps, got: #{inspect(other)}" + end + + defp normalize_package_search([]) do + raise ArgumentError, ":packages requires a non-empty list" + end + + defp normalize_package_search(packages) do + packages + |> Enum.map_join(",", fn + package when is_atom(package) -> + "#{package}:latest" + + {package, version} when is_atom(package) and is_binary(version) -> + "#{package}:#{version}" + + other -> + raise ArgumentError, + "entries in :packages must be either a package name or a package-version tuple, got: #{inspect(other)}" + end) + |> URI.encode_www_form() + end + + defp bad_search!(other) do + raise ArgumentError, + """ + search entries must be a map with: + + * required :name as string + * required :help as string + * optional :url as a string + * optional :packages as a non-empty list of packages and versions + + got: #{inspect(other)} + """ + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub.ex new file mode 100644 index 0000000..64cafbf --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub.ex @@ -0,0 +1,165 @@ +defmodule ExDoc.Formatter.EPUB do + @moduledoc false + + @mimetype "application/epub+zip" + @assets_dir "OEBPS/assets" + alias __MODULE__.{Assets, Templates} + alias ExDoc.Formatter + + @doc false + def autolink_options do + [extension: ".xhtml", highlight_tag: "samp"] + end + + @doc """ + Generates EPUB documentation for the given modules. + """ + def run(config, project_nodes, extras) when is_map(config) do + config = normalize_config(config) + File.rm_rf!(config.output) + File.mkdir_p!(Path.join(config.output, "OEBPS")) + + static_files = + Formatter.copy_assets(config.assets, Path.join(config.output, "OEBPS")) ++ + Formatter.copy_assets(additional_assets(config), config.output) + + Formatter.copy_logo(config, Path.join(@assets_dir, "logo")) + Formatter.copy_cover(config, Path.join(@assets_dir, "cover")) + + {modules, tasks} = Enum.split_with(project_nodes, &(&1.type != :task)) + generate_content(config, modules, tasks, extras, static_files) + generate_nav(config, modules, tasks, extras) + generate_title(config) + generate_extras(config, extras) + generate_modules(config, modules) + generate_modules(config, tasks) + + {:ok, epub} = generate_epub(config.output) + File.rm_rf!(config.output) + entrypoint = Path.relative_to_cwd(epub) + %{entrypoint: entrypoint, build: [entrypoint]} + end + + defp normalize_config(config) do + output = Path.join(config.output, "#{config.project}") + %{config | output: output} + end + + defp generate_extras(config, extras) do + for %ExDoc.ExtraNode{} = node <- extras do + output = "#{config.output}/OEBPS/#{node.id}.xhtml" + html = Templates.extra_template(config, node) + + if File.regular?(output) do + ExDoc.warn("file #{Path.relative_to_cwd(output)} already exists", []) + end + + File.write!(output, html) + end + end + + defp generate_content(config, modules, tasks, extras, static_files) do + uuid = "urn:uuid:#{uuid4()}" + datetime = format_datetime() + + static_files = + for name <- static_files, + String.contains?(name, "OEBPS"), + media_type = Templates.media_type(Path.extname(name)), + do: {Path.relative_to(name, "OEBPS"), media_type} + + content = + Templates.content_template(config, modules, tasks, extras, uuid, datetime, static_files) + + File.write!("#{config.output}/OEBPS/content.opf", content) + end + + defp generate_nav(config, modules, tasks, extras) do + modules = group_by_group(modules) + tasks = group_by_group(tasks) + extras = group_by_group(extras) + + content = Templates.nav_template(config, modules, tasks, extras) + File.write!("#{config.output}/OEBPS/nav.xhtml", content) + end + + defp group_by_group(nodes) do + nodes + |> Enum.chunk_by(& &1.group) + |> Enum.map(&{hd(&1).group, &1}) + end + + defp generate_title(config) do + content = Templates.title_template(config) + File.write!("#{config.output}/OEBPS/title.xhtml", content) + end + + defp generate_modules(config, nodes) do + nodes + |> Task.async_stream( + fn module_node -> + content = Templates.module_template(config, module_node) + File.write!("#{config.output}/OEBPS/#{module_node.id}.xhtml", content) + end, + timeout: :infinity + ) + |> Enum.map(fn {:ok, result} -> result end) + end + + defp generate_epub(output) do + :zip.create( + String.to_charlist("#{output}.epub"), + [{~c"mimetype", @mimetype} | files_to_add(output)], + compress: ~w[.css .xhtml .html .ncx .js .opf .jpg .png .xml]c + ) + end + + ## Helpers + + defp additional_assets(config) do + [ + {Assets.dist(config.proglang), "OEBPS/dist"}, + {Assets.metainfo(), "META-INF"} + ] + end + + defp files_to_add(path) do + Enum.reduce(Path.wildcard(Path.join(path, "**/*")), [], fn file, acc -> + case File.read(file) do + {:ok, bin} -> + [{file |> Path.relative_to(path) |> String.to_charlist(), bin} | acc] + + {:error, _} -> + acc + end + end) + end + + # Helper to format Erlang datetime tuple + defp format_datetime do + {{year, month, day}, {hour, min, sec}} = :calendar.universal_time() + list = [year, month, day, hour, min, sec] + + "~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ" + |> :io_lib.format(list) + |> IO.iodata_to_binary() + end + + @two_power_16 65536 + @two_power_32 4_294_967_296 + @two_power_48 281_474_976_710_656 + + defp uuid4 do + Enum.map_join( + [ + <<:rand.uniform(@two_power_32) - 1::32>>, + <<:rand.uniform(@two_power_16) - 1::16>>, + <<:rand.uniform(@two_power_16) - 1::16>>, + <<:rand.uniform(@two_power_16) - 1::16>>, + <<:rand.uniform(@two_power_48) - 1::48>> + ], + <<45>>, + &Base.encode16(&1, case: :lower) + ) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/assets.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/assets.ex new file mode 100644 index 0000000..88bf4b2 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/assets.ex @@ -0,0 +1,31 @@ +defmodule ExDoc.Formatter.EPUB.Assets do + @moduledoc false + + defmacrop embed_pattern(pattern) do + ["formatters/epub", pattern] + |> Path.join() + |> Path.wildcard() + |> Enum.map(fn path -> + Module.put_attribute(__CALLER__.module, :external_resource, path) + {Path.basename(path), File.read!(path)} + end) + end + + defp dist_js(), do: embed_pattern("dist/*.js") + defp dist_css(:elixir), do: embed_pattern("dist/epub-elixir-*.css") + defp dist_css(:erlang), do: embed_pattern("dist/epub-erlang-*.css") + + ## Assets + + def dist(proglang), do: dist_js() ++ dist_css(proglang) + def metainfo, do: embed_pattern("metainfo/*") + + ## Filenames + + def js_filename(), do: dist_js() |> extract_filename!() + def css_filename(language), do: dist_css(language) |> extract_filename!() + + ## Helpers + + defp extract_filename!([{location, _}]), do: location +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates.ex new file mode 100644 index 0000000..2bea6d4 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates.ex @@ -0,0 +1,125 @@ +defmodule ExDoc.Formatter.EPUB.Templates do + @moduledoc false + + require EEx + + import ExDoc.Utils, only: [h: 1, text_to_id: 1] + + alias ExDoc.Formatter.HTML.Templates, as: H + alias ExDoc.Formatter.EPUB.Assets + + # The actual rendering happens here + defp render_doc(ast), do: ast && ExDoc.DocAST.to_html(ast) + + @doc """ + Generated ID for static file. + """ + def static_file_to_id(static_file) do + static_file |> Path.basename() |> text_to_id() + end + + @doc """ + Creates the Package Document Definition. + + this definition encapsulates the publication metadata and the resource + information that constitute the EPUB publication. This definition also + includes the default reading order. + + See http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-package-def. + """ + EEx.function_from_file( + :def, + :content_template, + Path.expand("templates/content_template.eex", __DIR__), + [:config, :modules, :tasks, :extras, :uuid, :datetime, :static_files], + trim: true + ) + + @doc """ + Creates a chapter which contains all the details about an individual module. + + This chapter can include the following sections: *functions*, *types*, *callbacks*. + """ + EEx.function_from_file( + :def, + :module_template, + Path.expand("templates/module_template.eex", __DIR__), + [:config, :module], + trim: true + ) + + @doc """ + Creates the table of contents. + + This template follows the EPUB Navigation Document Definition. + + See http://www.idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-nav. + """ + EEx.function_from_file( + :def, + :nav_template, + Path.expand("templates/nav_template.eex", __DIR__), + [:config, :modules, :tasks, :extras], + trim: true + ) + + @doc """ + Creates a new chapter when the user provides additional files. + """ + EEx.function_from_file( + :def, + :extra_template, + Path.expand("templates/extra_template.eex", __DIR__), + [:config, :node], + trim: true + ) + + @doc """ + Creates the cover page for the EPUB document. + """ + EEx.function_from_file( + :def, + :title_template, + Path.expand("templates/title_template.eex", __DIR__), + [:config], + trim: true + ) + + EEx.function_from_file( + :defp, + :head_template, + Path.expand("templates/head_template.eex", __DIR__), + [:config, :title], + trim: true + ) + + EEx.function_from_file( + :defp, + :nav_grouped_item_template, + Path.expand("templates/nav_grouped_item_template.eex", __DIR__), + [:nodes], + trim: true + ) + + EEx.function_from_file( + :defp, + :toc_item_template, + Path.expand("templates/toc_item_template.eex", __DIR__), + [:nodes], + trim: true + ) + + "templates/media-types.txt" + |> Path.expand(__DIR__) + |> File.read!() + |> String.split("\n", trim: true) + |> Enum.each(fn line -> + [extension, media] = String.split(line, ",") + + def media_type("." <> unquote(extension)) do + unquote(media) + end + end) + + def media_type(_arg), do: nil +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/content_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/content_template.eex new file mode 100644 index 0000000..eecaf0d --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/content_template.eex @@ -0,0 +1,46 @@ + + + + <%= config.project %> - <%= config.version %> + <%= uuid %> + <%= config.language %> + <%= for {author, index} <- Enum.with_index(config.authors || [], 1) do %> + <%= author %> + <% end %> + <%= datetime %> + <%= if config.cover do %> + + <% end %> + + + + + <%= for extra <- extras do %> + + <% end %> + <%= for node <- modules ++ tasks do %> + + <% end %> + <%= for {static_file, media_type} <- static_files do %> + + <% end %> + <%= if config.cover do %> + + <% end %> + <%= if config.logo do %> + + + + + <%= for extra <- extras do %> + + <% end %> + <%= for node <- modules ++ tasks do %> + + <% end %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/extra_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/extra_template.eex new file mode 100644 index 0000000..ccffe69 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/extra_template.eex @@ -0,0 +1,6 @@ +<%= head_template(config, node.title) %> +

<%= ExDoc.DocAST.to_html(node.title_doc) %>

+ <%= render_doc(node.doc) %> + <%= config.before_closing_body_tag.(:epub) %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/head_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/head_template.eex new file mode 100644 index 0000000..5f60b6b --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/head_template.eex @@ -0,0 +1,12 @@ + + + + + <%= title %> - <%= config.project %> v<%= config.version %> + + + + <%= config.before_closing_head_tag.(:epub) %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/media-types.txt b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/media-types.txt new file mode 100644 index 0000000..0896b10 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/media-types.txt @@ -0,0 +1,19 @@ +gif,image/gif +jpg,image/jpeg +jpeg,image/jpeg +png,image/png +svg,image/svg+xml +xhtml,application/xhtml+xml +html,application/xhtml+xml +ncx,application/x-dtbncx+xml +otf,application/vnd.ms-opentype +ttf,application/vnd.ms-opentype +ttc,application/vnd.ms-opentype +eot,application/vnd.ms-opentype +woff,application/font-woff +opf,application/oebps-package+xml +mp3,audio/mpeg +mp4,video/mp4 +css,text/css +js,text/javascript +license,text/plain diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/module_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/module_template.eex new file mode 100644 index 0000000..cf8b1cc --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/module_template.eex @@ -0,0 +1,40 @@ +<%= head_template(config, module.title) %> +

+ <%= H.breakable_module_title(module.title) %> <%= H.module_type(module) %> +

+ + <%= if deprecated = module.deprecated do %> +
+ This <%= module.type %> is deprecated. <%=h deprecated %>. +
+ <% end %> + + <%= if doc = module.doc do %> +
+ <%= render_doc(doc) %> +
+ <% end %> + + <%= if module.docs_groups != [] do %> +
+

Summary

+ <%= for group <- module.docs_groups, do: H.summary_template(group.title, group.docs) %> +
+ <% end %> + + <%= for group <- module.docs_groups, key = text_to_id(group.title) do %> +
+

<%=h to_string(group.title) %>

+ <%= if doc = group.doc do %> +
+ <%= render_doc(doc) %> +
+ <% end %> +
+ <%= for node <- group.docs, do: H.detail_template(node, module) %> +
+
+ <% end %> + <%= config.before_closing_body_tag.(:epub) %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex new file mode 100644 index 0000000..67b1d03 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex @@ -0,0 +1,13 @@ +<%= for {title, nodes} <- nodes do %> + <%= if title do %> +
  • <%=h to_string(title) %> +
      + <% end %> + <%= for node <- nodes do %> +
    1. <%=h node.title %>
    2. + <% end %> + <%= if title do %> +
    +
  • + <% end %> +<% end %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_template.eex new file mode 100644 index 0000000..87d0d34 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_template.eex @@ -0,0 +1,24 @@ +<%= head_template(config, "Table Of Contents") %> +

    Table of contents

    + + <%= config.before_closing_body_tag.(:epub) %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/title_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/title_template.eex new file mode 100644 index 0000000..1c22350 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/title_template.eex @@ -0,0 +1,15 @@ +<%= head_template(config, "Cover") %> +
    + <%= if cover = config.cover do %> +
    + <% else %> +

    <%= config.project %>

    +

    v<%= config.version %>

    + <%= if logo = config.logo do %> +
    Logo
    + <% end %> + <% end %> +
    + <%= config.before_closing_body_tag.(:epub) %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/toc_item_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/toc_item_template.eex new file mode 100644 index 0000000..efba3c6 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/toc_item_template.eex @@ -0,0 +1,8 @@ +<%= for node <- nodes do %> + + + <%=URI.encode node.id %> + + + +<% end %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html.ex new file mode 100644 index 0000000..1413062 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html.ex @@ -0,0 +1,239 @@ +defmodule ExDoc.Formatter.HTML do + @moduledoc false + + alias __MODULE__.{Assets, Templates, SearchData} + alias ExDoc.Formatter + + @assets_dir "assets" + + def autolink_options do + [extension: ".html"] + end + + def run(config, project_nodes, extras) when is_map(config) do + if config.main == "index" do + raise ArgumentError, + ~S("main" cannot be set to "index", otherwise it will recursively link to itself) + end + + File.mkdir_p!(config.output) + + static_files = + Formatter.copy_assets(config.assets, config.output) ++ + Formatter.copy_assets(additional_assets(config), config.output) + + search_data = generate_search_data(config, project_nodes, extras) + + {modules, tasks} = Enum.split_with(project_nodes, &(&1.type != :task)) + + all_files = + search_data ++ + static_files ++ + generate_sidebar_items(config, modules, tasks, extras) ++ + generate_api_reference(config, modules, tasks) ++ + generate_extras(config, extras) ++ + Formatter.copy_favicon(config, Path.join(@assets_dir, "favicon")) ++ + Formatter.copy_logo(config, Path.join(@assets_dir, "logo")) ++ + generate_search(config) ++ + generate_not_found(config) ++ + generate_list(config, modules) ++ + generate_list(config, tasks) ++ + generate_redirects(config, ".html") + + entrypoint = config.output |> Path.join("index.html") |> Path.relative_to_cwd() + %{entrypoint: entrypoint, build: all_files} + end + + defp generate_not_found(config) do + filename = "404.html" + config = set_canonical_url(config, filename) + content = Templates.not_found_template(config) + write!(config, filename, content) + [filename] + end + + defp generate_search(config) do + filename = "search.html" + config = set_canonical_url(config, filename) + content = Templates.search_template(config) + write!(config, filename, content) + [filename] + end + + defp generate_sidebar_items(config, modules, tasks, extras) do + content = Templates.create_sidebar_items(config, modules, tasks, extras) + path = "dist/sidebar_items-#{digest(content)}.js" + write!(config, path, content) + [path] + end + + defp generate_search_data(config, linked, extras) do + content = SearchData.create(linked, extras, config.proglang) + path = "dist/search_data-#{digest(content)}.js" + write!(config, path, content) + [path] + end + + defp digest(content) do + content + |> :erlang.md5() + |> Base.encode16(case: :upper) + |> binary_part(0, 8) + end + + defp generate_extras(config, extras) do + generated_extras = + extras + |> Enum.reject(&is_map_key(&1, :url)) + |> with_prev_next() + |> Enum.map(fn {node, prev, next} -> + filename = "#{node.id}.html" + output = Path.join(config.output, filename) + config = set_canonical_url(config, filename) + + refs = %{ + prev: prev && %{path: "#{prev.id}.html", title: prev.title}, + next: next && %{path: "#{next.id}.html", title: next.title} + } + + html = Templates.extra_template(config, node, refs) + + if File.regular?(output) do + ExDoc.warn("file #{Path.relative_to_cwd(output)} already exists", []) + end + + File.write!(output, html) + filename + end) + + generated_extras ++ copy_extras(config, extras) + end + + defp copy_extras(config, extras) do + for %{source_path: source_path, id: id} when source_path != nil <- extras, + ext = extension_name(source_path), + ext == ".livemd" do + output = Path.join(config.output, "#{id}#{ext}") + + File.copy!(source_path, output) + + output + end + end + + defp with_prev_next([]), do: [] + + defp with_prev_next([head | tail]) do + Enum.zip([[head | tail], [nil, head | tail], tail ++ [nil]]) + end + + defp additional_assets(config) do + [ + {Assets.dist(config.proglang), "dist"}, + {Assets.fonts(), "dist"} + ] + end + + defp generate_api_reference(%{api_reference: false}, _modules, _tasks) do + [] + end + + defp generate_api_reference(config, modules, tasks) do + filename = "api-reference.html" + output = Path.join(config.output, filename) + config = set_canonical_url(config, filename) + + html = Templates.api_reference_template(config, modules, tasks) + + if File.regular?(output) do + ExDoc.warn("file #{Path.relative_to_cwd(output)} already exists", []) + end + + File.write!(output, html) + [filename] + end + + defp generate_redirects(config, ext) do + config.redirects + |> Map.new() + |> Map.put_new("index", config.main) + |> Enum.map(fn {from, to} -> + unless is_binary(from), + do: raise("expected a string for the source of a redirect, got: #{inspect(from)}") + + unless is_binary(to), + do: raise("expected a string for the destination of a redirect, got: #{inspect(to)}") + + source = from <> ext + + destination = + case String.split(to, "#", parts: 2) do + [to, anchor] -> to <> ext <> "#" <> anchor + _ -> to <> ext + end + + generate_redirect(config, source, destination) + + source + end) + end + + defp extension_name(input) do + input + |> Path.extname() + |> String.downcase() + end + + defp generate_redirect(config, filename, redirect_to) do + without_anchor = redirect_to |> String.split("#") |> hd() + + unless config.output |> Path.join(without_anchor) |> case_sensitive_file_regular?() do + ExDoc.warn("#{filename} redirects to #{redirect_to}, which does not exist", []) + end + + content = Templates.redirect_template(config, redirect_to) + write!(config, filename, content) + end + + defp case_sensitive_file_regular?(path) do + if File.regular?(path) do + files = path |> Path.dirname() |> File.ls!() + Path.basename(path) in files + else + false + end + end + + defp generate_list(config, nodes) do + nodes + |> Task.async_stream(&generate_module_page(config, &1), timeout: :infinity) + |> Enum.map(&elem(&1, 1)) + end + + defp generate_module_page(config, module_node) do + filename = "#{module_node.id}.html" + config = set_canonical_url(config, filename) + content = Templates.module_template(config, module_node) + write!(config, filename, content) + filename + end + + defp set_canonical_url(config, filename) do + if config.canonical do + canonical_url = + config.canonical + |> String.trim_trailing("/") + |> Kernel.<>("/" <> filename) + + Map.put(config, :canonical, canonical_url) + else + config + end + end + + defp write!(config, filename, content) do + config.output + |> Path.join(filename) + |> File.write!(content) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/assets.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/assets.ex new file mode 100644 index 0000000..2026b5e --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/assets.ex @@ -0,0 +1,55 @@ +defmodule ExDoc.Formatter.HTML.Assets do + @moduledoc false + + defmacrop embed_pattern(pattern) do + ["formatters/html", pattern] + |> Path.join() + |> Path.wildcard() + |> Enum.map(fn path -> + Module.put_attribute(__CALLER__.module, :external_resource, path) + {Path.basename(path), File.read!(path)} + end) + end + + defp dist_js(), do: embed_pattern("dist/html-*.js") + defp dist_inline_js(), do: embed_pattern("dist/inline_html-*.js") + defp dist_css(:elixir), do: embed_pattern("dist/html-elixir-*.css") + defp dist_css(:erlang), do: embed_pattern("dist/html-erlang-*.css") + defp dist_license(), do: embed_pattern("dist/*.LICENSE.txt") + + ## Assets + + def dist(proglang), do: dist_js() ++ dist_css(proglang) ++ dist_license() + def fonts, do: embed_pattern("dist/*.woff2") + + ## Sources + + def inline_js_source(), do: dist_inline_js() |> extract_source!() + + ## Filenames + + def js_filename(), do: dist_js() |> extract_filename!() + def css_filename(language), do: dist_css(language) |> extract_filename!() + + ## Helpers + + @doc """ + Some assets are generated automatically, so we find the revision at runtime. + """ + def rev(output, pattern) do + output = Path.expand(output) + + matches = + output + |> Path.join(pattern) + |> Path.wildcard() + + case matches do + [] -> raise("could not find matching #{output}/#{pattern}") + [asset | _] -> Path.relative_to(asset, output) + end + end + + defp extract_filename!([{location, _}]), do: location + defp extract_source!([{_, source}]), do: source +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/search_data.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/search_data.ex new file mode 100644 index 0000000..1d519b0 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/search_data.ex @@ -0,0 +1,122 @@ +defmodule ExDoc.Formatter.HTML.SearchData do + @moduledoc false + + def create(nodes, extras, proglang) do + items = Enum.flat_map(nodes, &module/1) ++ Enum.flat_map(extras, &extra/1) + + data = %{ + items: items, + content_type: "text/markdown", + proglang: proglang, + producer: %{ + name: "ex_doc", + version: to_string(Application.spec(:ex_doc)[:vsn]) + } + } + + ["searchData=" | ExDoc.Utils.to_json(data)] + end + + defp extra(%ExDoc.URLNode{}), do: [] + + defp extra(%ExDoc.ExtraNode{search_data: search_data} = map) when is_list(search_data) do + Enum.map(search_data, fn item -> + link = + if item.anchor === "" do + "#{URI.encode(map.id)}.html" + else + "#{URI.encode(map.id)}.html##{URI.encode(item.anchor)}" + end + + encode(link, item.title <> " - #{map.id}", item.type, clean_markdown(item.body)) + end) + end + + defp extra(%ExDoc.ExtraNode{} = map) do + page = URI.encode(map.id) <> ".html" + {intro, sections} = extract_sections_from_markdown(map.source_doc, "") + + intro = encode(page, map.title, :extras, intro) + [intro | render_sections(sections, page, map.title, :extras)] + end + + defp module(%ExDoc.ModuleNode{} = node) do + page = URI.encode(node.id) <> ".html" + {intro, sections} = extract_sections(node.source_format, node, "module-") + module = encode(page, node.title, node.type, intro) + + docs = + node.docs_groups + |> Enum.flat_map(& &1.docs) + |> Enum.flat_map(&node_child(&1, node, page)) + + [module] ++ render_sections(sections, page, node.title, node.type) ++ docs + end + + defp node_child(node, module_node, page) do + title = "#{module_node.id}.#{node.name}/#{node.arity}" + {intro, sections} = extract_sections(module_node.source_format, node, node.id <> "-") + + child = encode("#{page}##{URI.encode(node.id)}", title, node.type, intro) + [child | render_sections(sections, page, title, node.type)] + end + + defp encode(ref, title, type, doc) do + %{ref: ref, title: title, type: type, doc: doc} + end + + defp extract_sections("text/markdown", %{source_doc: %{"en" => doc}}, prefix) do + extract_sections_from_markdown(doc, prefix) + end + + defp extract_sections(_format, %{doc: nil}, _prefix) do + {"", []} + end + + defp extract_sections(_format, %{doc: doc}, _prefix) do + {ExDoc.DocAST.text(doc, " "), []} + end + + defp extract_sections_from_markdown(string, prefix) do + [intro | headers_sections] = + Regex.split(~r/(?\b.+)/, string, include_captures: true) + + sections = + headers_sections + |> Enum.chunk_every(2) + |> Enum.map(fn [header, section] -> + # Hardcoded markdown processor because we use it only for header matching + opts = [markdown_processor: ExDoc.Markdown.Earmark] + [{tag, attrs, content, meta}] = ExDoc.Markdown.to_ast(header, opts) + {tag, [section: section] ++ attrs, content, meta} + end) + |> ExDoc.DocAST.add_ids_to_headers([:h2, :h3], prefix) + |> Enum.map(fn {_, attrs, inner, _} -> + id = Keyword.fetch!(attrs, :id) + section = Keyword.fetch!(attrs, :section) + {ExDoc.DocAST.text(inner), id, clean_markdown(section)} + end) + + {clean_markdown(intro), sections} + end + + defp clean_markdown(text) do + text + |> ExDoc.Utils.strip_tags(" ") + |> drop_ignorable_codeblocks() + |> String.trim() + end + + defp render_sections(sections, page, title, type) do + for {header, anchor, body} <- sections do + encode("#{page}##{anchor}", header <> " - " <> title, type, body) + end + end + + @ignored_codeblocks ~w[vega-lite] + + defp drop_ignorable_codeblocks(section) do + block_names = Enum.join(@ignored_codeblocks, "|") + String.replace(section, ~r/^```(?:#{block_names})\n(?:[\s\S]*?)```$/m, "") + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates.ex new file mode 100644 index 0000000..5c88922 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates.ex @@ -0,0 +1,268 @@ +defmodule ExDoc.Formatter.HTML.Templates do + @moduledoc false + require EEx + alias ExDoc.Formatter.HTML.Assets + + import ExDoc.Utils, only: [h: 1, text_to_id: 1] + + defp enc(binary), do: ExDoc.Utils.h(URI.encode(binary)) + + @doc """ + Get the pretty name of a function node + """ + def pretty_type(%{type: t}) do + Atom.to_string(t) + end + + @doc """ + Returns the title with `` after each fullstop, allowing word breaks in long module names. + """ + def breakable_module_title(title) when is_binary(title) do + String.replace(title, ".", ".") + end + + @doc """ + Returns the HTML formatted title for the module page. + """ + def module_type(%{type: :task}), do: "" + def module_type(%{type: :module}), do: "" + def module_type(%{type: type}), do: "#{type}" + + @doc """ + Renders a "Copy Markdown" link if markdown formatter is enabled. + """ + def copy_markdown_link(config, path) do + if "markdown" in config.formatters do + """ + + + Copy Markdown + + """ + else + "" + end + end + + @doc """ + Create a JS object which holds all the items displayed in the sidebar area + """ + def create_sidebar_items(config, modules, tasks, extras) do + nodes = + [modules: modules, tasks: tasks] + |> Enum.map(&sidebar_module/1) + |> Map.new() + |> Map.put(:extras, api_reference(config, modules, tasks) ++ sidebar_extras(extras)) + + ["sidebarNodes=" | ExDoc.Utils.to_json(nodes)] + end + + defp api_reference(%{api_reference: false}, _modules, _tasks), do: [] + + defp api_reference(_config, modules, tasks) do + headers = + if(modules != [], do: [%{id: "Modules", anchor: "modules"}], else: []) ++ + if(tasks != [], do: [%{id: "Mix Tasks", anchor: "tasks"}], else: []) + + [%{id: "api-reference", title: "API Reference", group: "", headers: headers}] + end + + defp sidebar_extras(extras) do + for extra <- extras do + %{id: id, title: title, group: group} = extra + item = %{id: to_string(id), title: to_string(title), group: to_string(group)} + + case extra do + %{search_data: search_data} when is_list(search_data) -> + search_data = + Enum.map(search_data, fn search_item -> + %{ + anchor: search_item.anchor, + id: search_item.title, + labels: [search_item.type] + } + end) + + item + |> Map.put(:headers, headers(extra.doc)) + |> Map.put(:searchData, search_data) + + %{url: url} when is_binary(url) -> + Map.put(item, :url, url) + + _ -> + Map.put(item, :headers, headers(extra.doc)) + end + end + end + + defp sidebar_module({id, modules}) do + modules = + for module <- modules do + groups = + case module.docs_groups do + [] -> [] + entries -> [nodeGroups: Enum.map(entries, &sidebar_entries/1)] + end + + pairs = + for key <- [:id, :title, :nested_title, :nested_context], + value = Map.get(module, key), + do: {key, value} + + others = [ + deprecated: not is_nil(module.deprecated), + sections: headers(module.doc || []), + group: to_string(module.group) + ] + + Map.new(groups ++ pairs ++ others) + end + + {id, modules} + end + + defp sidebar_entries(group) do + nodes = + for node <- group.docs do + id = + if "struct" in node.annotations do + node.signature + else + if node.name == nil do + "nil/#{node.arity}" + else + "#{node.name}/#{node.arity}" + end + end + + deprecated? = not is_nil(node.deprecated) + + %{id: id, title: node.signature, anchor: URI.encode(node.id), deprecated: deprecated?} + end + + %{key: text_to_id(group.title), name: group.title, nodes: nodes} + end + + defp headers(doc) do + doc + |> ExDoc.DocAST.extract_headers_with_ids([:h2]) + |> Enum.map(fn {:h2, text, anchor} -> + %{id: text, anchor: anchor} + end) + end + + defp favicon_path(%{favicon: favicon}), do: "assets/favicon#{Path.extname(favicon)}" + + defp logo_path(%{logo: logo}), do: "assets/logo#{Path.extname(logo)}" + + defp sidebar_type(:exception), do: "modules" + defp sidebar_type(:module), do: "modules" + defp sidebar_type(:behaviour), do: "modules" + defp sidebar_type(:protocol), do: "modules" + defp sidebar_type(:task), do: "tasks" + + defp sidebar_type(:search), do: "search" + defp sidebar_type(:cheatmd), do: "extras" + defp sidebar_type(:livemd), do: "extras" + defp sidebar_type(:extra), do: "extras" + + @section_header_class_name "section-heading" + + @doc """ + Renders the document in the page. + + For now it enriches the document by adding fancy anchors + around h2 and h3 tags with IDs. + """ + def render_doc(nil), do: "" + + def render_doc(ast) do + ast + |> add_fancy_anchors() + |> add_nofollow() + |> ExDoc.DocAST.to_html() + end + + @official_domains ~w(hex.pm hexdocs.pm elixir-lang.org erlang.org) + + defp add_nofollow(ast) do + ExDoc.DocAST.map_tags(ast, fn + {:a, attrs, inner, meta} -> + href = Keyword.get(attrs, :href, "") + + if nofollow?(href) do + {:a, Keyword.put(attrs, :rel, "nofollow"), inner, meta} + else + {:a, attrs, inner, meta} + end + + other -> + other + end) + end + + defp nofollow?(href) do + uri = URI.parse(href) + uri.scheme in ["http", "https"] and not official_host?(uri.host) + end + + defp official_host?(nil), do: false + + defp official_host?(host) do + Enum.any?(@official_domains, fn domain -> + host == domain or String.ends_with?(host, "." <> domain) + end) + end + + defp add_fancy_anchors(ast) do + ExDoc.DocAST.map_tags(ast, fn + {tag, attrs, inner, meta} = ast + when tag in [:h2, :h3] and not is_map_key(meta, :verbatim) -> + if id = Keyword.get(attrs, :id) do + attrs = + Keyword.update( + attrs, + :class, + @section_header_class_name, + &(&1 <> " " <> @section_header_class_name) + ) + + {tag, attrs, + [ + {:a, [href: "##{id}", class: "hover-link"], + [ + {:i, [class: "ri-link-m", "aria-hidden": "true"], [], %{}} + ], %{}}, + {:span, [class: "text"], inner, %{}} + ], meta} + else + ast + end + + ast -> + ast + end) + end + + templates = [ + detail_template: [:node, :module], + footer_template: [:config, :source_path], + head_template: [:config, :title, :noindex], + module_template: [:config, :module], + not_found_template: [:config], + api_reference_entry_template: [:module_node], + api_reference_template: [:config, :modules, :tasks], + extra_template: [:config, :node, :refs], + search_template: [:config], + sidebar_template: [:config, :type], + summary_template: [:name, :nodes], + redirect_template: [:config, :redirect_to] + ] + + Enum.each(templates, fn {name, args} -> + filename = Path.expand("templates/#{name}.eex", __DIR__) + @doc false + EEx.function_from_file(:def, name, filename, args, trim: true) + end) +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex new file mode 100644 index 0000000..759555a --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex @@ -0,0 +1,11 @@ +
    +
    + <%= breakable_module_title(h module_node.title) %> + <%= if deprecated = module_node.deprecated do %> + deprecated + <% end %> +
    + <%= if doc = module_node.doc do %> +
    <%= ExDoc.DocAST.synopsis(doc) %>
    + <% end %> +
    diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_template.eex new file mode 100644 index 0000000..4b164a2 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_template.eex @@ -0,0 +1,39 @@ +<%= head_template(config, "API Reference", false) %> +<%= sidebar_template(config, :extra) %> + +
    +
    +

    API Reference <%= config.project %> v#<%= config.version %>

    + <%= copy_markdown_link(config, "api-reference.md") %> + <%= if config.source_url do %> + + + View Source + + <% end %> +
    + + <%= if modules != [] do %> +
    +

    Modules

    +
    + <%= for module_node <- Enum.sort_by(modules, & &1.id) do + api_reference_entry_template(module_node) + end %> +
    +
    + <% end %> + + <%= if tasks != [] do %> +
    +

    Mix Tasks

    +
    + <%= for task_node <- tasks do + api_reference_entry_template(task_node) + end %> +
    +
    + <% end %> +
    + +<%= footer_template(config, nil) %> \ No newline at end of file diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/detail_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/detail_template.eex new file mode 100644 index 0000000..7be26d9 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/detail_template.eex @@ -0,0 +1,38 @@ +
    + <%= for {default_name, default_arity} <- node.defaults do %> + "> + <% end %> +
    + + + +
    +

    <%=h node.signature %>

    + <%= for annotation <- node.annotations do %> + (<%= annotation %>) + <% end %> + <%= if node.source_url do %> + + + + <% end %> +
    +
    + <%= if deprecated = node.deprecated do %> +
    + This <%= node.type %> is deprecated. <%= h(deprecated) %>. +
    + <% end %> + +
    + <%= if node.specs != [] do %> +
    + <%= for spec <- node.specs do %> +
    <%= module.language.format_spec_attribute(node) %> <%= spec %>
    + <% end %> +
    + <% end %> + + <%= render_doc(node.doc) %> +
    +
    diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/extra_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/extra_template.eex new file mode 100644 index 0000000..97af056 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/extra_template.eex @@ -0,0 +1,64 @@ +<%= head_template(config, node.title, false) %> +<%= sidebar_template(config, node.type) %> + +
    +
    +

    <%= ExDoc.DocAST.to_html(node.title_doc) %>

    + <%= if node.type == :cheatmd do %> + + + Print Cheatsheet + + <% end %> + <%= copy_markdown_link(config, "#{node.id}.md") %> + <%= if node.source_url do %> + + + View Source + + <% end %> +
    + + <%= if node.type == :livemd do %> +
    + + Run in Livebook + +
    + <% end %> + + <%= if node.type == :cheatmd do %> + <%= node.doc |> ExDoc.DocAST.sectionize([:h2, :h3]) |> render_doc() %> + <% else %> + <%= render_doc(node.doc) %> + <% end %> +
    + +
    +
    + <%= if refs.prev do %> + + <% end %> +
    +
    + <%= if refs.next do %> + + <% end %> +
    +
    + +<%= footer_template(config, node.source_path) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/footer_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/footer_template.eex new file mode 100644 index 0000000..58d4e34 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/footer_template.eex @@ -0,0 +1,58 @@ + + + + + <%= config.before_closing_body_tag.(:html) %> + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/head_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/head_template.eex new file mode 100644 index 0000000..2bac354 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/head_template.eex @@ -0,0 +1,29 @@ + + + + + + + + + <%= if config.authors do %> + "> + <% end %> + <%= if noindex do %> + + <% end %> + <%= title %> — <%= config.project %> v<%= config.version %> + <%= if config.favicon do %> + + <% end %> + + <%= if config.canonical do %> + + <% end %> + + + + <%= config.before_closing_head_tag.(:html) %> + + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/module_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/module_template.eex new file mode 100644 index 0000000..bb8008b --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/module_template.eex @@ -0,0 +1,68 @@ +<%= head_template(config, module.title, false) %> +<%= sidebar_template(config, module.type) %> + +
    +
    +

    + <%= breakable_module_title(module.title) %> <%= module_type(module) %> + (<%= config.project %> v<%= config.version %>) + <%= for annotation <- module.annotations do %> + (<%= annotation %>) + <% end %> +

    + <%= if module.source_format == "text/markdown" do %> + <%= copy_markdown_link(config, "#{module.id}.md") %> + <% end %> + <%= if module.source_url do %> + + + View Source + + <% end %> +
    + + <%= if deprecated = module.deprecated do %> +
    + This <%= module.type %> is deprecated. <%= h(deprecated) %>. +
    + <% end %> + + <%= if doc = module.doc do %> +
    + <%= render_doc(doc) %> +
    + <% end %> +
    + +<%= if module.docs_groups != [] do %> +
    +

    + + + + Summary +

    + <%= for group <- module.docs_groups, do: summary_template(group.title, group.docs) %> +
    +<% end %> + +<%= for group <- module.docs_groups, key = text_to_id(group.title) do %> +
    +

    + + + + <%= group.title %> +

    + <%= if doc = group.doc do %> +
    + <%= render_doc(doc) %> +
    + <% end %> +
    + <%= for node <- group.docs, do: detail_template(node, module) %> +
    +
    +<% end %> + +<%= footer_template(config, module.source_path) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/not_found_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/not_found_template.eex new file mode 100644 index 0000000..d4503d1 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/not_found_template.eex @@ -0,0 +1,15 @@ +<%= head_template(config, "404", true) %> +<%= sidebar_template(config, :extra) %> + +

    + Page not found +

    + +

    Sorry, but the page you were trying to get to, does not exist. You +may want to try searching this site using the sidebar +<%= if config.api_reference do %> + or using our API Reference page +<% end %> +to find what you were looking for.

    + +<%= footer_template(config, nil) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/redirect_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/redirect_template.eex new file mode 100644 index 0000000..a6a9c7c --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/redirect_template.eex @@ -0,0 +1,10 @@ + + + + + <%= config.project %> v<%= config.version %> — Documentation + + + + + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/search_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/search_template.eex new file mode 100644 index 0000000..732b3b5 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/search_template.eex @@ -0,0 +1,12 @@ +<%= head_template(config, "Search", true) %> +<%= sidebar_template(config, :search) %> + + + +<%= footer_template(config, nil) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/sidebar_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/sidebar_template.eex new file mode 100644 index 0000000..ba2feab --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/sidebar_template.eex @@ -0,0 +1,72 @@ +
    + + + + + + + +
    +
    + diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/summary_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/summary_template.eex new file mode 100644 index 0000000..5005183 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/summary_template.eex @@ -0,0 +1,18 @@ +
    +

    + <%= name %> +

    + <%= for node <- nodes do %> +
    +
    + <%=h node.signature %> + <%= if deprecated = node.deprecated do %> + deprecated + <% end %> +
    + <%= if doc = node.doc do %> +
    <%= ExDoc.DocAST.synopsis(doc) %>
    + <% end %> +
    + <% end %> +
    diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown.ex new file mode 100644 index 0000000..63620c3 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown.ex @@ -0,0 +1,120 @@ +defmodule ExDoc.Formatter.MARKDOWN do + @moduledoc false + + alias __MODULE__.Templates + + def run(config, project_nodes, extras) when is_map(config) do + File.mkdir_p!(config.output) + + static_files = + if Enum.all?(config.assets, fn {_k, target_dir} -> + config.output |> Path.join(target_dir) |> File.exists?() + end) do + [] + else + ExDoc.Formatter.copy_assets(config.assets, config.output) + end + + {modules, tasks} = + project_nodes + |> Enum.filter(&(&1.source_format == "text/markdown")) + |> Enum.split_with(&(&1.type != :task)) + + all_files = + generate_nav(config, modules, tasks, extras) ++ + generate_api_reference(config, modules, tasks) ++ + generate_extras(config, extras) ++ + generate_list(config, modules) ++ + generate_list(config, tasks) ++ + static_files + + entrypoint = config.output |> Path.join("llms.txt") |> Path.relative_to_cwd() + %{entrypoint: entrypoint, build: List.flatten(all_files)} + end + + defp normalize_output(output) do + output + |> String.replace("\r\n", "\n") + |> String.replace(~r/\n{3,}/, "\n\n") + end + + defp generate_nav(config, modules, tasks, extras) do + modules = group_by_group(modules) + mix_tasks = group_by_group(tasks) + extras = group_by_group(extras) + + content = + config + |> Templates.llms_txt_template(modules, mix_tasks, extras) + |> normalize_output() + + filename = "llms.txt" + + write!(config, filename, content) + + [filename] + end + + defp generate_api_reference(%{api_reference: false}, _modules, _tasks) do + [] + end + + defp generate_api_reference(config, modules, tasks) do + modules = group_by_group(modules) + mix_tasks = group_by_group(tasks) + + content = + config + |> Templates.api_reference_template(modules, mix_tasks) + |> normalize_output() + + filename = "api-reference.md" + + write!(config, filename, content) + + [filename] + end + + defp group_by_group(nodes) do + nodes + |> Enum.chunk_by(& &1.group) + |> Enum.map(&{hd(&1).group, &1}) + end + + defp generate_extras(config, extras) do + for %ExDoc.ExtraNode{id: id, source_doc: content} <- extras do + filename = "#{id}.md" + + write!(config, filename, content) + + filename + end + end + + defp generate_list(config, nodes) do + nodes + |> Task.async_stream(&generate_module(config, &1), timeout: :infinity) + |> Enum.map(&elem(&1, 1)) + end + + ## Helpers + + defp generate_module(config, module_node) do + content = + config + |> Templates.module_template(module_node) + |> normalize_output() + + filename = "#{module_node.id}.md" + + write!(config, filename, content) + + filename + end + + defp write!(config, filename, content) do + config.output + |> Path.join(filename) + |> File.write!(content) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates.ex new file mode 100644 index 0000000..556a1a6 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates.ex @@ -0,0 +1,65 @@ +defmodule ExDoc.Formatter.MARKDOWN.Templates do + @moduledoc false + + require EEx + import ExDoc.Utils, only: [h: 1] + + @doc """ + Returns the original markdown documentation from source_doc. + """ + def node_doc(%{source_doc: %{"en" => source}}) when is_binary(source), do: source + def node_doc(_), do: nil + + @doc """ + Computes the synopsis based on source_doc. + """ + def synopsis(%{source_doc: %{"en" => source}}) when is_binary(source), + do: + source + |> String.split(["\r\n\r\n", "\n\n"], parts: 2) + |> hd() + |> String.trim_trailing(":") + |> then(&": #{&1}") + + def synopsis(_), do: nil + + EEx.function_from_file( + :def, + :module_template, + Path.expand("templates/module_template.eex", __DIR__), + [:config, :module], + trim: true + ) + + EEx.function_from_file( + :def, + :api_reference_template, + Path.expand("templates/api_reference_template.eex", __DIR__), + [:config, :modules, :mix_tasks], + trim: true + ) + + EEx.function_from_file( + :def, + :llms_txt_template, + Path.expand("templates/llms_txt_template.eex", __DIR__), + [:config, :modules, :mix_tasks, :extras], + trim: true + ) + + EEx.function_from_file( + :defp, + :nav_group_template, + Path.expand("templates/nav_group_template.eex", __DIR__), + [:title, :groups], + trim: true + ) + + EEx.function_from_file( + :defp, + :detail_template, + Path.expand("templates/detail_template.eex", __DIR__), + [:node, :module], + trim: true + ) +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/api_reference_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/api_reference_template.eex new file mode 100644 index 0000000..80e822a --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/api_reference_template.eex @@ -0,0 +1,3 @@ +# <%= config.project %> v<%= config.version %> - API Reference +<%= nav_group_template "Modules", modules %> +<%= nav_group_template "Mix Tasks", mix_tasks %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/detail_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/detail_template.eex new file mode 100644 index 0000000..b19cfa5 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/detail_template.eex @@ -0,0 +1,14 @@ +# `<%= node.name %>` +<%= for annotation <- node.annotations do %>*<%= annotation %>* <% end %> + +<%= if deprecated = node.deprecated do %> +> This <%= node.type %> is deprecated. <%= h(deprecated) %>. +<% end %> + +<%= if node.source_specs != [] do %> +```<%= module.language.highlight_info().language_name %> +<%= for spec <- node.source_specs do %><%= module.language.format_spec_attribute(node) %> <%= module.language.format_spec(spec) %> +<% end %>``` +<% end %> + +<%= node_doc(node) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex new file mode 100644 index 0000000..42725d4 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex @@ -0,0 +1,8 @@ +# <%= config.project %> v<%= config.version %> - Table of Contents +<%= if config.description do %> +> <%= config.description %> + +<% end %> +<%= nav_group_template config.extra_section, extras %> +<%= nav_group_template "Modules", modules %> +<%= nav_group_template "Mix Tasks", mix_tasks %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/module_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/module_template.eex new file mode 100644 index 0000000..cb2a22a --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/module_template.eex @@ -0,0 +1,16 @@ +# `<%=h module.title %>` +<%= if module.source_url do %>[🔗](<%= module.source_url %>)<% end %> + +<%= for annotation <- module.annotations do %>*<%= annotation %>* <% end %> +<%= if deprecated = module.deprecated do %> +> This <%= module.type %> is deprecated. <%=h deprecated %>. +<% end %> +<%= node_doc(module) %> +<%= for group <- module.docs_groups, node <- group.docs do %> +<%= detail_template(node, module) %> +<% end %><%= if config.api_reference do %> + +--- + +*Consult [api-reference.md](api-reference.md) for complete listing* +<% end %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/nav_group_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/nav_group_template.eex new file mode 100644 index 0000000..8594a3b --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/nav_group_template.eex @@ -0,0 +1,14 @@ +<%= if groups != [] do %> +## <%= title %> + +<%= for {title, nodes} <- groups do %> +<%= if title do %> +- <%=h to_string(title) %> +<%= for node <- nodes do %> - [<%=h node.title %>](<%= URI.encode node.id %>.md)<%= synopsis(node) %> +<% end %> +<% else %> +<%= for node <- nodes do %>- [<%=h node.title %>](<%= URI.encode node.id %>.md)<%= synopsis(node) %> +<% end %> +<% end %> +<% end %> +<% end %> \ No newline at end of file diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language.ex new file mode 100644 index 0000000..78c4e30 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language.ex @@ -0,0 +1,170 @@ +defmodule ExDoc.Language do + @moduledoc false + + @typedoc """ + The map has the following keys: + + * `:module` - the module + + * `:default_groups` - the default groups used by this module + + * `:docs` - the docs chunk + + * `:language` - the language callback + + * `:id` - module page name + + * `:title` - module display title + + * `:type` - module type + + * `:source_line` - the line where the module code is located, defmodule in Elixir, or -module in Erlang + + * `:source_file` - the source file the module code is located, defmodule in Elixir, or -module in Erlang + + * `:source_basedir` - the absolute directory where the Elixir/Erlang compiler was run. + + * `:nesting_info` - a `{nested_title, nested_context}` tuple or `nil`. + For example, `"A.B.C"` becomes `{"C", "A.B."}`. + + * `:private` - a map with language-specific data + """ + @type module_data() :: %{ + module: module(), + default_groups: [binary()], + docs: tuple(), + language: module(), + id: String.t(), + title: String.t(), + type: atom() | nil, + source_basedir: String.t(), + source_file: String.t() | nil, + source_line: non_neg_integer(), + nesting_info: {String.t(), String.t()} | nil, + private: map() + } + + @doc """ + Returns a map with module information. + """ + @callback module_data(module(), tuple(), ExDoc.Config.t()) :: module_data() | false + + @doc """ + Returns a map with documentation information about a given node or `false`. + + The map has the following keys: + + * `:id_key` - the key used to namespace this entry + + * `:default_group` - the default group this definition falls under + + * `:doc_fallback` - if set, a 0-arity function that returns DocAST which + will be used as fallback to empty docs on the function node + + * `:extra_annotations` - additional annotations + + * `:signature` - the function signature + + * `:source_file` - the source file where the code in located + + * `:source_line` - the line where the code is located, def/defp in Elixir, foo(...) in Erlang + + * `:specs` - a list of specs that will be later formatted by `c:typespec/2` + + * `:type` - the type of the doc (`:function`, `:macro`, `:type`, etc) + + """ + @callback doc_data(entry :: tuple(), module_data()) :: + %{ + id_key: binary(), + default_group: binary(), + doc_fallback: (-> ExDoc.DocAST.t()), + extra_annotations: [String.t()], + signature: [binary()], + source_file: String.t() | nil, + source_line: non_neg_integer() | nil, + specs: [ExDoc.DocNode.spec_ast()], + type: atom() + } + | false + + @doc """ + Autolinks docs. + """ + @callback autolink_doc(doc :: ExDoc.DocAST.t(), config :: ExDoc.Autolink.t()) :: + ExDoc.DocAST.t() + + @doc """ + Autolinks typespecs. + """ + @callback autolink_spec(spec :: term(), config :: ExDoc.Autolink.t()) :: iodata() + + @doc """ + Formats typespecs (without autolinking). + """ + @callback format_spec(spec :: term()) :: iodata() + + @doc """ + Returns information for syntax highlighting. + """ + @callback highlight_info() :: %{ + language_name: String.t(), + lexer: module(), + opts: keyword() + } + + @doc """ + Return an attribute in the canonical representation. + """ + @callback format_spec_attribute(%ExDoc.DocNode{}) :: String.t() + + @doc """ + Parse a module.function string and return it. + """ + @callback parse_module_function(String.t()) :: + {:local, function :: atom()} + | {:remote, module :: module(), function :: atom()} + | :error + + @doc """ + Parse a module string and return it. + """ + @callback parse_module(String.t(), mode :: :regular_link | :custom_link) :: + {:module, atom()} | :error + + @doc """ + Return a URL to autoimported function if atom+arity are autoimported + """ + @callback try_autoimported_function( + name :: atom(), + arity :: non_neg_integer(), + mode :: :regular_link | :custom_link, + config :: ExDoc.Autolink.t(), + original_text :: String.t() + ) :: + nil | String.t() + + @doc """ + Return a URL to built-in type if atom+arity are built-in + """ + @callback try_builtin_type( + name :: atom(), + arity :: non_neg_integer(), + mode :: :regular_link | :custom_link, + config :: ExDoc.Autolink.t(), + original_text :: String.t() + ) :: + nil | String.t() + + def get(:elixir, _module), do: {:ok, ExDoc.Language.Elixir} + def get(:erlang, _module), do: {:ok, ExDoc.Language.Erlang} + + def get(language, module) when is_atom(language) and is_atom(module) do + ExDoc.warn( + "skipping module #{module}, reason: unsupported language (#{language})", + [] + ) + + :error + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/elixir.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/elixir.ex new file mode 100644 index 0000000..05ab578 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/elixir.ex @@ -0,0 +1,729 @@ +defmodule ExDoc.Language.Elixir do + @moduledoc false + + @behaviour ExDoc.Language + + alias ExDoc.Autolink + alias ExDoc.Language.Source + + @impl true + @spec module_data(atom, any, any) :: + false + | %{ + docs: any, + id: binary, + language: ExDoc.Language.Elixir, + source_line: pos_integer, + source_file: Path.t(), + source_basedir: Path.t(), + module: module, + nesting_info: nil, + private: %{ + abst_code: any, + callbacks: map, + impls: map, + optional_callbacks: any, + specs: map + }, + title: binary, + type: :module | :behaviour | :task | :protocol | :implementation | :exception + } + def module_data(module, docs_chunk, config) do + {type, skip} = module_type_and_skip(module) + + cond do + skip -> + false + + abst_code = Source.get_abstract_code(module) -> + title = module_title(module, type) + + source_basedir = Source.fetch_basedir!(abst_code, module) + + {source_file, source_line} = + Source.fetch_module_location!(abst_code, source_basedir, module) + + optional_callbacks = Source.get_optional_callbacks(module, type) + + %{ + module: module, + default_groups: ~w(Types Callbacks Functions), + docs: docs_chunk, + language: __MODULE__, + id: inspect(module), + title: title, + type: type, + source_line: source_line, + source_file: source_file, + source_basedir: source_basedir, + nesting_info: nesting_info(title, config.nest_modules_by_prefix), + private: %{ + abst_code: abst_code, + specs: Source.get_specs(abst_code, source_basedir), + callbacks: Source.get_callbacks(abst_code, source_basedir), + impls: get_impls(module), + optional_callbacks: optional_callbacks + } + } + + true -> + ExDoc.warn( + "skipping docs for module #{inspect(module)}, reason: :no_debug_info", + [] + ) + + false + end + end + + @impl true + def doc_data(entry, %{type: type} = module_data) do + case entry do + {_key, _anno, _sig, :hidden, _metadata} -> + false + + {{_kind, name, _arity}, _anno, _sig, _doc, _metadata} + when name in [:impl_for, :impl_for!] and type == :protocol -> + false + + {{kind, _, _}, _anon, _sig, _doc, _metadata} when kind in [:function, :macro] -> + function_data(entry, module_data) + + {{kind, _, _}, _anon, _sig, _doc, _metadata} + when kind in [:callback, :macrocallback] and type != :protocol -> + callback_data(entry, module_data) + + {{:type, _, _}, _anon, _sig, _doc, _metadata} -> + type_data(entry, module_data) + + _ -> + false + end + end + + defp function_data(entry, module_data) do + {{kind, name, arity}, anno, signature, _doc_content, metadata} = entry + + extra_annotations = + case {kind, name, arity} do + {:macro, _, _} -> ["macro"] + {_, :__struct__, _} -> ["struct"] + _ -> [] + end + + actual_def = actual_def(name, arity, kind) + + %{ + id_key: "", + default_group: "Functions", + doc_fallback: fn -> + impl = Map.fetch(module_data.private.impls, actual_def) + callback_doc_ast(name, arity, impl) || delegate_doc_ast(metadata[:delegate_to]) + end, + extra_annotations: extra_annotations, + signature: signature, + source_file: nil, + source_line: find_function_line(module_data, actual_def) || Source.anno_line(anno), + specs: specs(kind, name, actual_def, module_data), + type: kind + } + end + + defp callback_data(entry, module_data) do + {{kind, name, arity}, anno, _signature, _doc, _metadata} = entry + actual_def = actual_def(name, arity, kind) + + extra_annotations = + if actual_def in module_data.private.optional_callbacks, do: ["optional"], else: [] + + {anno, specs} = + case module_data.private.callbacks do + %{^actual_def => {:attribute, anno, :callback, {_name, specs}}} -> + {anno, + if kind == :macrocallback do + Enum.map(specs, &remove_callback_term/1) + else + specs + end} + + %{} -> + {anno, []} + end + + line = Source.anno_line(anno) + quoted = Enum.map(specs, &Code.Typespec.spec_to_quoted(name, &1)) + signature = [get_typespec_signature(hd(quoted), arity)] + + %{ + id_key: "c:", + default_group: "Callbacks", + doc_fallback: fn -> nil end, + extra_annotations: extra_annotations, + signature: signature, + source_file: nil, + source_line: line, + specs: quoted, + type: kind + } + end + + defp remove_callback_term({:type, num, :bounded_fun, [lhs, rhs]}) do + {:type, num, :bounded_fun, [remove_callback_term(lhs), rhs]} + end + + defp remove_callback_term({:type, num, :fun, [{:type, num, :product, [_ | rest_args]} | rest]}) do + {:type, num, :fun, [{:type, num, :product, rest_args} | rest]} + end + + defp type_data(entry, module_data) do + {{_kind, name, arity}, _anno, _signature, _doc, _metadata} = entry + + %{type: type, spec: spec, source_file: source, source_line: line} = + Source.fetch_type!(module_data, name, arity) + + quoted = spec |> Code.Typespec.type_to_quoted() |> process_type_ast(type) + signature = [get_typespec_signature(quoted, arity)] + + %{ + id_key: "t:", + default_group: "Types", + doc_fallback: fn -> nil end, + extra_annotations: [], + source_file: source, + source_line: line, + signature: signature, + specs: [quoted], + type: type + } + end + + @autoimported_modules [Kernel, Kernel.SpecialForms] + + @impl true + def try_autoimported_function(name, arity, mode, %Autolink{} = config, original_text) do + Enum.find_value(@autoimported_modules, fn module -> + Autolink.remote_url({:function, module, name, arity}, config, original_text, + warn?: false, + mode: mode + ) + end) + end + + @basic_types [ + any: 0, + none: 0, + atom: 0, + map: 0, + pid: 0, + port: 0, + reference: 0, + struct: 0, + tuple: 0, + float: 0, + integer: 0, + neg_integer: 0, + non_neg_integer: 0, + pos_integer: 0, + list: 1, + nonempty_list: 1, + maybe_improper_list: 2, + nonempty_improper_list: 2, + nonempty_maybe_improper_list: 2 + ] + + @built_in_types [ + term: 0, + arity: 0, + as_boolean: 1, + binary: 0, + bitstring: 0, + boolean: 0, + byte: 0, + char: 0, + charlist: 0, + nonempty_charlist: 0, + fun: 0, + function: 0, + identifier: 0, + iodata: 0, + iolist: 0, + keyword: 0, + keyword: 1, + list: 0, + nonempty_list: 0, + maybe_improper_list: 0, + nonempty_maybe_improper_list: 0, + mfa: 0, + module: 0, + no_return: 0, + node: 0, + number: 0, + struct: 0, + timeout: 0 + ] + + @impl true + def try_builtin_type(name, arity, _mode, %Autolink{} = config, _original_text) + when {name, arity} in @basic_types do + Autolink.ex_doc_app_url(Kernel, config, "typespecs", config.ext, "#basic-types") + end + + def try_builtin_type(name, arity, _mode, %Autolink{} = config, _original_text) + when {name, arity} in @built_in_types do + Autolink.ex_doc_app_url(Kernel, config, "typespecs", config.ext, "#built-in-types") + end + + def try_builtin_type(_name, _arity, _mode, %Autolink{}, _original_text) do + nil + end + + @impl true + def parse_module_function(string) do + case string |> String.split(".") |> Enum.reverse() do + [string] -> + with {:function, function} <- parse_function(string) do + {:local, function} + end + + ["", "", ""] -> + {:local, :..} + + ["//", "", ""] -> + {:local, :..//} + + ["", ""] -> + {:local, :.} + + ["", "", "" | rest] -> + module_string = rest |> Enum.reverse() |> Enum.join(".") + + with {:module, module} <- parse_module(module_string, :custom_link) do + {:remote, module, :..} + end + + ["", "" | rest] -> + module_string = rest |> Enum.reverse() |> Enum.join(".") + + with {:module, module} <- parse_module(module_string, :custom_link) do + {:remote, module, :.} + end + + [function_string | rest] -> + module_string = rest |> Enum.reverse() |> Enum.join(".") + + with {:module, module} <- parse_module(module_string, :custom_link), + {:function, function} <- parse_function(function_string) do + {:remote, module, function} + end + end + end + + # There are special forms that are forbidden by the tokenizer + defp parse_function("__aliases__"), do: {:function, :__aliases__} + defp parse_function("__block__"), do: {:function, :__block__} + defp parse_function("%"), do: {:function, :%} + + defp parse_function(string) do + case Code.string_to_quoted("& #{string}/0", warnings: false) do + {:ok, {:&, _, [{:/, _, [{function, _, _}, 0]}]}} when is_atom(function) -> + {:function, function} + + _ -> + :error + end + end + + @impl true + def parse_module(<> <> _ = string, _mode) when first in ?A..?Z do + if string =~ ~r/^[A-Za-z0-9_.]+$/ do + do_parse_module(string) + else + :error + end + end + + def parse_module(":" <> _ = string, :custom_link) do + do_parse_module(string) + end + + def parse_module(_, _) do + :error + end + + defp do_parse_module(string) do + case Code.string_to_quoted(string, warn_on_unnecessary_quotes: false, emit_warnings: false) do + {:ok, module} when is_atom(module) -> + {:module, module} + + {:ok, {:__aliases__, _, parts}} -> + if Enum.all?(parts, &is_atom/1) do + {:module, Module.concat(parts)} + else + :error + end + + _ -> + :error + end + end + + @impl true + def autolink_doc(ast, %Autolink{} = config) do + true = config.language == __MODULE__ + + config = %{config | force_module_prefix: false} + walk_doc(ast, config) + end + + @impl true + def format_spec(ast) do + ast + |> Macro.to_string() + |> safe_format_string!() + |> ExDoc.Utils.h() + end + + @impl true + def autolink_spec(ast, %Autolink{} = config) do + string = format_spec(ast) + name = typespec_name(ast) + {name, rest} = split_name(string, name) + name <> autolink_typespec(rest, config) + end + + @impl true + def highlight_info() do + %{ + language_name: "elixir", + lexer: Makeup.Lexers.ElixirLexer, + opts: [] + } + end + + @impl true + def format_spec_attribute(%{type: :type}), do: "@type" + def format_spec_attribute(%{type: :opaque}), do: "@opaque" + def format_spec_attribute(%{type: :nominal}), do: "@nominal" + def format_spec_attribute(%{type: :callback}), do: "@callback" + def format_spec_attribute(%{type: :macrocallback}), do: "@macrocallback" + def format_spec_attribute(%{}), do: "@spec" + + ## Module Helpers + + defp nesting_info(title, prefixes) do + prefixes + |> Enum.find(&String.starts_with?(title, &1 <> ".")) + |> case do + nil -> nil + prefix -> {"." <> String.trim_leading(title, prefix <> "."), prefix} + end + end + + defp module_type_and_skip(module) do + cond do + function_exported?(module, :__info__, 1) and + Enum.any?(module.__info__(:struct) || [], &(&1.field == :__exception__)) -> + {:exception, false} + + function_exported?(module, :__protocol__, 1) -> + {:protocol, false} + + function_exported?(module, :__impl__, 1) -> + {:impl, true} + + match?("Elixir.Mix.Tasks." <> _, Atom.to_string(module)) -> + {:task, false} + + function_exported?(module, :behaviour_info, 1) -> + {:behaviour, false} + + true -> + {:module, false} + end + end + + defp module_title(module, :task), do: "mix " <> task_name(module) + defp module_title(module, _), do: inspect(module) + + defp task_name(module) do + "Elixir.Mix.Tasks." <> name = Atom.to_string(module) + name |> String.split(".") |> Enum.map_join(".", &Macro.underscore/1) + end + + def get_impls(module) do + for behaviour <- behaviours_implemented_by(module), + abstract_code = Source.get_abstract_code(behaviour), + {callback, _} <- Source.get_callbacks(abstract_code, ""), + do: {callback, behaviour}, + into: %{} + end + + defp behaviours_implemented_by(module) do + for {:behaviour, list} <- module.module_info(:attributes), + behaviour <- list, + do: behaviour + end + + ## Helpers + + defp specs(kind, name, actual_def, module_data) do + specs = + module_data.private.specs + |> Map.get(actual_def) + |> then(fn + {:attribute, _anno, :spec, {_name, type}} -> type + nil -> [] + end) + |> Enum.map(&Code.Typespec.spec_to_quoted(name, &1)) + + if kind == :macro do + Enum.map(specs, &remove_first_macro_arg/1) + else + specs + end + end + + defp actual_def(name, arity, :macrocallback) do + {String.to_atom("MACRO-" <> to_string(name)), arity + 1} + end + + defp actual_def(name, arity, :macro) do + {String.to_atom("MACRO-" <> to_string(name)), arity + 1} + end + + defp actual_def(name, arity, _), do: {name, arity} + + defp remove_first_macro_arg({:"::", info, [{name, info2, [_term_arg | rest_args]}, return]}) do + {:"::", info, [{name, info2, rest_args}, return]} + end + + defp remove_first_macro_arg({:when, meta, [lhs, rhs]}) do + {:when, meta, [remove_first_macro_arg(lhs), rhs]} + end + + defp delegate_doc_ast({m, f, a}) do + [ + {:p, [], + ["See ", {:code, [class: "inline"], [Exception.format_mfa(m, f, a)], %{line: 1}}, "."], + %{}} + ] + end + + defp delegate_doc_ast(nil) do + nil + end + + defp callback_doc_ast(name, arity, {:ok, behaviour}) do + [ + {:p, [], + [ + "Callback implementation for ", + {:code, [class: "inline"], ["c:#{inspect(behaviour)}.#{name}/#{arity}"], %{}}, + "." + ], %{}} + ] + end + + defp callback_doc_ast(_, _, _) do + nil + end + + defp find_function_line(module_data, na) do + {_source, line} = Source.fetch_function_location!(module_data, na) + line + end + + defp get_typespec_signature({:when, _, [{:"::", _, [{name, meta, args}, _]}, _]}, arity) do + Macro.to_string({name, meta, strip_types(args, arity)}) + end + + defp get_typespec_signature({:"::", _, [{name, meta, args}, _]}, arity) do + Macro.to_string({name, meta, strip_types(args, arity)}) + end + + defp get_typespec_signature({name, meta, args}, arity) do + Macro.to_string({name, meta, strip_types(args, arity)}) + end + + defp strip_types(args, arity) do + args + |> Enum.take(-arity) + |> Enum.with_index(1) + |> Enum.map(fn + {{:"::", _, [left, _]}, position} -> to_var(left, position) + {{:|, _, _}, position} -> to_var({}, position) + {left, position} -> to_var(left, position) + end) + |> Macro.prewalk(fn node -> Macro.update_meta(node, &Keyword.delete(&1, :line)) end) + end + + defp to_var({:%, meta, [name, _]}, _), do: {:%, meta, [name, {:%{}, meta, []}]} + defp to_var({:%{}, _, _}, _), do: {:map, [], nil} + defp to_var({:<<>>, _, _}, _), do: {:binary, [], nil} + defp to_var({:{}, _, _}, _), do: {:tuple, [], nil} + defp to_var({name, meta, _}, _) when is_atom(name), do: {name, meta, nil} + + defp to_var({{:., meta, [_module, name]}, _, _args}, _) when is_atom(name), + do: {name, meta, nil} + + defp to_var([{:->, _, _} | _], _), do: {:function, [], nil} + defp to_var({_, _}, _), do: {:tuple, [], nil} + defp to_var(integer, _) when is_integer(integer), do: {:integer, [], nil} + defp to_var(float, _) when is_float(float), do: {:float, [], nil} + defp to_var(list, _) when is_list(list), do: {:list, [], nil} + defp to_var(atom, _) when is_atom(atom), do: {:atom, [], nil} + defp to_var(_, position), do: {:"arg#{position}", [], nil} + + # Cut off the body of an opaque type while leaving it on a normal type. + defp process_type_ast({:"::", _, [d | _]}, :opaque), do: d + defp process_type_ast(ast, _), do: ast + + defp walk_doc(list, config) when is_list(list) do + Enum.map(list, &walk_doc(&1, config)) + end + + defp walk_doc(binary, _) when is_binary(binary) do + binary + end + + defp walk_doc({:pre, _, _, _} = ast, _config) do + ast + end + + defp walk_doc({:a, attrs, inner, meta} = ast, config) do + case Autolink.custom_link(attrs, config) do + url when is_binary(url) -> + {:a, Keyword.put(attrs, :href, url), inner, meta} + + :remove_link -> + remove_link(ast) + + nil -> + ast + end + end + + defp walk_doc({:code, attrs, [code], meta} = ast, config) do + config = %{config | line: meta[:line]} + + case Autolink.url(code, :regular_link, config) do + url when is_binary(url) -> + code = + code + |> remove_prefix() + |> remove_fragment() + + {:a, [href: url], [{:code, attrs, [code], meta}], %{}} + + :remove_link -> + {:code, attrs, [code], meta} + + nil -> + ast + end + end + + defp walk_doc({tag, attrs, ast, meta}, config) do + {tag, attrs, List.flatten(walk_doc(ast, config)), meta} + end + + defp remove_link({:a, _attrs, inner, _meta}) do + inner + end + + defp remove_prefix("c:" <> rest), do: rest + defp remove_prefix("t:" <> rest), do: rest + defp remove_prefix("m:" <> rest), do: rest + defp remove_prefix(rest), do: rest + + defp remove_fragment(string) do + string |> String.split("#") |> hd() + end + + defp safe_format_string!(string) do + try do + string + |> Code.format_string!(line_length: 80) + |> IO.iodata_to_binary() + rescue + _ -> string + end + end + + defp typespec_name({:"::", _, [{name, _, _}, _]}), do: Atom.to_string(name) + defp typespec_name({:when, _, [left, _]}), do: typespec_name(left) + defp typespec_name({name, _, _}) when is_atom(name), do: Atom.to_string(name) + + # extract out function name so we don't process it. This is to avoid linking it when there's + # a type with the same name + defp split_name(string, name) do + if String.starts_with?(string, name) do + {name, binary_part(string, byte_size(name), byte_size(string) - byte_size(name))} + else + {"", string} + end + end + + defp autolink_typespec(string, config) do + regex = ~r{ + ( # + (?: + ( # + (?: + \:[a-z][_a-zA-Z0-9]* # Erlang module + )| + (?: + [A-Z][_a-zA-Z0-9]* # Elixir module + (?:\.[A-Z][_a-zA-Z0-9]*)* # Elixir submodule + ) + ) # + \. # Dot operator + )? + ([a-z_][_a-zA-Z0-9]*[\?\!]?) # Name + ) # + (\(.*\)) # Arguments + }x + + Regex.replace(regex, string, fn _all, call_string, module_string, name_string, rest -> + module = string_to_module(module_string) + name = String.to_atom(name_string) + arity = count_args(rest, 0, 0) + original_text = call_string <> "()" + + url = + if module do + Autolink.remote_url({:type, module, name, arity}, config, original_text) + else + Autolink.local_url(:type, name, arity, config, original_text) + end + + if url do + ~s[#{ExDoc.Utils.h(call_string)}] + else + call_string + end <> autolink_typespec(rest, config) + end) + end + + defp string_to_module(""), do: nil + + defp string_to_module(string) do + if String.starts_with?(string, ":") do + string |> String.trim_leading(":") |> String.to_atom() + else + Module.concat([string]) + end + end + + defp count_args("()" <> _, 0, 0), do: 0 + defp count_args("(" <> rest, counter, acc), do: count_args(rest, counter + 1, acc) + defp count_args("[" <> rest, counter, acc), do: count_args(rest, counter + 1, acc) + defp count_args("{" <> rest, counter, acc), do: count_args(rest, counter + 1, acc) + defp count_args(")" <> _, 1, acc), do: acc + 1 + defp count_args(")" <> rest, counter, acc), do: count_args(rest, counter - 1, acc) + defp count_args("]" <> rest, counter, acc), do: count_args(rest, counter - 1, acc) + defp count_args("}" <> rest, counter, acc), do: count_args(rest, counter - 1, acc) + defp count_args("," <> rest, 1, acc), do: count_args(rest, 1, acc + 1) + defp count_args(<<_>> <> rest, counter, acc), do: count_args(rest, counter, acc) + defp count_args("", _counter, acc), do: acc +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/erlang.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/erlang.ex new file mode 100644 index 0000000..2ad0ff5 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/erlang.ex @@ -0,0 +1,736 @@ +defmodule ExDoc.Language.Erlang do + @moduledoc false + + @behaviour ExDoc.Language + + alias ExDoc.Language.Source + alias ExDoc.{Autolink, Refs} + + @impl true + @spec module_data(atom, any, any) :: + false + | %{ + docs: any, + id: binary, + language: ExDoc.Language.Erlang, + source_line: pos_integer, + source_file: Path.t(), + source_basedir: Path.t(), + module: module, + nesting_info: nil, + private: %{abst_code: any, callbacks: map, optional_callbacks: any, specs: map}, + title: binary, + type: :module | :behaviour + } + def module_data(module, docs_chunk, _config) do + if abst_code = Source.get_abstract_code(module) do + id = Atom.to_string(module) + source_basedir = Source.fetch_basedir!(abst_code, module) + + {source_file, source_line} = + Source.fetch_module_location!(abst_code, source_basedir, module) + + type = module_type(module) + + %{ + module: module, + default_groups: ~w(Types Callbacks Functions), + docs: docs_chunk, + language: __MODULE__, + id: id, + title: id, + type: type, + source_line: source_line, + source_file: source_file, + source_basedir: source_basedir, + nesting_info: nil, + private: %{ + abst_code: abst_code, + specs: Source.get_specs(abst_code, source_basedir), + callbacks: Source.get_callbacks(abst_code, source_basedir), + optional_callbacks: Source.get_optional_callbacks(module, type) + } + } + else + ExDoc.warn("skipping docs for module #{inspect(module)}, reason: :no_debug_info", []) + false + end + end + + @impl true + def doc_data(entry, module_data) do + {{kind, name, arity}, anno, signature, doc_content, metadata} = entry + + cond do + doc_content == :hidden -> + false + + # Edoc on Erlang/OTP24.1+ includes private functions in + # the chunk, so we manually yank them out. + kind == :function and function_exported?(module_data.module, name, arity) -> + function_data(name, arity, signature, metadata, module_data) + + kind == :callback -> + callback_data(name, arity, anno, signature, metadata, module_data) + + kind == :type -> + type_data(name, arity, signature, metadata, module_data) + + true -> + false + end + end + + defp function_data(name, arity, signature, metadata, module_data) do + specs = + case Map.fetch(module_data.private.specs, {name, arity}) do + {:ok, spec} -> + [spec] + + :error -> + case Map.fetch(module_data.private.specs, {module_data.module, name, arity}) do + {:ok, spec} -> + [spec] + + :error -> + [] + end + end + + {file, line} = Source.fetch_function_location!(module_data, {name, arity}) + + %{ + id_key: "", + default_group: "Functions", + doc_fallback: fn -> equiv_data(module_data.module, file, line, metadata) end, + extra_annotations: [], + signature: signature, + source_file: file, + source_line: line, + specs: specs, + type: :function + } + end + + defp callback_data(name, arity, anno, signature, metadata, module_data) do + extra_annotations = + if {name, arity} in module_data.private.optional_callbacks, do: ["optional"], else: [] + + {specs, anno} = + case Map.fetch(module_data.private.callbacks, {name, arity}) do + {:ok, spec} -> + {[spec], elem(spec, 1)} + + :error -> + {[], anno} + end + + file = Source.anno_file(anno) + line = Source.anno_line(anno) + + %{ + id_key: "c:", + default_group: "Callbacks", + doc_fallback: fn -> equiv_data(module_data.module, file, line, metadata, "c:") end, + extra_annotations: extra_annotations, + signature: signature, + source_file: file, + source_line: line, + specs: specs, + type: :callback + } + end + + defp type_data(name, arity, signature, metadata, module_data) do + %{attr: attr, source_file: file, source_line: line, type: type} = + Source.fetch_type!(module_data, name, arity) + + %{ + id_key: "t:", + default_group: "Types", + doc_fallback: fn -> equiv_data(module_data.module, file, line, metadata, "t:") end, + extra_annotations: [], + signature: signature, + source_file: file, + source_line: line, + specs: [attr], + type: type + } + end + + defp equiv_data(module, file, line, metadata, prefix \\ "") do + case metadata[:equiv] do + nil -> + nil + + equiv when is_binary(equiv) -> + ## We try to parse the equiv in order to link to the target + with {:ok, toks, _} <- :erl_scan.string(:unicode.characters_to_list(equiv <> ".")), + {:ok, [{:call, _, {:atom, _, func}, args}]} <- :erl_parse.parse_exprs(toks) do + equivalent_to( + {:a, [href: "`#{prefix}#{func}/#{length(args)}`"], + [{:code, [class: "inline"], [equiv], %{}}], %{}} + ) + else + {:ok, [{:op, _, :/, {:atom, _, _}, {:integer, _, _}}]} -> + equivalent_to({:code, [class: "inline"], ["#{prefix}#{equiv}"], %{}}) + + _ -> + equivalent_to({:code, [class: "inline"], [equiv], %{}}) + end + + equiv -> + ExDoc.warn("invalid equiv #{inspect(equiv)}", + file: file, + line: line, + module: module + ) + + nil + end + end + + defp equivalent_to(node) do + [{:p, [], ["Equivalent to ", node, "."], %{}}] + end + + @impl true + def autolink_doc(ast, %Autolink{} = config) do + true = config.language == __MODULE__ + + config = %{config | force_module_prefix: true} + walk_doc(ast, config) + end + + @impl true + def autolink_spec(nil, _opts) do + nil + end + + def autolink_spec(ast, %Autolink{} = config) do + {name, anno, quoted} = + case ast do + {:attribute, anno, kind, {mfa, ast}} when kind in [:spec, :callback] -> + {mn, name} = + case mfa do + {name, _} -> {name, name} + {module, name, _} -> {{module, name}, name} + end + + {mn, anno, Enum.map(ast, &Code.Typespec.spec_to_quoted(name, &1))} + + {:attribute, anno, kind, ast} when kind in [:type, :opaque, :nominal] -> + {name, _, _} = ast + {name, anno, Code.Typespec.type_to_quoted(ast)} + end + + formatted = format_spec(ast) + config = %{config | file: Source.anno_file(anno), line: Source.anno_line(anno)} + autolink_spec(quoted, name, formatted, config) + end + + @impl true + def highlight_info() do + %{ + language_name: "erlang", + lexer: Makeup.Lexers.ErlangLexer, + opts: [] + } + end + + @impl true + def format_spec_attribute(%{type: :type}), do: "-type" + def format_spec_attribute(%{type: :opaque}), do: "-opaque" + def format_spec_attribute(%{type: :nominal}), do: "-nominal" + def format_spec_attribute(%{type: :callback}), do: "-callback" + def format_spec_attribute(%{}), do: "-spec" + + ## Autolink + + defp walk_doc(list, config) when is_list(list) do + Enum.map(list, &walk_doc(&1, config)) + end + + defp walk_doc(binary, _) when is_binary(binary) do + binary + end + + defp walk_doc({:pre, _, _, _} = ast, _config) do + ast + end + + defp walk_doc({:code, attrs, [code], meta} = ast, config) when is_binary(code) do + config = %{config | line: meta[:line]} + + case Autolink.url(code, :regular_link, config) do + url when is_binary(url) -> + code = + code + |> remove_prefix() + |> remove_fragment() + + {:a, [href: url], [{:code, attrs, [code], meta}], %{}} + + _ -> + ast + end + end + + defp walk_doc({:a, attrs, inner, meta} = ast, config) do + case attrs[:rel] do + "https://erlang.org/doc/link/seeerl" -> + {fragment, url} = extract_fragment(attrs[:href] || "", "#") + + case String.split(url, ":") do + [module] -> + walk_doc({:a, [href: "`m:#{maybe_quote(module)}#{fragment}`"], inner, meta}, config) + + [app, module] -> + inner = strip_app(inner, app) + walk_doc({:a, [href: "`m:#{maybe_quote(module)}#{fragment}`"], inner, meta}, config) + + _ -> + warn_ref(attrs[:href], config) + inner + end + + "https://erlang.org/doc/link/seemfa" -> + {prefix, url} = + case String.split(attrs[:href], "Module:") do + [url] -> + {"", url} + + [left, right] -> + {"c:", left <> right} + end + + {mfa, inner} = + case String.split(url, ":") do + [mfa] -> + {mfa, inner} + + [app, mfa] -> + {mfa, strip_app(inner, app)} + end + + walk_doc({:a, [href: "`#{prefix}#{fixup(mfa)}`"], inner, meta}, config) + + "https://erlang.org/doc/link/seetype" -> + {type, inner} = + case String.split(attrs[:href], ":") do + [type] -> + {type, inner} + + [app, type] -> + {type, strip_app(inner, app)} + end + + type = + case String.split(type, "(") do + [type] -> + type + + [type, _] -> + type <> "/0" + end + + walk_doc({:a, [href: "`t:#{fixup(type)}`"], inner, meta}, config) + + "https://erlang.org/doc/link/" <> see -> + warn_ref(attrs[:href] <> " (#{see})", %{config | id: nil}) + inner + + _ -> + case Autolink.custom_link(attrs, config) do + :remove_link -> + remove_link(ast) + + nil -> + ast + + url -> + {:a, Keyword.put(attrs, :href, url), inner, meta} + end + end + end + + defp walk_doc({tag, attrs, ast, meta}, config) do + {tag, attrs, walk_doc(ast, config), meta} + end + + defp remove_link({:a, _attrs, inner, _meta}) do + inner + end + + defp remove_prefix("c:" <> rest), do: rest + defp remove_prefix("m:" <> rest), do: rest + defp remove_prefix("t:" <> rest), do: rest + defp remove_prefix("\\" <> rest), do: rest + defp remove_prefix(rest), do: rest + + defp remove_fragment(string) do + string |> String.split("#") |> hd() + end + + defp extract_fragment(url, prefix) do + case String.split(url, "#", parts: 2) do + [url] -> {"", url} + [url, fragment] -> {prefix <> fragment, url} + end + end + + defp fixup(mfa) do + {m, fa} = + case String.split(mfa, "#") do + ["", mfa] -> + {"", mfa} + + [m, fa] -> + {"#{maybe_quote(m)}:", fa} + end + + [f, a] = String.split(fa, "/") + m <> maybe_quote(f) <> "/" <> a + end + + defp maybe_quote(m) do + to_string(:io_lib.write_atom(String.to_atom(m))) + end + + defp strip_app([{:code, attrs, [code], meta}], app) do + [{:code, attrs, List.wrap(strip_app(code, app)), meta}] + end + + defp strip_app(code, app) when is_binary(code) do + List.wrap(String.trim_leading(code, "//#{app}/")) + end + + defp strip_app(other, _app) do + List.wrap(other) + end + + defp warn_ref(href, config) do + message = "invalid reference: #{href}" + nil = config.id + Autolink.maybe_warn(config, message, nil, %{}) + end + + defp final_url({kind, name, arity}, _config) do + Autolink.fragment(kind, name, arity) + end + + defp final_url({kind, module, name, arity}, config) do + tool = Autolink.tool(module, config) + Autolink.app_module_url(tool, module, Autolink.fragment(kind, name, arity), config) + end + + @impl true + def parse_module_function(string) do + case String.split(string, ":") do + [module_string, function_string] -> + with {:module, module} <- parse_module(module_string, :custom_link), + {:function, function} <- parse_function(function_string) do + {:remote, module, function} + end + + [function_string] -> + with {:function, function} <- parse_function(function_string) do + {:local, function} + end + + _ -> + :error + end + end + + defp parse_function(string) do + with {:ok, toks, _} <- :erl_scan.string(String.to_charlist("fun #{string}/0.")), + {:ok, [{:fun, _, {:function, name, _arity}}]} <- :erl_parse.parse_exprs(toks) do + {:function, name} + else + _ -> + :error + end + end + + @impl true + def try_autoimported_function(name, arity, mode, %Autolink{} = config, original_text) do + if :erl_internal.bif(name, arity) do + Autolink.remote_url({:function, :erlang, name, arity}, config, original_text, + warn?: false, + mode: mode + ) + end + end + + @impl true + def try_builtin_type(name, arity, mode, %Autolink{} = config, original_text) do + if :erl_internal.is_type(name, arity) do + Autolink.remote_url({:type, :erlang, name, arity}, config, original_text, + warn?: false, + mode: mode + ) + end + end + + @impl true + def parse_module(string, _mode) do + case :erl_scan.string(String.to_charlist(string)) do + {:ok, [{:atom, _, module}], _} when is_atom(module) -> + {:module, module} + + _ -> + :error + end + end + + @impl true + def format_spec(ast) do + {:attribute, _, type, _} = ast + + # `-type ` => 6 + offset = byte_size(Atom.to_string(type)) + 2 + + options = [linewidth: 98 + offset] + + spec = + :erl_pp.attribute(ast, options) + |> IO.chardata_to_string() + |> String.trim() + |> String.trim_leading("-#{Atom.to_string(type)} ") + + if type == :opaque do + String.replace(spec, ~r/ ::.*$/s, "") + else + spec + end + end + + # Traverses quoted and formatted string of the typespec AST, replacing refs with links. + # + # Let's say we have this typespec: + # + # -spec f(X) -> #{atom() => bar(), integer() => X}. + # + # We traverse the AST and find types and their string representations: + # + # -spec f(X) -> #{atom() => bar(), integer() => X}. + # ^^^^ ^^^ ^^^^^^^ + # + # atom/0 => atom + # bar/0 => bar + # integer/0 => integer + # + # We then traverse the formatted string, *in order*, replacing the type strings with links: + # + # "atom(" => "atom(" + # "bar(" => "bar(" + # "integer(" => "integer(" + # + # Finally we end up with: + # + # -spec f(X) -> #{atom() => bar(), integer() => X}. + # + # All of this hassle is to preserve the original *text layout* of the initial representation, + # all the spaces, newlines, etc. + defp autolink_spec(quoted, name, formatted, config) do + acc = + for quoted <- List.wrap(quoted) do + {_quoted, acc} = + Macro.prewalk(quoted, [], fn + # module.name(args) + {{:., _, [module, name]}, _, args}, acc -> + {{:t, [], args}, [{pp({module, name}), {module, name, length(args)}} | acc]} + + {name, _, _}, acc when name in [:<<>>, :..] -> + {nil, acc} + + # -1, +1 + {op, _, [int]}, acc when is_integer(int) and op in [:+, :-] -> + {nil, acc} + + # fun() (spec_to_quoted expands it to (... -> any() in Elixir v1.17 and earlier) + # TODO: Remove me when we require Elixir v1.18+ + {:->, _, [[{name, _, _}], {:any, _, _}]} = node, acc when name == :... -> + if Version.match?(System.version(), ">= 1.18.0-rc") do + {node, acc} + else + {nil, acc} + end + + # record{type :: remote:type/arity} + {:field_type, _, [name, {{:., _, [r_mod, r_type]}, _, args}]}, acc -> + {{name, [], args}, [{pp({r_mod, r_type}), {r_mod, r_type, length(args)}} | acc]} + + # #{x :: t()} + {:field_type, _, [name, type]}, acc when is_atom(name) -> + {[type], acc} + + {name, _, args} = ast, acc when is_atom(name) and is_list(args) -> + arity = length(args) + + cond do + name == :record and args != [] and acc != [] -> + {ast, acc} + + name in [:"::", :when, :%{}, :{}, :|, :->, :..., :fun] -> + {ast, acc} + + # %{required(...) => ..., optional(...) => ...} + name in [:required, :optional] and arity == 1 -> + {ast, acc} + + # name(args) + true -> + {ast, [{pp(name), {name, arity}} | acc]} + end + + other, acc -> + {other, acc} + end) + + acc + |> Enum.reverse() + # drop the name of the typespec + |> Enum.drop(1) + end + |> Enum.concat() + + put_stack(acc) + + # Drop and re-add type name (it, the first element in acc, is dropped there too) + # + # 1. foo() :: bar() + # 2. () :: bar() + # 3. () :: bar() + # 4. foo() :: bar() + name = pp(name) + formatted = trim_name(formatted, name) + formatted = replace(formatted, acc, config) + name <> formatted + end + + defp trim_name(string, name) do + name_size = byte_size(name) + binary_part(string, name_size, byte_size(string) - name_size) + end + + defp replace(formatted, [], _config) do + formatted + end + + defp replace(formatted, acc, config) do + String.replace(formatted, Enum.map(acc, &"#{elem(&1, 0)}("), fn string -> + string = String.trim_trailing(string, "(") + + ref = + case get_stack() do + [{^string, ref} | tail] -> + put_stack(tail) + ref + + _ -> + Autolink.maybe_warn( + config, + "internal inconsistency when processing #{inspect(formatted)}", + nil, + nil + ) + end + + what = + case config.current_kfa do + {:function, _, _} -> :spec + {kind, _, _} -> kind + end + + url = + case ref do + {name, arity} -> + ref = {:type, config.current_module, name, arity} + visibility = Refs.get_visibility(ref) + + cond do + config.skip_code_autolink_to.("t:#{name}/#{arity}") -> + nil + + visibility in [:public] -> + final_url({:type, name, arity}, config) + + :erl_internal.is_type(name, arity) -> + final_url({:type, :erlang, name, arity}, config) + + true -> + Autolink.maybe_warn( + config, + "#{what} references type \"#{name}/#{arity}\" but it is " <> + Autolink.format_visibility(visibility, :type), + nil, + nil + ) + + nil + end + + {module, name, arity} -> + ref = {:type, module, name, arity} + visibility = Refs.get_visibility(ref) + + cond do + config.skip_code_autolink_to.("t:#{module}:#{name}/#{arity}") -> + nil + + visibility in [:public] -> + final_url(ref, config) + + true -> + Autolink.maybe_warn( + config, + "#{what} references type \"#{module}:#{name}/#{arity}\" but it is " <> + Autolink.format_visibility(visibility, :type), + nil, + nil + ) + + nil + end + end + + if url do + ~s|#{string}(| + else + string <> "(" + end + end) + end + + defp put_stack(items) do + Process.put({__MODULE__, :stack}, items) + end + + defp get_stack() do + Process.get({__MODULE__, :stack}) + end + + defp pp(:fun), do: "fun" + + defp pp(name) when is_atom(name) do + :io_lib.format("~p", [name]) |> IO.iodata_to_binary() + end + + defp pp({module, name}) when is_atom(module) and is_atom(name) do + :io_lib.format("~p:~p", [module, name]) |> IO.iodata_to_binary() + end + + ## Helpers + + defp module_type(module) do + cond do + function_exported?(module, :behaviour_info, 1) -> + :behaviour + + true -> + :module + end + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/source.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/source.ex new file mode 100644 index 0000000..de29702 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/source.ex @@ -0,0 +1,275 @@ +defmodule ExDoc.Language.Source do + # Shared helpers used by both Erlang and Elixir. + @moduledoc false + + def anno_line(line) when is_integer(line), do: abs(line) + def anno_line(anno), do: anno |> :erl_anno.line() |> abs() + + def anno_file(anno) do + case :erl_anno.file(anno) do + :undefined -> + nil + + file -> + String.Chars.to_string(file) + end + end + + @doc """ + Get abstract code and basedir for a module + + The abstract code has been traversed so that all records in types + have had their fields in-lined. + """ + def get_abstract_code(module) do + with {^module, binary, _file} <- :code.get_object_code(module), + {:ok, {_, [{:abstract_code, {_vsn, abst_code}}]}} <- + :beam_lib.chunks(binary, [:abstract_code]) do + expand_records_in_types(abst_code) + else + _ -> nil + end + end + + defp expand_records_in_types(abst_code) do + # Find all records in ast and collect any fields with type declarations + records = + filtermap_ast(abst_code, nil, fn + {:attribute, anno, :record, {name, fields}} -> + {name, + fields + |> Enum.flat_map(fn + {:typed_record_field, record_field, type} -> + [{:type, anno, :field_type, [elem(record_field, 2), type]}] + + record_field when elem(record_field, 0) == :record_field -> + [{:type, anno, :field_type, [elem(record_field, 2), {:type, anno, :term, []}]}] + + _ -> + [] + end)} + + _ -> + nil + end) + |> Map.new() + + # Expand records in all specs, callbacks, types, opaques and nominals + filtermap_ast(abst_code, nil, fn + {:attribute, anno, kind, {mfa, ast}} when kind in [:spec, :callback] -> + ast = Enum.map(ast, &expand_records(&1, records)) + {:attribute, anno, kind, {mfa, ast}} + + {:attribute, anno, type, {name, ast, args}} when type in [:opaque, :nominal, :type] -> + {:attribute, anno, type, {name, expand_records(ast, records), args}} + + otherwise -> + otherwise + end) + end + + defp expand_records(types, records) when is_list(types) do + Enum.map(types, &expand_records(&1, records)) + end + + defp expand_records({:ann_type, anno, [name, type]}, records) do + {:ann_type, anno, [name, expand_records(type, records)]} + end + + # When we encounter a record, we fetch the type definitions in the record and + # merge then with the type. If there are duplicates we take the one in the type + # declaration + defp expand_records({:type, anno, :record, [{:atom, _, record} = name | args]}, records) do + args = + (args ++ Map.get(records, record, [])) + |> Enum.uniq_by(fn {:type, _, :field_type, [{:atom, _, name} | _]} -> name end) + + # We delete the record from the map so that recursive + # record definitions are not expanded. + records = Map.delete(records, record) + + {:type, anno, :record, expand_records([name | args], records)} + end + + defp expand_records({type, anno, what, args}, records) when type in [:type, :user_type] do + {type, anno, what, expand_records(args, records)} + end + + defp expand_records({:remote_type, anno, [m, t, args]}, records) do + {:remote_type, anno, [m, t, expand_records(args, records)]} + end + + defp expand_records(otherwise, _records) do + otherwise + end + + @doc """ + Fetches the basedir of a module. + + The basedir is the cwd of the Elixir/Erlang compiler when compiling the module. + All `-file` attributes in the module is relative to this directory. + """ + def fetch_basedir!(abst_code, module) do + # We look for the first -file attribute to see what the source file that + # was compiled is called. Both Erlang and Elixir places one at the top. + filename = + Enum.find_value(abst_code, fn + {:attribute, _anno, :file, {filename, _line}} -> + filename + + _ -> + nil + end) || raise "could not find base directory for #{inspect(module)}" + + # The first -file attribute will be either relative or absolute + # depending on whether the compiler was called with an absolute + # or relative path. + if Path.type(filename) == :relative do + # If the compiler was called with a relative path, then any other + # relative -file attribute will be relative to the same directory. + # We use `module_info(:compile)[:source]` to get an absolute path + # to the source file and calculate the basedir from that + compile_source = + cond do + source = module.module_info(:compile)[:source] -> + source + + module in :erlang.pre_loaded() -> + {:ok, {_, [compile_info: compile_info]}} = + :beam_lib.chunks( + Application.app_dir(:erts, "ebin/#{module}.beam") + |> String.to_charlist(), + [:compile_info] + ) + + compile_info[:source] + + true -> + # This happens only when building docs for Makeup itself, + # as some of its modules may have been loaded from the escript. + filename + end + + compile_source + |> String.Chars.to_string() + |> Path.absname() + |> Path.split() + |> Enum.drop(Path.split(filename) |> Enum.count() |> Kernel.*(-1)) + |> Path.join() + else + # If an absolute path was used, then any relative -file attribute + # is relative to the directory of the source file + Path.dirname(filename) + end + end + + def fetch_module_location!(abst_code, source_basedir, module) do + find_ast(abst_code, source_basedir, fn + {:attribute, anno, :module, ^module} -> + {anno_file(anno), anno_line(anno)} + + _ -> + nil + end) || raise "could not find module definition for #{inspect(module)}" + end + + def fetch_function_location!(module_data, {name, arity}) do + find_ast(module_data.private.abst_code, module_data.source_basedir, fn + {:function, anno, ^name, ^arity, _} -> {anno_file(anno), anno_line(anno)} + _ -> nil + end) || raise "could not find function definition for #{name}/#{arity}" + end + + # Returns a map of {name, arity} => spec. + def get_specs(abst_code, source_basedir) do + filtermap_ast(abst_code, source_basedir, fn + {:attribute, _anno, :spec, {name, _types}} = spec -> + {name, spec} + + _ -> + nil + end) + |> Map.new() + end + + def fetch_type!(module_data, name, arity) do + find_ast(module_data.private.abst_code, module_data.source_basedir, fn + {:attribute, anno, type, {^name, _, args} = spec} = attr -> + if type in [:nominal, :opaque, :type] and length(args) == arity do + %{ + type: type, + spec: spec, + attr: attr, + source_file: anno_file(anno), + source_line: anno_line(anno) + } + end + + _ -> + nil + end) || raise "could not find type definition for #{name}/#{arity}" + end + + def get_callbacks(abst_code, source_basedir) do + filtermap_ast(abst_code, source_basedir, fn + {:attribute, _anno, :callback, {name, _types}} = callback -> + {name, callback} + + _ -> + nil + end) + |> Map.new() + end + + def get_optional_callbacks(module, :behaviour) do + module.behaviour_info(:optional_callbacks) + rescue + FunctionClauseError -> [] + end + + def get_optional_callbacks(_module, _type), do: [] + + defp find_ast(ast, source_basedir, fun) do + filtermap_ast(ast, source_basedir, fun) |> List.first() + end + + # The file which a form belongs to is decided by the previous :file + # attribute in the AST. The :file can be either relative, or absolute + # depending on how the file was included. So when traversing the AST + # we need to keep track of the :file attributes and update the anno + # with the correct file. + defp filtermap_ast(ast, source_basedir, fun) do + Enum.reduce(ast, {nil, []}, fn + {:attribute, _anno, :file, {filename, _line}} = entry, {_file, acc} -> + {if Path.type(filename) == :relative && source_basedir do + Path.join(source_basedir, filename) + else + filename + end, + if entry = fun.(entry) do + [entry | acc] + else + acc + end} + + entry, {file, acc} -> + anno = + if file && source_basedir do + :erl_anno.set_file(file, elem(entry, 1)) + else + elem(entry, 1) + end + + if entry = fun.(put_elem(entry, 1, anno)) do + {file, [entry | acc]} + else + {file, acc} + end + + _, file_acc -> + file_acc + end) + |> elem(1) + |> Enum.reverse() + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown.ex new file mode 100644 index 0000000..73f5cec --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown.ex @@ -0,0 +1,54 @@ +defmodule ExDoc.Markdown do + @moduledoc """ + Adapter behaviour and conveniences for converting Markdown to HTML. + + ExDoc is compatible with any markdown processor that implements the + functions defined in this module. The markdown processor can be changed + via the `:markdown_processor` option. Note that doing such change is global. + + ExDoc supports the following Markdown parsers out of the box: + + * [EarmarkParser](https://github.com/robertdober/earmark_parser) + + ExDoc uses EarmarkParser by default. + """ + + @doc """ + Converts markdown into HTML. + """ + @callback to_ast(String.t(), Keyword.t()) :: term() + + @doc """ + Returns true if all dependencies necessary are available. + """ + @callback available?() :: boolean() + + @doc """ + Converts the given markdown document to HTML AST. + + ## Options + + * `:markdown_processor` - The markdown processor to use, + either as a module or a `{module, keyword}` tuple + + All other options are passed through to the markdown processor. + """ + def to_ast(text, opts \\ []) when is_binary(text) do + {processor_pair, options} = Keyword.pop!(opts, :markdown_processor) + + {processor, options} = + case processor_pair do + {processor, processor_options} when is_atom(processor) and is_list(processor_options) -> + {processor, Keyword.merge(processor_options, options)} + + processor when is_atom(processor) and processor != nil -> + {processor, options} + + _ -> + raise ArgumentError, + ":markdown_processor must be either `Mod` or `{Mod, options}`, got: #{inspect(processor_pair)}" + end + + processor.to_ast(text, options) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown/earmark.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown/earmark.ex new file mode 100644 index 0000000..d451cd2 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown/earmark.ex @@ -0,0 +1,168 @@ +defmodule ExDoc.Markdown.Earmark do + @moduledoc """ + ExDoc extension for the EarmarkParser Markdown parser. + """ + + @behaviour ExDoc.Markdown + + @admonition_classes ~w(warning error info tip neutral) + + @impl true + def available? do + match?({:ok, _}, Application.ensure_all_started(:earmark_parser)) and + Code.ensure_loaded?(EarmarkParser) + end + + @doc """ + Generate HTML AST. + + ## Options + + * `:gfm` - (boolean) turns on Github Flavored Markdown extensions. Defaults to `true`. + + * `:breaks` - (boolean) only applicable if `gfm` is enabled. Makes all line + breaks significant (so every line in the input is a new line in the output). + + """ + @impl true + def to_ast(text, opts) do + options = [ + gfm: true, + line: 1, + file: "nofile", + breaks: false, + pure_links: true, + math: true + ] + + options = Keyword.merge(options, opts) + + case EarmarkParser.as_ast(text, options) do + {:ok, ast, messages} -> + print_messages(messages, options) + fixup(ast) + + {:error, ast, messages} -> + print_messages(messages, options) + fixup(ast) + end + end + + defp print_messages(messages, options) do + for {_severity, line, message} <- messages do + ExDoc.warn(message, file: options[:file], line: line) + end + end + + defp fixup(list) when is_list(list) do + fixup_list(list, []) + end + + defp fixup(binary) when is_binary(binary) do + binary + end + + defp fixup({tag, attrs, ast}) do + fixup({tag, attrs, ast, %{}}) + end + + # Rewrite math back to the original syntax, it's up to the user to render it + + defp fixup({"code", [{"class", "math-inline"}], [content], _}) do + "$#{content}$" + end + + defp fixup({"code", [{"class", "math-display"}], [content], _}) do + "$$\n#{content}\n$$" + end + + # Convert admonition blockquotes to sections for screen reader accessibility + defp fixup( + {"blockquote", blockquote_attrs, [{tag, h_attrs, h_content, h_meta} | rest] = ast, + blockquote_meta} + ) + when tag in ["h3", "h4"] do + h_admonition = + with {{"class", classes}, attrs} <- List.keytake(h_attrs, "class", 0), + class_list <- String.split(classes, " "), + adm_classes = [_ | _] <- Enum.filter(class_list, &(&1 in @admonition_classes)) do + {"admonition " <> Enum.join(adm_classes, " "), + [{"class", "admonition-title #{classes}"} | attrs]} + else + _ -> nil + end + + section_attrs_fn = fn admonition_classes -> + {classes, attrs} = + case List.keytake(blockquote_attrs, "class", 0) do + nil -> + {admonition_classes, blockquote_attrs} + + {{"class", classes}, attrs} -> + {"#{admonition_classes} #{classes}", attrs} + end + + [{"role", "note"}, {"class", classes} | attrs] + end + + if h_admonition do + {admonition_classes, h_attrs} = h_admonition + section_attrs = section_attrs_fn.(admonition_classes) + h_elem = {tag, h_attrs, h_content, h_meta} + + fixup({"section", section_attrs, [h_elem | rest], blockquote_meta}) + else + # regular blockquote, copied fixup/1 here to avoid infinite loop + {:blockquote, Enum.map(blockquote_attrs, &fixup_attr/1), fixup(ast), blockquote_meta} + end + end + + defp fixup({tag, attrs, ast, meta}) when is_binary(tag) and is_list(attrs) and is_map(meta) do + {fixup_tag(tag), Enum.map(attrs, &fixup_attr/1), fixup(ast), meta} + end + + defp fixup({:comment, _, _, _} = comment) do + comment + end + + # We are matching on Livebook outputs here, because we prune comments at this point + defp fixup_list( + [ + {:comment, _, [~s/ livebook:{"output":true} /], %{comment: true}}, + {"pre", pre_attrs, [{"code", code_attrs, [source], code_meta}], pre_meta} + | ast + ], + acc + ) do + code_attrs = + case Enum.split_with(code_attrs, &match?({"class", _}, &1)) do + {[], attrs} -> [{"class", "output"} | attrs] + {[{"class", class}], attrs} -> [{"class", "#{class} output"} | attrs] + end + + code_node = {"code", code_attrs, [source], code_meta} + fixup_list([{"pre", pre_attrs, [code_node], pre_meta} | ast], acc) + end + + defp fixup_list([head | tail], acc) do + fixed = fixup(head) + + if fixed == [] do + fixup_list(tail, acc) + else + fixup_list(tail, [fixed | acc]) + end + end + + defp fixup_list([], acc) do + Enum.reverse(acc) + end + + defp fixup_tag(tag) do + String.to_atom(tag) + end + + defp fixup_attr({name, value}) do + {String.to_atom(name), value} + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/nodes.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/nodes.ex new file mode 100644 index 0000000..ba46505 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/nodes.ex @@ -0,0 +1,158 @@ +# TODO: source_doc should only be a string once we remove application/html+erlang. +defmodule ExDoc.ModuleNode do + @moduledoc """ + Represents a module. + """ + + defstruct id: nil, + title: nil, + nested_context: nil, + nested_title: nil, + module: nil, + group: nil, + deprecated: nil, + doc: nil, + source_doc: nil, + source_format: nil, + moduledoc_line: nil, + moduledoc_file: nil, + source_path: nil, + source_url: nil, + docs_groups: [], + typespecs: [], + type: nil, + language: nil, + annotations: [], + metadata: nil + + @type t :: %__MODULE__{ + id: String.t(), + title: String.t(), + nested_context: String.t() | nil, + nested_title: String.t() | nil, + module: module(), + group: atom() | nil, + deprecated: String.t() | nil, + doc: ExDoc.DocAST.t() | nil, + source_doc: term() | nil, + source_format: String.t() | nil, + moduledoc_line: non_neg_integer(), + moduledoc_file: String.t(), + source_path: String.t() | nil, + source_url: String.t() | nil, + docs_groups: [ExDoc.DocGroupNode.t()], + typespecs: [ExDoc.DocNode.t()], + type: atom(), + language: module(), + annotations: [atom()], + metadata: map() + } +end + +defmodule ExDoc.DocNode do + @moduledoc """ + Represents a function, macro, callback, or type. + """ + + defstruct id: nil, + name: nil, + arity: 0, + defaults: [], + deprecated: nil, + doc: nil, + source_doc: nil, + type: nil, + signature: nil, + source_specs: [], + specs: [], + annotations: [], + group: nil, + doc_line: nil, + doc_file: nil, + source_url: nil + + @type annotation :: String.t() + @type function_default :: {name :: atom(), arity :: non_neg_integer()} + @type spec_ast :: term() + + @type t :: %__MODULE__{ + id: String.t(), + name: atom(), + arity: non_neg_integer(), + defaults: [function_default()], + deprecated: String.t() | nil, + doc: ExDoc.DocAST.t() | nil, + source_doc: term() | nil, + type: atom(), + signature: String.t(), + source_specs: [spec_ast()], + specs: [String.t()], + annotations: [annotation()], + group: String.t() | nil, + doc_file: String.t(), + doc_line: non_neg_integer(), + source_url: String.t() | nil + } +end + +defmodule ExDoc.DocGroupNode do + @moduledoc """ + Represents a group of functions, macros, callbacks, or types. + """ + defstruct title: nil, description: nil, doc: nil, docs: [] + + @type t :: %__MODULE__{ + title: String.t() | atom(), + description: String.t() | nil, + doc: ExDoc.DocAST.t() | nil, + docs: [ExDoc.DocNode.t()] + } +end + +defmodule ExDoc.ExtraNode do + @moduledoc """ + Represents an extra page. + """ + + defstruct id: nil, + title: nil, + title_doc: nil, + group: nil, + type: nil, + doc: nil, + source_doc: nil, + source_path: nil, + source_url: nil, + search_data: nil + + @type t :: %__MODULE__{ + id: String.t(), + title: String.t(), + title_doc: ExDoc.DocAST.t() | String.t(), + group: atom() | nil, + type: atom(), + doc: ExDoc.DocAST.t() | nil, + source_doc: String.t(), + source_path: String.t(), + source_url: String.t(), + search_data: [map()] | nil + } +end + +defmodule ExDoc.URLNode do + @moduledoc """ + Represents an extra URL. + """ + + defstruct id: nil, + title: nil, + group: nil, + url: nil + + @type t :: %__MODULE__{ + id: String.t(), + title: String.t(), + group: atom() | nil, + url: String.t() + } +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/refs.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/refs.ex new file mode 100644 index 0000000..ddf9173 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/refs.ex @@ -0,0 +1,191 @@ +defmodule ExDoc.Refs do + # A read-through cache of documentation references. + # + # A given ref is always associated with a module. If we don't have a ref + # in the cache we fetch the module's docs chunk and fill in the cache. + # + # If the module does not have the docs chunk, we fetch it's functions, + # callbacks and types from other sources. + @moduledoc false + + @typep entry() :: {ref(), visibility()} + + @typep ref() :: + {:module, module()} + | {kind(), module(), name :: atom(), arity()} + @typep kind() :: :function | :callback | :type + @typep visibility() :: :hidden | :public | :undefined | :partial + + @name __MODULE__ + + use GenServer + + @spec start_link(any()) :: GenServer.on_start() + def start_link(arg) do + GenServer.start_link(__MODULE__, arg, name: @name) + end + + @spec init(any()) :: {:ok, nil} + def init(_) do + :ets.new(@name, [:named_table, :public, :set]) + {:ok, nil} + end + + @spec clear() :: :ok + def clear() do + :ets.delete_all_objects(@name) + :ok + end + + @spec get_visibility(ref()) :: visibility() + def get_visibility(ref) do + case lookup(ref) do + {:ok, visibility} -> + visibility + + :error -> + case fetch(ref) do + {:ok, visibility} -> visibility + :error -> :undefined + end + end + end + + @spec insert([entry()]) :: :ok + def insert(entries) do + true = :ets.insert(@name, entries) + :ok + end + + @spec insert_from_chunk(module, tuple()) :: :ok + def insert_from_chunk(module, result) do + module + |> fetch_entries(result) + |> insert() + + :ok + end + + defp lookup(ref) do + case :ets.lookup(@name, ref) do + [{^ref, visibility}] -> {:ok, visibility} + [] -> :error + end + rescue + _ -> :error + end + + defp fetch({:module, module} = ref) do + insert_from_chunk(module, Code.fetch_docs(module)) + lookup(ref) + end + + defp fetch({_kind, module, _name, _arity} = ref) do + get_visibility({:module, module}) + lookup(ref) + end + + defp fetch_entries(module, result) do + case result do + {:docs_v1, _, _, _, module_doc, _, docs} -> + module_visibility = visibility(module_doc) + + [{{:module, module}, module_visibility}] ++ + for {{kind, name, arity}, _, _, doc, metadata} <- docs, + ref_kind = to_ref_kind(kind), + visibility = visibility(module_doc, {ref_kind, name, doc}), + arity <- (arity - (metadata[:defaults] || 0))..arity do + {{ref_kind, module, name, arity}, visibility} + end + + {:error, _reason} -> + with true <- :code.which(module) != :non_existing, + true <- Code.ensure_loaded?(module) do + # We say it is limited because the types may not actually be available in the beam file. + [{{:module, module}, :limited}] ++ + to_refs(exports(module), module, :function) ++ + to_refs(callbacks(module), module, :callback) ++ + to_refs(types(module, [:type, :opaque, :nominal]), module, :type) + else + _ -> + [{{:module, module}, :undefined}] + end + end + end + + defguardp has_no_docs(doc) when doc == :none or doc == %{} + + defp starts_with_underscore?(name), do: match?([?_ | _], Atom.to_charlist(name)) + + defp visibility(:hidden), + do: :hidden + + defp visibility(_module_doc), + do: :public + + defp visibility(_module_doc, {kind, _name, _doc}) + when kind not in [:callback, :function, :type], + do: raise(ArgumentError, "Unknown kind #{inspect(kind)}") + + defp visibility(:hidden, {_kind, _name, _doc}), + do: :hidden + + defp visibility(_, {_kind, _name, :hidden}), + do: :hidden + + defp visibility(_, {kind, name, doc}) when has_no_docs(doc) do + cond do + kind in [:callback, :type] -> + :public + + kind == :function and starts_with_underscore?(name) -> + :hidden + + kind == :function -> + :public + end + end + + defp visibility(_, {_, _, _}) do + :public + end + + defp to_ref_kind(:macro), do: :function + defp to_ref_kind(:macrocallback), do: :callback + defp to_ref_kind(other), do: other + + defp exports(module) do + if function_exported?(module, :__info__, 1) do + module.__info__(:functions) ++ module.__info__(:macros) + else + module.module_info(:exports) + end + end + + defp callbacks(module) do + if function_exported?(module, :behaviour_info, 1) do + module.behaviour_info(:callbacks) + else + [] + end + end + + defp types(module, kind_list) do + case Code.Typespec.fetch_types(module) do + {:ok, list} -> + for {kind, {name, _, args}} <- list, + kind in kind_list do + {name, length(args)} + end + + :error -> + [] + end + end + + defp to_refs(list, module, kind, visibility \\ :public) do + for {name, arity} <- list do + {{kind, module, name, arity}, visibility} + end + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/retriever.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/retriever.ex new file mode 100644 index 0000000..e66a859 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/retriever.ex @@ -0,0 +1,415 @@ +defmodule ExDoc.Retriever do + # Functions to extract documentation information from modules. + @moduledoc false + + defmodule Error do + @moduledoc false + defexception [:message] + end + + alias ExDoc.{Config, DocAST, Refs} + alias ExDoc.Retriever.Error + + @doc """ + Extract documentation from all modules in the specified directories. + + Returns a tuple containing `{modules, filtered}`, using `config.filter_modules` + as a filter criteria. + """ + @spec docs_from_dir([Path.t()], ExDoc.Config.t()) :: + {[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]} + def docs_from_dir(dirs, config) when is_list(dirs) do + dirs + |> Enum.reduce({[], []}, &docs_from_dir(&1, &2, config)) + |> sort_modules(config) + end + + defp docs_from_dir(dir, acc, config) do + files = Path.wildcard(Path.expand("*.beam", dir)) + + files + |> Enum.map(&filename_to_module/1) + |> docs_from_modules(acc, config) + end + + @doc """ + Extract documentation from all modules and returns a tuple containing + `{modules, filtered}`, two lists of modules that were extracted and filtered + by `config.filter_modules`, respectively. + """ + @spec docs_from_modules([atom], ExDoc.Config.t()) :: + {[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]} + def docs_from_modules(modules, config) when is_list(modules) do + modules |> docs_from_modules({[], []}, config) |> sort_modules(config) + end + + defp docs_from_modules(modules, acc, config) do + modules + |> Task.async_stream(&get_module(&1, config), timeout: :infinity) + |> Enum.reduce(acc, fn {:ok, result}, {modules, filtered} = acc -> + case result do + {:error, _module} -> + acc + + {:ok, module_node} -> + if config.filter_modules.(module_node.module, module_node.metadata), + do: {[module_node | modules], filtered}, + else: {modules, [module_node | filtered]} + end + end) + end + + defp sort_modules({modules, filtered}, config) do + {sort_modules(modules, config), sort_modules(filtered, config)} + end + + defp sort_modules(modules, config) when is_list(modules) do + Enum.sort_by(modules, fn module -> + {Config.index(config.groups_for_modules, module.group), module.nested_context, + module.nested_title, module.id} + end) + end + + defp filename_to_module(name) do + name = Path.basename(name, ".beam") + String.to_atom(name) + end + + defp get_module(module, config) do + with {:docs_v1, _, language, _, _, _metadata, _} = docs_chunk <- docs_chunk(module), + {:ok, language} <- ExDoc.Language.get(language, module), + %{} = module_data <- language.module_data(module, docs_chunk, config) do + {:ok, generate_node(module, module_data, config)} + else + _ -> + {:error, module} + end + end + + defp docs_chunk(module) do + result = Code.fetch_docs(module) + Refs.insert_from_chunk(module, result) + + case result do + {:docs_v1, _, _, _, :hidden, _, _} -> + false + + {:docs_v1, _, _, _, _, _, _} = docs -> + case Code.ensure_loaded(module) do + {:module, _} -> + docs + + {:error, reason} -> + ExDoc.warn("skipping docs for module #{inspect(module)}, reason: #{reason}", []) + false + end + + {:error, :chunk_not_found} -> + false + + {:error, :module_not_found} -> + unless Code.ensure_loaded?(module) do + raise Error, "module #{inspect(module)} is not defined/available" + end + + {:error, _} = error -> + raise Error, "error accessing #{inspect(module)}: #{inspect(error)}" + + _ -> + raise Error, + "unknown format in Docs chunk. This likely means you are running on " <> + "a more recent Elixir version that is not supported by ExDoc. Please update." + end + end + + defp generate_node(module, module_data, config) do + source = %{ + url_pattern: config.source_url_pattern, + path: module_data.source_file, + relative_path: path_relative_to_cwd(module_data.source_file) + } + + {doc_line, doc_file, format, source_doc, doc_ast, metadata} = + get_module_docs(module_data, source, config) + + group_for_doc = config.group_for_doc + annotations_for_docs = config.annotations_for_docs + + {docs, nodes_groups} = + get_docs(module_data, source, group_for_doc, annotations_for_docs, config) + + docs = ExDoc.Utils.natural_sort_by(docs, &"#{&1.name}/#{&1.arity}") + + moduledoc_groups = Map.get(metadata, :groups, []) + + docs_groups = + get_docs_groups( + moduledoc_groups ++ config.docs_groups ++ module_data.default_groups, + nodes_groups, + docs, + config + ) + + metadata = Map.put(metadata, :kind, module_data.type) + group = Config.match_module(config.groups_for_modules, module, module_data.id, metadata) + {nested_title, nested_context} = module_data.nesting_info || {nil, nil} + + %ExDoc.ModuleNode{ + id: module_data.id, + title: module_data.title, + nested_title: nested_title, + nested_context: nested_context, + group: group, + module: module, + type: module_data.type, + deprecated: metadata[:deprecated], + docs_groups: docs_groups, + doc: normalize_doc_ast(doc_ast, "module-"), + source_doc: source_doc, + source_format: format, + moduledoc_line: doc_line, + moduledoc_file: doc_file, + source_url: source_link(source, module_data.source_line), + language: module_data.language, + annotations: List.wrap(metadata[:tags]), + metadata: metadata + } + end + + defp doc_ast(format, %{"en" => doc_content}, options) do + DocAST.parse!(doc_content, format, options) + end + + defp doc_ast(_format, _, _options) do + nil + end + + defp normalize_doc_ast(doc_ast, prefix) do + doc_ast + |> DocAST.add_ids_to_headers([:h2, :h3], prefix) + end + + # Helpers + + defp get_module_docs(module_data, source, config) do + {:docs_v1, anno, _, format, moduledoc, metadata, _} = module_data.docs + doc_file = anno_file(anno, source) + doc_line = anno_line(anno) + options = [file: doc_file, line: doc_line + 1, markdown_processor: config.markdown_processor] + {doc_line, doc_file, format, moduledoc, doc_ast(format, moduledoc, options), metadata} + end + + defp get_docs(module_data, source, group_for_doc, annotations_for_docs, config) do + {:docs_v1, _, _, _, _, _, docs} = module_data.docs + + {nodes, groups} = + for doc <- docs, + doc_data = module_data.language.doc_data(doc, module_data) do + {_node, _group} = + get_doc(doc, doc_data, module_data, source, group_for_doc, annotations_for_docs, config) + end + |> Enum.unzip() + + {filter_defaults(nodes), groups} + end + + defp get_doc(doc, doc_data, module_data, source, group_for_doc, annotations_for_docs, config) do + {:docs_v1, _, _, content_type, _, module_metadata, _} = module_data.docs + {{type, name, arity}, anno, _signature, source_doc, metadata} = doc + doc_file = anno_file(anno, source) + doc_line = anno_line(anno) + + metadata = + Map.merge( + %{kind: type, name: name, arity: arity, module: module_data.module}, + metadata + ) + + source_url = source_link(doc_data.source_file, source, doc_data.source_line) + + annotations = + annotations_for_docs.(metadata) ++ + annotations_from_metadata(metadata, module_metadata) ++ doc_data.extra_annotations + + defaults = get_defaults(name, arity, Map.get(metadata, :defaults, 0)) + + doc_ast = + doc_ast(content_type, source_doc, + file: doc_file, + line: doc_line + 1, + markdown_processor: config.markdown_processor + ) || + doc_data.doc_fallback.() + + group = normalize_group(group_for_doc.(metadata) || doc_data.default_group) + id = doc_data.id_key <> nil_or_name(name, arity) + + doc_node = %ExDoc.DocNode{ + id: id, + name: name, + arity: arity, + deprecated: metadata[:deprecated], + doc: normalize_doc_ast(doc_ast, id <> "-"), + source_doc: source_doc, + doc_line: doc_line, + doc_file: doc_file, + defaults: ExDoc.Utils.natural_sort_by(defaults, fn {name, arity} -> "#{name}/#{arity}" end), + signature: signature(doc_data.signature), + source_specs: doc_data.specs, + source_url: source_url, + type: doc_data.type, + group: group.title, + annotations: annotations + } + + {doc_node, group} + end + + defp get_defaults(_name, _arity, 0), do: [] + + defp get_defaults(name, arity, defaults) do + for default <- (arity - defaults)..(arity - 1), do: {name, default} + end + + defp filter_defaults(nodes) do + Enum.map(nodes, &filter_defaults(&1, nodes)) + end + + defp filter_defaults(node, nodes) do + update_in(node.defaults, fn defaults -> + Enum.reject(defaults, fn {name, arity} -> + Enum.any?(nodes, &match?(%{name: ^name, arity: ^arity}, &1)) + end) + end) + end + + defp get_docs_groups(module_groups, nodes_groups, doc_nodes, config) do + module_groups = Enum.map(module_groups, &normalize_group/1) + + nodes_groups_descriptions = Map.new(nodes_groups, &{&1.title, &1.description}) + + # Doc nodes already have normalized groups + nodes_groups = ExDoc.Utils.natural_sort_by(nodes_groups, & &1.title) + normal_groups = module_groups ++ nodes_groups + nodes_by_group_title = Enum.group_by(doc_nodes, & &1.group) + + {docs_groups, _} = + Enum.flat_map_reduce(normal_groups, %{}, fn + group, seen when is_map_key(seen, group.title) -> + {[], seen} + + group, seen -> + seen = Map.put(seen, group.title, true) + + case Map.get(nodes_by_group_title, group.title, []) do + [] -> + {[], seen} + + child_nodes -> + group = finalize_group(group, child_nodes, nodes_groups_descriptions, config) + {[group], seen} + end + end) + + docs_groups + end + + defp finalize_group(group, doc_nodes, description_fallbacks, config) do + description = + case group.description do + nil -> Map.get(description_fallbacks, group.title) + text -> text + end + + doc_ast = + case description do + nil -> + nil + + text -> + doc_ast = + doc_ast("text/markdown", %{"en" => text}, + markdown_processor: config.markdown_processor + ) + + sub_id = ExDoc.Utils.text_to_id(group.title) + normalize_doc_ast(doc_ast, "group-#{sub_id}-") + end + + %ExDoc.DocGroupNode{ + title: group.title, + description: description, + doc: doc_ast, + docs: doc_nodes + } + end + + ## General helpers + + defp nil_or_name(name, arity) do + if name == nil do + "nil/#{arity}" + else + "#{name}/#{arity}" + end + end + + defp signature(list) when is_list(list), do: Enum.join(list, " ") + + defp annotations_from_metadata(metadata, module_metadata) do + # Give precedence to the function/callback/type metadata over the module metadata. + cond do + since = metadata[:since] -> ["since #{since}"] + since = module_metadata[:since] -> ["since #{since}"] + true -> [] + end + end + + defp anno_line(line) when is_integer(line), do: abs(line) + defp anno_line(anno), do: anno |> :erl_anno.line() |> abs() + + defp anno_file(anno, source) do + case :erl_anno.file(anno) do + :undefined -> + source.relative_path + + file -> + source.path + |> Path.dirname() + |> Path.join(file) + |> path_relative_to_cwd() + end + end + + # TODO: Remove when we require Elixir 1.16 + if function_exported?(Path, :relative_to_cwd, 2) do + defp path_relative_to_cwd(path), do: Path.relative_to_cwd(path, force: true) + else + defp path_relative_to_cwd(path), do: Path.relative_to_cwd(path) + end + + defp source_link(nil, source, line), do: source_link(source, line) + + defp source_link(file, %{url_pattern: url_pattern}, line) do + url_pattern.(path_relative_to_cwd(file), line) + end + + defp source_link(%{url_pattern: url_pattern, relative_path: path}, line) do + url_pattern.(path, line) + end + + defp normalize_group(group) do + case group do + %{title: title, description: description} + when is_binary(title) and (is_binary(description) or is_nil(description)) -> + %{group | title: title, description: description} + + kw when is_list(kw) -> + true = Keyword.keyword?(kw) + %{title: to_string(Keyword.fetch!(kw, :title)), description: kw[:description]} + + title when is_binary(title) when is_atom(title) -> + %{title: to_string(title), description: nil} + end + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/shell_lexer.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/shell_lexer.ex new file mode 100644 index 0000000..c55824e --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/shell_lexer.ex @@ -0,0 +1,43 @@ +defmodule ExDoc.ShellLexer do + # Makeup lexer for sh, bash, etc commands. + # The only thing it does is making the `$ ` prompt not selectable. + @moduledoc false + + @behaviour Makeup.Lexer + + @impl true + def lex(text, _opts) do + text + |> String.split("\n") + |> Enum.flat_map(fn + "$ " <> rest -> + [ + {:generic_prompt, %{selectable: false}, "$ "}, + {:text, %{}, rest <> "\n"} + ] + + text -> + [{:text, %{}, text <> "\n"}] + end) + end + + @impl true + def match_groups(_arg0, _arg1) do + raise "not implemented yet" + end + + @impl true + def postprocess(_arg0, _arg1) do + raise "not implemented yet" + end + + @impl true + def root(_arg0) do + raise "not implemented yet" + end + + @impl true + def root_element(_arg0) do + raise "not implemented yet" + end +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/utils.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/utils.ex new file mode 100644 index 0000000..44571a0 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/utils.ex @@ -0,0 +1,159 @@ +defmodule ExDoc.Utils do + # General helpers used throughout ExDoc or extracted for testing. + # Avoid adding functions to this module whenever possible, + # instead prefer defining modules closer to the context they are used. + @moduledoc false + + @doc """ + HTML escapes the given string. + + iex> ExDoc.Utils.h("") + "<foo>" + + """ + def h(string) do + String.replace(string, ~w|& < > "|, fn + "&" -> "&" + "<" -> "<" + ">" -> ">" + ~S(") -> """ + end) + end + + @doc """ + Strips HTML tags from text leaving their text content + """ + def strip_tags(text, replace_with \\ "") when is_binary(text) do + clean_html_regex = ~r/<\/?\s*[a-zA-Z]+(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>/ + String.replace(text, clean_html_regex, replace_with) + end + + @doc """ + Generates an ID from some text. + + Used primarily with titles, headings, and functions group names. + """ + def text_to_id(atom) when is_atom(atom), do: text_to_id(Atom.to_string(atom)) + + def text_to_id(text) when is_binary(text) do + text + |> String.replace(~r/&#\d+;/, "") + |> String.replace(~r/&[A-Za-z0-9]+;/, "") + |> String.replace(~r/\W+/u, "-") + |> String.trim("-") + |> String.downcase() + end + + @doc """ + Sorts mapped strings by natural order. + """ + def natural_sort_by(enumerable, mapper) when is_function(mapper, 1) do + Enum.sort_by(enumerable, fn elem -> + elem + |> mapper.() + |> to_sortable_charlist() + end) + end + + defp to_sortable_charlist(string) do + string + |> :unicode.characters_to_nfkd_list() + |> make_sortable() + end + + @offset -1_000_000_000 + + # Numbers come first, so group and pad them with offset + defp make_sortable([digit | chars]) when digit in ?0..?9 do + {digits, chars} = Enum.split_while(chars, &(&1 in ?0..?9)) + [@offset + List.to_integer([digit | digits]) | make_sortable(chars)] + end + + # Then Elixir special punctuation - trailing bang `!` + defp make_sortable([?! | chars]), do: [?0 | make_sortable(chars)] + + # Then Elixir special punctuation - question mark `?` + defp make_sortable([?? | chars]), do: [?1 | make_sortable(chars)] + + # Then underscore + defp make_sortable([?_ | chars]), do: [?2 | make_sortable(chars)] + + # Then uppercased letters and lowercased letters + defp make_sortable([char | chars]) when char in ?a..?z do + [char - 31.5 | make_sortable(chars)] + end + + defp make_sortable([char | chars]), do: [char | make_sortable(chars)] + defp make_sortable([]), do: [] + + @doc """ + A very simple JSON encoder. + + We want to minimize the number of dependencies ExDoc has, + because we don't want someone to be allowed to not upgrade + their app due to an ExDoc restriction, so we ship with a + simple JSON implementation. + """ + # TODO: Remove this once we require Elixir v1.20+ + def to_json(nil), do: "null" + def to_json(true), do: "true" + def to_json(false), do: "false" + + def to_json(map) when is_map(map) do + mapped = + Enum.map_intersperse(Map.to_list(map), ?,, fn {key, value} -> + [key |> Atom.to_string() |> inspect(), ?:, to_json(value)] + end) + + [?{, mapped, ?}] + end + + def to_json(list) when is_list(list) do + mapped = Enum.map_intersperse(list, ?,, &to_json/1) + [?[, mapped, ?]] + end + + def to_json(atom) when is_atom(atom) do + atom |> Atom.to_string() |> inspect() + end + + def to_json(binary) when is_binary(binary) do + to_json_string(binary, "\"") + end + + def to_json(integer) when is_integer(integer) do + Integer.to_string(integer) + end + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc) when x <= 0x000F, + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc) when x <= 0x001F, + do: to_json_string(rest, <>) + + defp to_json_string(<>, acc), + do: to_json_string(rest, <>) + + defp to_json_string(<<>>, acc), do: <> +end diff --git a/plugins/mob_iap/deps/ex_doc/lib/mix/tasks/docs.ex b/plugins/mob_iap/deps/ex_doc/lib/mix/tasks/docs.ex new file mode 100644 index 0000000..fcef373 --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/lib/mix/tasks/docs.ex @@ -0,0 +1,333 @@ +defmodule Mix.Tasks.Docs do + use Mix.Task + + @shortdoc "Generate documentation for the project" + @requirements ["compile"] + + @moduledoc ~S""" + Uses ExDoc to generate a static web page from the project documentation. + + ## Command line options + + * `--canonical`, `-n` - Indicate the preferred URL with + `rel="canonical"` link element, defaults to no canonical path + + * `--formatter`, `-f` - Which formatters to use, `html`, + `epub`, or `markdown`. This option can be given more than once. By default, + `html`, `epub`, and `markdown` are generated. + + * `--language` - Specifies the language to annotate the + EPUB output in valid [BCP 47](https://tools.ietf.org/html/bcp47) + + * `--open` - open browser window pointed to the documentation + + * `--output`, `-o` - Output directory for the generated + docs, default: `"doc"` + + * `--proglang` - Chooses the main programming language: `elixir` + or `erlang` + + * `--warnings-as-errors` - Exits with non-zero exit code if any warnings are found + + The command line options have higher precedence than the options + specified in your `mix.exs` file below. + + ## Configuration + + ExDoc will automatically pull in information from your project, + like the application and version. However, you may want to set + `:name`, `:source_url` and `:homepage_url` to have a nicer output + from ExDoc, for example: + + def project do + [ + app: :my_app, + version: "0.1.0-dev", + deps: deps(), + + # Docs + name: "My App", + source_url: "https://github.com/USER/PROJECT", + homepage_url: "http://YOUR_PROJECT_HOMEPAGE", + docs: [ + main: "MyApp", # The main page in the docs + favicon: "path/to/favicon.png", + logo: "path/to/logo.png", + extras: ["README.md"] + ] + ] + end + + ExDoc also allows configuration specific to the documentation to + be set. The following options should be put under the `:docs` key + in your project's main configuration. The `:docs` options should + be a keyword list or a function returning a keyword list that will + be lazily executed. See all supported options in `ExDoc.generate/4`. + + ## Umbrella project + + ExDoc can be used in an umbrella project and generates a single documentation + for all child apps. You can use the `:ignore_apps` configuration to exclude + certain projects in the umbrella from documentation. + + Generating documentation per each child app can be achieved by running: + + mix cmd mix docs + + See `mix help cmd` for more information. + """ + + @switches [ + canonical: :string, + formatter: :keep, + language: :string, + open: :boolean, + output: :string, + proglang: :string, + warnings_as_errors: :boolean + ] + + @aliases [ + f: :formatter, + n: :canonical, + o: :output + ] + + @doc false + def run(args, config \\ Mix.Project.config(), generator \\ &ExDoc.generate/4) do + {:ok, _} = Application.ensure_all_started(:ex_doc) + + unless Code.ensure_loaded?(ExDoc.Formatter.Config) do + Mix.raise( + "Could not load ExDoc configuration. Please make sure you are running the " <> + "docs task in the same Mix environment it is listed in your deps" + ) + end + + {cli_opts, args, _} = OptionParser.parse(args, aliases: @aliases, switches: @switches) + + if args != [] do + Mix.raise("Extraneous arguments on the command line") + end + + project = + to_string( + config[:name] || config[:app] || + Mix.raise("expected :name or :app to be found in the project definition in mix.exs") + ) + + version = config[:version] || "dev" + + cli_opts = + Keyword.update(cli_opts, :proglang, :elixir, fn proglang -> + if proglang not in ~w(erlang elixir) do + Mix.raise("--proglang must be elixir or erlang") + end + + String.to_atom(proglang) + end) + + options = + config + |> get_docs_opts() + |> Keyword.merge(cli_opts) + # accepted at root level config + |> normalize_source_url(config) + # accepted at root level config + |> normalize_homepage_url(config) + # accepted at root level config + |> normalize_description(config) + |> normalize_apps(config) + |> normalize_main() + |> normalize_deps() + |> normalize_formatters() + |> put_package(config) + + source_beams = source_beams(options, config) + Code.prepend_path(source_beams) + + for path <- Keyword.get_values(options, :paths), + path <- Path.wildcard(path) do + Code.prepend_path(path) + end + + Mix.shell().info("Generating docs...") + generated_docs = generator.(project, version, source_beams, options) + + Enum.each(generated_docs, fn %{entrypoint: entrypoint, formatter: formatter} -> + extension = formatter_module_to_extension(formatter) + Mix.shell().info([:green, "View #{extension} docs at #{inspect(entrypoint)}"]) + end) + + open? = Keyword.get(cli_opts, :open, false) + + with [%{entrypoint: entrypoint} | _] when open? <- generated_docs do + browser_open(entrypoint) + end + + warned = Enum.filter(generated_docs, & &1.warned?) + + if options[:warnings_as_errors] == true and warned != [] do + formatters = Enum.map(warned, &formatter_module_to_extension(&1.formatter)) + + format_message = + case formatters do + [formatter] -> "#{formatter} format" + _ -> "#{Enum.join(formatters, ", ")} formats" + end + + message = + "Documents have been generated, but generation for #{format_message} failed " <> + "due to warnings while using the --warnings-as-errors option" + + message_formatted = IO.ANSI.format([:red, message, :reset]) + IO.puts(:stderr, message_formatted) + exit({:shutdown, 1}) + else + Enum.map(generated_docs, & &1.entrypoint) + end + end + + defp formatter_module_to_extension(module) do + module |> Module.split() |> List.last() |> String.downcase() + end + + defp normalize_formatters(options) do + formatters = + case Keyword.get_values(options, :formatter) do + [] -> options[:formatters] || ["html", "markdown", "epub"] + values -> values + end + + options + |> Keyword.delete(:formatter) + |> Keyword.put(:formatters, formatters) + end + + defp get_docs_opts(config) do + docs = config[:docs] + + cond do + is_function(docs, 0) -> docs.() + is_nil(docs) -> [] + true -> docs + end + end + + defp normalize_source_url(options, config) do + if source_url = config[:source_url] do + Keyword.put(options, :source_url, source_url) + else + options + end + end + + defp normalize_homepage_url(options, config) do + if homepage_url = config[:homepage_url] do + Keyword.put(options, :homepage_url, homepage_url) + else + options + end + end + + defp normalize_description(options, config) do + if description = config[:description] do + Keyword.put(options, :description, description) + else + options + end + end + + defp source_beams(options, config) do + if Mix.Project.umbrella?(config) do + umbrella_compile_paths(Keyword.get(options, :ignore_apps, [])) + else + [Mix.Project.compile_path()] + end + end + + defp umbrella_compile_paths(ignored_apps) do + build = Mix.Project.build_path() + + for {app, _} <- Mix.Project.apps_paths(), + app not in ignored_apps do + Path.join([build, "lib", Atom.to_string(app), "ebin"]) + end + end + + defp normalize_apps(options, config) do + if Mix.Project.umbrella?(config) do + ignore = Keyword.get(options, :ignore_apps, []) + + apps = + for {app, _} <- Mix.Project.apps_paths(), app not in ignore do + app + end + + Keyword.put(options, :apps, Enum.sort(apps)) + else + Keyword.put(options, :apps, List.wrap(config[:app])) + end + end + + defp normalize_main(options) do + main = options[:main] + + cond do + is_nil(main) -> + Keyword.delete(options, :main) + + is_atom(main) -> + Keyword.put(options, :main, inspect(main)) + + is_binary(main) -> + options + end + end + + defp normalize_deps(options) do + user_deps = Keyword.get(options, :deps, []) + + deps = + for {app, doc} <- Keyword.merge(get_deps(), user_deps), + lib_dir = :code.lib_dir(app), + is_list(lib_dir), + do: {app, doc} + + Keyword.put(options, :deps, deps) + end + + defp get_deps do + for {key, _} <- Mix.Project.deps_paths(), + _ = Application.load(key), + vsn = Application.spec(key, :vsn) do + {key, "https://hexdocs.pm/#{key}/#{vsn}/"} + end + end + + defp put_package(options, config) do + if package = config[:package] do + Keyword.put(options, :package, package[:name] || config[:app]) + else + options + end + end + + defp browser_open(path) do + {cmd, args, options} = + case :os.type() do + {:win32, _} -> + dirname = Path.dirname(path) + basename = Path.basename(path) + {"cmd", ["/c", "start", basename], [cd: dirname]} + + {:unix, :darwin} -> + {"open", [path], []} + + {:unix, _} -> + {"xdg-open", [path], []} + end + + System.cmd(cmd, args, options) + end +end diff --git a/plugins/mob_iap/deps/ex_doc/mix.exs b/plugins/mob_iap/deps/ex_doc/mix.exs new file mode 100644 index 0000000..6e2eb7f --- /dev/null +++ b/plugins/mob_iap/deps/ex_doc/mix.exs @@ -0,0 +1,166 @@ +defmodule ExDoc.Mixfile do + use Mix.Project + + @source_url "https://github.com/elixir-lang/ex_doc" + @version "0.40.3" + + def project do + [ + app: :ex_doc, + version: @version, + elixir: "~> 1.15", + deps: deps(), + aliases: aliases(), + package: package(), + escript: escript(), + elixirc_paths: elixirc_paths(Mix.env()), + source_url: @source_url, + test_elixirc_options: [docs: true, debug_info: true], + test_ignore_filters: [&String.starts_with?(&1, "test/fixtures/")], + name: "ExDoc", + description: "ExDoc is a documentation generation tool for Elixir", + docs: docs() + ] + end + + def cli do + [preferred_envs: ["hex.publish": :prod]] + end + + def application do + [ + extra_applications: [:eex] ++ extra_applications(Mix.env()), + mod: {ExDoc.Application, []} + ] + end + + defp extra_applications(:test), do: [:edoc, :xmerl] + defp extra_applications(_), do: [] + + defp deps do + [ + {:earmark_parser, "~> 1.4.44"}, + {:makeup_elixir, "~> 0.14 or ~> 1.0"}, + {:makeup_erlang, "~> 0.1 or ~> 1.0"}, + # Add other makeup lexers as optional for the executable + {:makeup_c, ">= 0.1.0", optional: true}, + {:makeup_html, ">= 0.1.0", optional: true}, + {:jason, "~> 1.2", only: :test}, + {:lazy_html, "~> 0.1.0", only: :test} + ] + end + + defp aliases do + [ + build: ["cmd --cd assets npm run build", "compile --force", &docs/1], + clean: [&clean_test_fixtures/1, "clean"], + fix: ["format", "cmd --cd assets npm run lint:fix"], + lint: ["format --check-formatted", "cmd --cd assets npm run lint"], + setup: ["deps.get", "cmd --cd assets npm install"] + ] + end + + defp package do + [ + licenses: ["Apache-2.0"], + maintainers: ["José Valim", "Milton Mazzarri", "Wojtek Mach"], + files: ~w(CHANGELOG.md Cheatsheet.cheatmd formatters lib LICENSE mix.exs README.md), + links: %{ + "GitHub" => @source_url, + "Changelog" => "https://hexdocs.pm/ex_doc/changelog.html", + "Writing documentation" => "https://hexdocs.pm/elixir/writing-documentation.html" + } + ] + end + + defp escript do + [ + main_module: ExDoc.CLI + ] + end + + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + + defp docs do + if Mix.env() == :dev do + [ + search: [ + %{ + name: "ExDoc + Elixir", + help: "Search latest ExDoc + Elixir", + packages: [:ex_doc, elixir: "main"] + }, + %{ + name: "ExDoc on Google", + help: "Search everything about ExDoc on Google", + url: "https://google.com/?q=" + }, + %{name: "Lunr", help: "Search using Lunr in browser"} + ] + ] + else + [] + end ++ + [ + main: "readme", + extras: + [ + "README.md", + "Cheatsheet.cheatmd", + "CHANGELOG.md" + ] ++ test_dev_examples(Mix.env()), + source_ref: "v#{@version}", + source_url: @source_url, + groups_for_modules: [ + Markdown: [ + ExDoc.Markdown, + ExDoc.Markdown.Earmark + ], + Formatter: [ + ExDoc.Formatter, + ExDoc.Formatter.Config + ], + Nodes: [ + ExDoc.ModuleNode, + ExDoc.DocNode, + ExDoc.DocGroupNode, + ExDoc.ExtraNode, + ExDoc.URLNode + ] + ], + groups_for_extras: [ + Examples: ~r"test/examples" + ], + skip_undefined_reference_warnings_on: [ + "CHANGELOG.md" + ] + ] + end + + defp docs(args) do + Mix.Task.run("docs", args) + {text_tags, 0} = System.cmd("git", ["tag"]) + + versions = + for("v" <> rest <- String.split(text_tags), do: Version.parse!(rest)) + |> Enum.sort({:desc, Version}) + + list_contents = + Enum.map_intersperse(versions, ", ", fn version -> + string = Version.to_string(version) + ~s[{"version":"v#{string}", "url":"https://hexdocs.pm/ex_doc/#{string}"}] + end) + + File.write!("doc/docs_config.js", """ + var versionNodes = [#{list_contents}]; + """) + end + + defp test_dev_examples(:dev), do: Path.wildcard("test/examples/*") + defp test_dev_examples(_), do: [] + + defp clean_test_fixtures(_args) do + File.rm_rf("test/tmp") + end +end diff --git a/plugins/mob_iap/deps/jason/.hex b/plugins/mob_iap/deps/jason/.hex new file mode 100644 index 0000000000000000000000000000000000000000..b7e0a46f02623131946948ed3f86d62a31d7cd9e GIT binary patch literal 261 zcmZ9H%WlIU5JlZMeXQ~+(EtO6UH_zFhH>qr;K)u&_WkkFEW0~8I=a&RnRZiO`$_I` z7rSfMbuV>aVwT+S`4qRM)YorWw|OZKrH**C^)1H|Kb!t)*;DQFd_UF0T#Bq8JPDt- zTmH-6wU`LtZ2$uDX)xZi1|5uq#2k1uVv|EXCLNVRK_()r#Ri)KLac}7cKts}z+RgW toB~appfMTNh$sYr5NOb&iwc2Nut4ky?U1~2WWaf>`&BN><4!w%$-j` tags). + + If you were not using the `html_safe` option, you are not affected. + + Affected versions: < 1.2.1 + Patched versions: >= 1.2.1 + +## 1.2.0 (17.03.2020) + +### Enhancements + +* Add `Jason.Encode.keyword/2` + ([cb1f26a](https://github.com/michalmuskala/jason/commit/cb1f26a)). + +### Bug fixes + +* Fix `Jason.Helpers.json_map/1` value expansion + ([70b046a](https://github.com/michalmuskala/jason/commit/70b046a)). + +## 1.1.2 (19.10.2018) + +### Bug fixes + +* correctly handle the `pretty: false` option + ([ba318c8](https://github.com/michalmuskala/jason/commit/ba318c8)). + +## 1.1.1 (10.07.2018) + +### Bug fixes + +* correctly handle escape sequences in strings when pretty printing + ([794bbe4](https://github.com/michalmuskala/jason/commit/794bbe4)). + +## 1.1.0 (02.07.2018) + +### Enhancements + +* pretty-printing support through `Jason.Formatter` and `pretty: true` option + in `Jason.encode/2` ([d758e36](https://github.com/michalmuskala/jason/commit/d758e36)). + +### Bug fixes + +* silence variable warnings for fields with underscores used during deriving + ([88dd85c](https://github.com/michalmuskala/jason/commit/88dd85c)). +* **potential incompatibility** don't raise `Protocol.UndefinedError` in non-bang functions + ([ad0f57b](https://github.com/michalmuskala/jason/commit/ad0f57b)). + +## 1.0.1 (02.07.2018) + +### Bug fixes + +* fix `Jason.Encode.escape` type ([a57b430](https://github.com/michalmuskala/jason/commit/a57b430)) +* multiple documentation improvements + +## 1.0.0 (26.01.2018) + +No changes + +## 1.0.0-rc.3 (26.01.2018) + +### Changes + +* update `escape` option of `Jason.encode/2` to take values: + `:json | :unicode_safe | :html_safe | :javascript_safe` for consistency. Old values of + `:unicode` and `:javascript` are still supported for compatibility with Poison. + ([f42dcbd](https://github.com/michalmuskala/jason/commit/f42dcbd)) + +## 1.0.0-rc.2 (07.01.2018) + +### Bug fixes + +* add type for `strings` option ([b459ee4](https://github.com/michalmuskala/jason/commit/b459ee4)) +* support iodata in `decode!` ([a1f3456](https://github.com/michalmuskala/jason/commit/a1f3456)) + +## 1.0.0-rc.1 (22.12.2017) + +* Initial release diff --git a/plugins/mob_iap/deps/jason/LICENSE b/plugins/mob_iap/deps/jason/LICENSE new file mode 100644 index 0000000..f22c0ba --- /dev/null +++ b/plugins/mob_iap/deps/jason/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2017-present Michał Muskała + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/plugins/mob_iap/deps/jason/README.md b/plugins/mob_iap/deps/jason/README.md new file mode 100644 index 0000000..7db1eaf --- /dev/null +++ b/plugins/mob_iap/deps/jason/README.md @@ -0,0 +1,157 @@ +# Jason [![Hex Version](https://img.shields.io/hexpm/v/jason.svg)](https://hex.pm/packages/jason) [![Hex Docs](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/jason/) + +A blazing fast JSON parser and generator in pure Elixir. + +The parser and generator are at least twice as fast as other Elixir/Erlang libraries +(most notably `Poison`). +The performance is comparable to `jiffy`, which is implemented in C as a NIF. +Jason is usually only twice as slow. + +Both parser and generator fully conform to +[RFC 8259](https://tools.ietf.org/html/rfc8259) and +[ECMA 404](https://ecma-international.org/publications-and-standards/standards/ecma-404/) +standards. The parser is tested using [JSONTestSuite](https://github.com/nst/JSONTestSuite). + +## Installation + +The package can be installed by adding `jason` to your list of dependencies +in `mix.exs`: + +```elixir +def deps do + [{:jason, "~> 1.4"}] +end +``` + +## Basic Usage + +``` elixir +iex(1)> Jason.encode!(%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"}) +"{\"age\":44,\"name\":\"Steve Irwin\",\"nationality\":\"Australian\"}" + +iex(2)> Jason.decode!(~s({"age":44,"name":"Steve Irwin","nationality":"Australian"})) +%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"} +``` + +Full documentation can be found at [https://hexdocs.pm/jason](https://hexdocs.pm/jason). + +## Use with other libraries + +### Postgrex + +Versions starting at 0.14.0 use `Jason` by default. For earlier versions, please refer to +[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#postgrex). + +### Ecto + +Versions starting at 3.0.0 use `Jason` by default. For earlier versions, please refer to +[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#ecto). + +### Plug (and Phoenix) + +Phoenix starting at 1.4.0 uses `Jason` by default. For earlier versions, please refer to +[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#plug-and-phoenix). + +### Absinthe + +You need to pass the `:json_codec` option to `Absinthe.Plug` + +```elixir +# When called directly: +plug Absinthe.Plug, + schema: MyApp.Schema, + json_codec: Jason + +# When used in phoenix router: +forward "/api", + to: Absinthe.Plug, + init_opts: [schema: MyApp.Schema, json_codec: Jason] +``` + +## Benchmarks + +Detailed benchmarks (including memory measurements): +https://gist.github.com/michalmuskala/4d64a5a7696ca84ac7c169a0206640d5 + +HTML reports for the benchmark (only performance measurements): +http://michal.muskala.eu/jason/decode.html and http://michal.muskala.eu/jason/encode.html + +### Running + +Benchmarks against most popular Elixir & Erlang json libraries can be executed after +going into the `bench/` folder and then executing `mix bench.encode` and `mix bench.decode`. +A HTML report of the benchmarks (after their execution) can be found in +`bench/output/encode.html` and `bench/output/decode.html` respectively. + +## Differences to Poison + +Jason has a couple feature differences compared to Poison. + + * Jason follows the JSON spec more strictly, for example it does not allow + unescaped newline characters in JSON strings - e.g. `"\"\n\""` will + produce a decoding error. + * no support for decoding into data structures (the `as:` option). + * no built-in encoders for `MapSet`, `Range` and `Stream`. + * no support for encoding arbitrary structs - explicit implementation + of the `Jason.Encoder` protocol is always required. + * different pretty-printing customisation options (default `pretty: true` works the same) + +### Encoders + +If you require encoders for any of the unsupported collection types, I suggest +adding the needed implementations directly to your project: + +```elixir +defimpl Jason.Encoder, for: [MapSet, Range, Stream] do + def encode(struct, opts) do + Jason.Encode.list(Enum.to_list(struct), opts) + end +end +``` + +If you need to encode some struct that does not implement the protocol, +if you own the struct, you can derive the implementation specifying +which fields should be encoded to JSON: + +```elixir +@derive {Jason.Encoder, only: [....]} +defstruct # ... +``` + +It is also possible to encode all fields, although this should be +used carefully to avoid accidentally leaking private information +when new fields are added: + +```elixir +@derive Jason.Encoder +defstruct # ... +``` + +Finally, if you don't own the struct you want to encode to JSON, +you may use `Protocol.derive/3` placed outside of any module: + +```elixir +Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) +Protocol.derive(Jason.Encoder, NameOfTheStruct) +``` + +## Injecting an already encoded JSON inside a to-be-encoded structure + +If parts of the to-be-encoded structure are already JSON-encoded, you can +use `Jason.Fragment` to mark the parts as already encoded, and avoid a +decoding/encoding roundtrip. + +```elixir +already_encoded_json = Jason.encode!(%{hello: "world"}) +Jason.encode!(%{foo: Jason.Fragment.new(already_encoded_json)}) +``` + +This feature is especially useful if you need to cache a part of the JSON, +or if it is already provided by another system (e.g. `jsonb_agg` with Postgres). + +## License + +Jason is released under the Apache License 2.0 - see the [LICENSE](LICENSE) file. + +Some elements of tests and benchmarks have their origins in the +[Poison library](https://github.com/devinus/poison) and were initially licensed under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/plugins/mob_iap/deps/jason/hex_metadata.config b/plugins/mob_iap/deps/jason/hex_metadata.config new file mode 100644 index 0000000..60f4ac0 --- /dev/null +++ b/plugins/mob_iap/deps/jason/hex_metadata.config @@ -0,0 +1,21 @@ +{<<"links">>,[{<<"GitHub">>,<<"https://github.com/michalmuskala/jason">>}]}. +{<<"name">>,<<"jason">>}. +{<<"version">>,<<"1.4.5">>}. +{<<"description">>, + <<"A blazing fast JSON parser and generator in pure Elixir.">>}. +{<<"elixir">>,<<"~> 1.4">>}. +{<<"app">>,<<"jason">>}. +{<<"licenses">>,[<<"Apache-2.0">>]}. +{<<"requirements">>, + [[{<<"name">>,<<"decimal">>}, + {<<"app">>,<<"decimal">>}, + {<<"optional">>,true}, + {<<"requirement">>,<<"~> 1.0 or ~> 2.0 or ~> 3.0">>}, + {<<"repository">>,<<"hexpm">>}]]}. +{<<"files">>, + [<<"lib">>,<<"lib/jason.ex">>,<<"lib/encoder.ex">>,<<"lib/decoder.ex">>, + <<"lib/ordered_object.ex">>,<<"lib/formatter.ex">>,<<"lib/encode.ex">>, + <<"lib/codegen.ex">>,<<"lib/helpers.ex">>,<<"lib/sigil.ex">>, + <<"lib/fragment.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>, + <<"CHANGELOG.md">>]}. +{<<"build_tools">>,[<<"mix">>]}. diff --git a/plugins/mob_iap/deps/jason/lib/codegen.ex b/plugins/mob_iap/deps/jason/lib/codegen.ex new file mode 100644 index 0000000..2d7e464 --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/codegen.ex @@ -0,0 +1,139 @@ +defmodule Jason.Codegen do + @moduledoc false + + alias Jason.{Encode, EncodeError} + + def jump_table(ranges, default) do + ranges + |> ranges_to_orddict() + |> :array.from_orddict(default) + |> :array.to_orddict() + end + + def jump_table(ranges, default, max) do + ranges + |> ranges_to_orddict() + |> :array.from_orddict(default) + |> resize(max) + |> :array.to_orddict() + end + + defmacro bytecase(var, do: clauses) do + {ranges, default, literals} = clauses_to_ranges(clauses, [], __CALLER__) + + jump_table = jump_table(ranges, default) + + quote do + case unquote(var) do + unquote(jump_table_to_clauses(jump_table, literals)) + end + end + end + + defmacro bytecase(var, max, do: clauses) do + {ranges, default, empty} = clauses_to_ranges(clauses, [], __CALLER__) + + jump_table = jump_table(ranges, default, max) + + quote do + case unquote(var) do + unquote(jump_table_to_clauses(jump_table, empty)) + end + end + end + + def build_kv_iodata(kv, encode_args) do + elements = + kv + |> Enum.map(&encode_pair(&1, encode_args)) + |> Enum.intersperse(",") + + collapse_static(List.flatten(["{", elements] ++ ~c'}')) + end + + defp clauses_to_ranges([{:->, _, [[{:in, _, [byte, range]}, rest], action]} | tail], acc, env) do + range = Macro.expand(range, env) + clauses_to_ranges(tail, [{range, {byte, rest, action}} | acc], env) + end + + defp clauses_to_ranges([{:->, _, [[default, rest], action]} | tail], acc, _env) do + {Enum.reverse(acc), {default, rest, action}, literal_clauses(tail)} + end + + defp literal_clauses(clauses) do + Enum.map(clauses, fn {:->, _, [[literal], action]} -> + {literal, action} + end) + end + + defp jump_table_to_clauses([{val, {{:_, _, _}, rest, action}} | tail], empty) do + quote do + <> -> + unquote(action) + end ++ jump_table_to_clauses(tail, empty) + end + + defp jump_table_to_clauses([{val, {byte, rest, action}} | tail], empty) do + quote do + <> when unquote(byte) === unquote(val) -> + unquote(action) + end ++ jump_table_to_clauses(tail, empty) + end + + defp jump_table_to_clauses([], literals) do + Enum.flat_map(literals, fn {pattern, action} -> + quote do + unquote(pattern) -> + unquote(action) + end + end) + end + + defp resize(array, size), do: :array.resize(size, array) + + defp ranges_to_orddict(ranges) do + ranges + |> Enum.flat_map(fn + {int, value} when is_integer(int) -> + [{int, value}] + + {enum, value} -> + Enum.map(enum, &{&1, value}) + end) + |> :orddict.from_list() + end + + defp encode_pair({key, value}, encode_args) do + key = IO.iodata_to_binary(Encode.key(key, &escape_key/1)) + key = "\"" <> key <> "\":" + [key, quote(do: Encode.value(unquote(value), unquote_splicing(encode_args)))] + end + + defp escape_key(binary) do + check_safe_key!(binary) + binary + end + + defp check_safe_key!(binary) do + for <<(<> <- binary)>> do + if byte > 0x7F or byte < 0x1F or byte in ~c'"\\/' do + raise EncodeError, + "invalid byte #{inspect(byte, base: :hex)} in literal key: #{inspect(binary)}" + end + end + + :ok + end + + defp collapse_static([bin1, bin2 | rest]) when is_binary(bin1) and is_binary(bin2) do + collapse_static([bin1 <> bin2 | rest]) + end + + defp collapse_static([other | rest]) do + [other | collapse_static(rest)] + end + + defp collapse_static([]) do + [] + end +end diff --git a/plugins/mob_iap/deps/jason/lib/decoder.ex b/plugins/mob_iap/deps/jason/lib/decoder.ex new file mode 100644 index 0000000..7ccd86d --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/decoder.ex @@ -0,0 +1,708 @@ +defmodule Jason.DecodeError do + @type t :: %__MODULE__{position: integer, data: String.t} + + defexception [:position, :token, :data] + + def message(%{position: position, token: token}) when is_binary(token) do + "unexpected sequence at position #{position}: #{inspect token}" + end + def message(%{position: position, data: data}) when position == byte_size(data) do + "unexpected end of input at position #{position}" + end + def message(%{position: position, data: data}) do + byte = :binary.at(data, position) + str = <> + if String.printable?(str) do + "unexpected byte at position #{position}: " <> + "#{inspect byte, base: :hex} (#{inspect str})" + else + "unexpected byte at position #{position}: " <> + "#{inspect byte, base: :hex}" + end + end +end + +defmodule Jason.Decoder do + @moduledoc false + + import Bitwise + + alias Jason.{DecodeError, Codegen} + + import Codegen, only: [bytecase: 2, bytecase: 3] + import Record + + @dialyzer :no_improper_lists + + # @compile :native + + # We use integers instead of atoms to take advantage of the jump table + # optimization + @terminate 0 + @array 1 + @key 2 + @object 3 + + defrecordp :decode, [keys: nil, strings: nil, objects: nil, floats: nil] + + def parse(data, opts) when is_binary(data) do + key_decode = key_decode_function(opts) + string_decode = string_decode_function(opts) + float_decode = float_decode_function(opts) + object_decode = object_decode_function(opts) + decode = decode(keys: key_decode, strings: string_decode, objects: object_decode, floats: float_decode) + try do + value(data, data, 0, [@terminate], decode) + catch + {:position, position} -> + {:error, %DecodeError{position: position, data: data}} + {:token, token, position} -> + {:error, %DecodeError{token: token, position: position, data: data}} + else + value -> + {:ok, value} + end + end + + defp key_decode_function(%{keys: :atoms}), do: &String.to_atom/1 + defp key_decode_function(%{keys: :atoms!}), do: &String.to_existing_atom/1 + defp key_decode_function(%{keys: :strings}), do: &(&1) + defp key_decode_function(%{keys: fun}) when is_function(fun, 1), do: fun + + defp string_decode_function(%{strings: :copy}), do: &:binary.copy/1 + defp string_decode_function(%{strings: :reference}), do: &(&1) + + defp object_decode_function(%{objects: :maps}), do: &:maps.from_list/1 + defp object_decode_function(%{objects: :ordered_objects}), do: &Jason.OrderedObject.new(:lists.reverse(&1)) + + defp float_decode_function(%{floats: :native}) do + fn string, token, skip -> + try do + :erlang.binary_to_float(string) + catch + :error, :badarg -> + token_error(token, skip) + end + end + end + + if Code.ensure_loaded?(Decimal) do + defp float_decode_function(%{floats: :decimals}) do + fn string, token, skip -> + try do + Decimal.new(string) + rescue + Decimal.Error -> + token_error(token, skip) + end + end + end + else + defp float_decode_function(%{floats: :decimals}) do + raise ArgumentError, "decimal library not found, :decimals option not available" + end + end + + defp value(data, original, skip, stack, decode) do + bytecase data do + _ in ~c'\s\n\t\r', rest -> + value(rest, original, skip + 1, stack, decode) + _ in ~c'0', rest -> + number_zero(rest, original, skip, stack, decode, 1) + _ in ~c'123456789', rest -> + number(rest, original, skip, stack, decode, 1) + _ in ~c'-', rest -> + number_minus(rest, original, skip, stack, decode) + _ in ~c'"', rest -> + string(rest, original, skip + 1, stack, decode, 0) + _ in ~c'[', rest -> + array(rest, original, skip + 1, stack, decode) + _ in ~c'{', rest -> + object(rest, original, skip + 1, stack, decode) + _ in ~c']', rest -> + empty_array(rest, original, skip + 1, stack, decode) + _ in ~c't', rest -> + case rest do + <<"rue", rest::bits>> -> + continue(rest, original, skip + 4, stack, decode, true) + <<_::bits>> -> + error(original, skip) + end + _ in ~c'f', rest -> + case rest do + <<"alse", rest::bits>> -> + continue(rest, original, skip + 5, stack, decode, false) + <<_::bits>> -> + error(original, skip) + end + _ in ~c'n', rest -> + case rest do + <<"ull", rest::bits>> -> + continue(rest, original, skip + 4, stack, decode, nil) + <<_::bits>> -> + error(original, skip) + end + _, rest -> + error(rest, original, skip + 1, stack, decode) + <<_::bits>> -> + error(original, skip) + end + end + + defp number_minus(<>, original, skip, stack, decode) do + number_zero(rest, original, skip, stack, decode, 2) + end + defp number_minus(<>, original, skip, stack, decode) + when byte in ~c'123456789' do + number(rest, original, skip, stack, decode, 2) + end + defp number_minus(<<_rest::bits>>, original, skip, _stack, _decode) do + error(original, skip + 1) + end + + if function_exported?(Application, :compile_env, 3) do + @integer_digit_limit Application.compile_env(:jason, :decoding_integer_digit_limit, 1024) + else + # use apply to avoid warnings in newer Elixir versions + @integer_digit_limit apply(Application, :get_env, [:jason, :decoding_integer_digit_limit, 1024]) + end + + defp number(<>, original, skip, stack, decode, len) + when byte in ~c'0123456789' do + number(rest, original, skip, stack, decode, len + 1) + end + defp number(<>, original, skip, stack, decode, len) do + number_frac(rest, original, skip, stack, decode, len + 1) + end + defp number(<>, original, skip, stack, decode, len) when e in ~c'eE' do + prefix = binary_part(original, skip, len) + number_exp_copy(rest, original, skip + len + 1, stack, decode, prefix) + end + defp number(<>, original, skip, stack, decode, len) do + token = binary_part(original, skip, len) + if byte_size(token) > @integer_digit_limit do + token_error(token, skip) + end + int = String.to_integer(token) + continue(rest, original, skip + len, stack, decode, int) + end + + defp number_frac(<>, original, skip, stack, decode, len) + when byte in ~c'0123456789' do + number_frac_cont(rest, original, skip, stack, decode, len + 1) + end + defp number_frac(<<_rest::bits>>, original, skip, _stack, _decode, len) do + error(original, skip + len) + end + + defp number_frac_cont(<>, original, skip, stack, decode, len) + when byte in ~c'0123456789' do + number_frac_cont(rest, original, skip, stack, decode, len + 1) + end + defp number_frac_cont(<>, original, skip, stack, decode, len) + when e in ~c'eE' do + number_exp(rest, original, skip, stack, decode, len + 1) + end + defp number_frac_cont(<>, original, skip, stack, decode, len) do + token = binary_part(original, skip, len) + decode(floats: float_decode) = decode + float = float_decode.(token, token, skip) + continue(rest, original, skip + len, stack, decode, float) + end + + defp number_exp(<>, original, skip, stack, decode, len) + when byte in ~c'0123456789' do + number_exp_cont(rest, original, skip, stack, decode, len + 1) + end + defp number_exp(<>, original, skip, stack, decode, len) + when byte in ~c'+-' do + number_exp_sign(rest, original, skip, stack, decode, len + 1) + end + defp number_exp(<<_rest::bits>>, original, skip, _stack, _decode, len) do + error(original, skip + len) + end + + defp number_exp_sign(<>, original, skip, stack, decode, len) + when byte in ~c'0123456789' do + number_exp_cont(rest, original, skip, stack, decode, len + 1) + end + defp number_exp_sign(<<_rest::bits>>, original, skip, _stack, _decode, len) do + error(original, skip + len) + end + + defp number_exp_cont(<>, original, skip, stack, decode, len) + when byte in ~c'0123456789' do + number_exp_cont(rest, original, skip, stack, decode, len + 1) + end + defp number_exp_cont(<>, original, skip, stack, decode, len) do + token = binary_part(original, skip, len) + decode(floats: float_decode) = decode + float = float_decode.(token, token, skip) + continue(rest, original, skip + len, stack, decode, float) + end + + defp number_exp_copy(<>, original, skip, stack, decode, prefix) + when byte in ~c'0123456789' do + number_exp_cont(rest, original, skip, stack, decode, prefix, 1) + end + defp number_exp_copy(<>, original, skip, stack, decode, prefix) + when byte in ~c'+-' do + number_exp_sign(rest, original, skip, stack, decode, prefix, 1) + end + defp number_exp_copy(<<_rest::bits>>, original, skip, _stack, _decode, _prefix) do + error(original, skip) + end + + defp number_exp_sign(<>, original, skip, stack, decode, prefix, len) + when byte in ~c'0123456789' do + number_exp_cont(rest, original, skip, stack, decode, prefix, len + 1) + end + defp number_exp_sign(<<_rest::bits>>, original, skip, _stack, _decode, _prefix, len) do + error(original, skip + len) + end + + defp number_exp_cont(<>, original, skip, stack, decode, prefix, len) + when byte in ~c'0123456789' do + number_exp_cont(rest, original, skip, stack, decode, prefix, len + 1) + end + defp number_exp_cont(<>, original, skip, stack, decode, prefix, len) do + suffix = binary_part(original, skip, len) + string = prefix <> ".0e" <> suffix + prefix_size = byte_size(prefix) + initial_skip = skip - prefix_size - 1 + final_skip = skip + len + token = binary_part(original, initial_skip, prefix_size + len + 1) + decode(floats: float_decode) = decode + float = float_decode.(string, token, initial_skip) + continue(rest, original, final_skip, stack, decode, float) + end + + defp number_zero(<>, original, skip, stack, decode, len) do + number_frac(rest, original, skip, stack, decode, len + 1) + end + defp number_zero(<>, original, skip, stack, decode, len) when e in ~c'eE' do + number_exp_copy(rest, original, skip + len + 1, stack, decode, "0") + end + defp number_zero(<>, original, skip, stack, decode, len) do + continue(rest, original, skip + len, stack, decode, 0) + end + + @compile {:inline, array: 5} + + defp array(rest, original, skip, stack, decode) do + value(rest, original, skip, [@array, [] | stack], decode) + end + + defp empty_array(<>, original, skip, stack, decode) do + case stack do + [@array, [] | stack] -> + continue(rest, original, skip, stack, decode, []) + _ -> + error(original, skip - 1) + end + end + + defp array(data, original, skip, stack, decode, value) do + bytecase data do + _ in ~c'\s\n\t\r', rest -> + array(rest, original, skip + 1, stack, decode, value) + _ in ~c']', rest -> + [acc | stack] = stack + value = :lists.reverse(acc, [value]) + continue(rest, original, skip + 1, stack, decode, value) + _ in ~c',', rest -> + [acc | stack] = stack + value(rest, original, skip + 1, [@array, [value | acc] | stack], decode) + _, _rest -> + error(original, skip) + <<_::bits>> -> + empty_error(original, skip) + end + end + + @compile {:inline, object: 5} + + defp object(rest, original, skip, stack, decode) do + key(rest, original, skip, [[] | stack], decode) + end + + defp object(data, original, skip, stack, decode, value) do + bytecase data do + _ in ~c'\s\n\t\r', rest -> + object(rest, original, skip + 1, stack, decode, value) + _ in ~c'}', rest -> + skip = skip + 1 + [key, acc | stack] = stack + decode(keys: key_decode) = decode + final = [{key_decode.(key), value} | acc] + decode(objects: object_decode) = decode + continue(rest, original, skip, stack, decode, object_decode.(final)) + _ in ~c',', rest -> + skip = skip + 1 + [key, acc | stack] = stack + decode(keys: key_decode) = decode + acc = [{key_decode.(key), value} | acc] + key(rest, original, skip, [acc | stack], decode) + _, _rest -> + error(original, skip) + <<_::bits>> -> + empty_error(original, skip) + end + end + + defp key(data, original, skip, stack, decode) do + bytecase data do + _ in ~c'\s\n\t\r', rest -> + key(rest, original, skip + 1, stack, decode) + _ in ~c'}', rest -> + case stack do + [[] | stack] -> + decode(objects: object_decode) = decode + continue(rest, original, skip + 1, stack, decode, object_decode.([])) + _ -> + error(original, skip) + end + _ in ~c'"', rest -> + string(rest, original, skip + 1, [@key | stack], decode, 0) + _, _rest -> + error(original, skip) + <<_::bits>> -> + empty_error(original, skip) + end + end + + defp key(data, original, skip, stack, decode, value) do + bytecase data do + _ in ~c'\s\n\t\r', rest -> + key(rest, original, skip + 1, stack, decode, value) + _ in ~c':', rest -> + value(rest, original, skip + 1, [@object, value | stack], decode) + _, _rest -> + error(original, skip) + <<_::bits>> -> + empty_error(original, skip) + end + end + + # TODO: check if this approach would be faster: + # https://git.ninenines.eu/cowlib.git/tree/src/cow_ws.erl#n469 + # http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + defp string(data, original, skip, stack, decode, len) do + bytecase data, 128 do + _ in ~c'"', rest -> + decode(strings: string_decode) = decode + string = string_decode.(binary_part(original, skip, len)) + continue(rest, original, skip + len + 1, stack, decode, string) + _ in ~c'\\', rest -> + part = binary_part(original, skip, len) + escape(rest, original, skip + len, stack, decode, part) + _ in unquote(0x00..0x1F), _rest -> + error(original, skip + len) + _, rest -> + string(rest, original, skip, stack, decode, len + 1) + <> when char <= 0x7FF -> + string(rest, original, skip, stack, decode, len + 2) + <> when char <= 0xFFFF -> + string(rest, original, skip, stack, decode, len + 3) + <<_char::utf8, rest::bits>> -> + string(rest, original, skip, stack, decode, len + 4) + <<_::bits>> -> + empty_error(original, skip + len) + end + end + + defp string(data, original, skip, stack, decode, acc, len) do + bytecase data, 128 do + _ in ~c'"', rest -> + last = binary_part(original, skip, len) + string = IO.iodata_to_binary([acc | last]) + continue(rest, original, skip + len + 1, stack, decode, string) + _ in ~c'\\', rest -> + part = binary_part(original, skip, len) + escape(rest, original, skip + len, stack, decode, [acc | part]) + _ in unquote(0x00..0x1F), _rest -> + error(original, skip + len) + _, rest -> + string(rest, original, skip, stack, decode, acc, len + 1) + <> when char <= 0x7FF -> + string(rest, original, skip, stack, decode, acc, len + 2) + <> when char <= 0xFFFF -> + string(rest, original, skip, stack, decode, acc, len + 3) + <<_char::utf8, rest::bits>> -> + string(rest, original, skip, stack, decode, acc, len + 4) + <<_::bits>> -> + empty_error(original, skip + len) + end + end + + defp escape(data, original, skip, stack, decode, acc) do + bytecase data do + _ in ~c'b', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\b'], 0) + _ in ~c't', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\t'], 0) + _ in ~c'n', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\n'], 0) + _ in ~c'f', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\f'], 0) + _ in ~c'r', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\r'], 0) + _ in ~c'"', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\"'], 0) + _ in ~c'/', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'/'], 0) + _ in ~c'\\', rest -> + string(rest, original, skip + 2, stack, decode, [acc | ~c'\\'], 0) + _ in ~c'u', rest -> + escapeu(rest, original, skip, stack, decode, acc) + _, _rest -> + error(original, skip + 1) + <<_::bits>> -> + empty_error(original, skip) + end + end + + defmodule Unescape do + @moduledoc false + + import Bitwise + + @digits Enum.concat([?0..?9, ?A..?F, ?a..?f]) + + def unicode_escapes(chars1 \\ @digits, chars2 \\ @digits) do + for char1 <- chars1, char2 <- chars2 do + {(char1 <<< 8) + char2, integer8(char1, char2)} + end + end + + defp integer8(char1, char2) do + (integer4(char1) <<< 4) + integer4(char2) + end + + defp integer4(char) when char in ?0..?9, do: char - ?0 + defp integer4(char) when char in ?A..?F, do: char - ?A + 10 + defp integer4(char) when char in ?a..?f, do: char - ?a + 10 + + defp token_error_clause(original, skip, len) do + quote do + _ -> + token_error(unquote_splicing([original, skip, len])) + end + end + + defmacro escapeu_first(int, last, rest, original, skip, stack, decode, acc) do + clauses = escapeu_first_clauses(last, rest, original, skip, stack, decode, acc) + quote location: :keep do + case unquote(int) do + unquote(clauses ++ token_error_clause(original, skip, 6)) + end + end + end + + defp escapeu_first_clauses(last, rest, original, skip, stack, decode, acc) do + for {int, first} <- unicode_escapes(), + not (first in 0xDC..0xDF) do + escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) + end + end + + defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) + when first in 0xD8..0xDB do + hi = + quote bind_quoted: [first: first, last: last] do + 0x10000 + ((((first &&& 0x03) <<< 8) + last) <<< 10) + end + args = [rest, original, skip, stack, decode, acc, hi] + [clause] = + quote location: :keep do + unquote(int) -> escape_surrogate(unquote_splicing(args)) + end + clause + end + + defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) + when first <= 0x00 do + skip = quote do: (unquote(skip) + 6) + acc = + quote bind_quoted: [acc: acc, first: first, last: last] do + if last <= 0x7F do + # 0????? + [acc, last] + else + # 110xxxx?? 10????? + byte1 = ((0b110 <<< 5) + (first <<< 2)) + (last >>> 6) + byte2 = (0b10 <<< 6) + (last &&& 0b111111) + [acc, byte1, byte2] + end + end + args = [rest, original, skip, stack, decode, acc, 0] + [clause] = + quote location: :keep do + unquote(int) -> string(unquote_splicing(args)) + end + clause + end + + defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) + when first <= 0x07 do + skip = quote do: (unquote(skip) + 6) + acc = + quote bind_quoted: [acc: acc, first: first, last: last] do + # 110xxx?? 10?????? + byte1 = ((0b110 <<< 5) + (first <<< 2)) + (last >>> 6) + byte2 = (0b10 <<< 6) + (last &&& 0b111111) + [acc, byte1, byte2] + end + args = [rest, original, skip, stack, decode, acc, 0] + [clause] = + quote location: :keep do + unquote(int) -> string(unquote_splicing(args)) + end + clause + end + + defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) + when first <= 0xFF do + skip = quote do: (unquote(skip) + 6) + acc = + quote bind_quoted: [acc: acc, first: first, last: last] do + # 1110xxxx 10xxxx?? 10?????? + byte1 = (0b1110 <<< 4) + (first >>> 4) + byte2 = ((0b10 <<< 6) + ((first &&& 0b1111) <<< 2)) + (last >>> 6) + byte3 = (0b10 <<< 6) + (last &&& 0b111111) + [acc, byte1, byte2, byte3] + end + args = [rest, original, skip, stack, decode, acc, 0] + [clause] = + quote location: :keep do + unquote(int) -> string(unquote_splicing(args)) + end + clause + end + + defmacro escapeu_last(int, original, skip) do + clauses = escapeu_last_clauses() + quote location: :keep do + case unquote(int) do + unquote(clauses ++ token_error_clause(original, skip, 6)) + end + end + end + + defp escapeu_last_clauses() do + for {int, last} <- unicode_escapes() do + [clause] = + quote do + unquote(int) -> unquote(last) + end + clause + end + end + + defmacro escapeu_surrogate(int, last, rest, original, skip, stack, decode, acc, + hi) do + clauses = escapeu_surrogate_clauses(last, rest, original, skip, stack, decode, acc, hi) + quote location: :keep do + case unquote(int) do + unquote(clauses ++ token_error_clause(original, skip, 12)) + end + end + end + + defp escapeu_surrogate_clauses(last, rest, original, skip, stack, decode, acc, hi) do + digits1 = ~c'Dd' + digits2 = Stream.concat([?C..?F, ?c..?f]) + for {int, first} <- unicode_escapes(digits1, digits2) do + escapeu_surrogate_clause(int, first, last, rest, original, skip, stack, decode, acc, hi) + end + end + + defp escapeu_surrogate_clause(int, first, last, rest, original, skip, stack, decode, acc, hi) do + skip = quote do: unquote(skip) + 12 + acc = + quote bind_quoted: [acc: acc, first: first, last: last, hi: hi] do + lo = ((first &&& 0x03) <<< 8) + last + [acc | <<(hi + lo)::utf8>>] + end + args = [rest, original, skip, stack, decode, acc, 0] + [clause] = + quote do + unquote(int) -> + string(unquote_splicing(args)) + end + clause + end + end + + defp escapeu(<>, original, skip, stack, decode, acc) do + require Unescape + last = escapeu_last(int2, original, skip) + Unescape.escapeu_first(int1, last, rest, original, skip, stack, decode, acc) + end + defp escapeu(<<_rest::bits>>, original, skip, _stack, _decode, _acc) do + empty_error(original, skip) + end + + # @compile {:inline, escapeu_last: 3} + + defp escapeu_last(int, original, skip) do + require Unescape + Unescape.escapeu_last(int, original, skip) + end + + defp escape_surrogate(<>, original, + skip, stack, decode, acc, hi) do + require Unescape + last = escapeu_last(int2, original, skip + 6) + Unescape.escapeu_surrogate(int1, last, rest, original, skip, stack, decode, acc, hi) + end + defp escape_surrogate(<<_rest::bits>>, original, skip, _stack, _decode, _acc, _hi) do + error(original, skip + 6) + end + + defp error(<<_rest::bits>>, _original, skip, _stack, _decode) do + throw {:position, skip - 1} + end + + defp empty_error(_original, skip) do + throw {:position, skip} + end + + @compile {:inline, error: 2, token_error: 2, token_error: 3} + defp error(_original, skip) do + throw {:position, skip} + end + + defp token_error(token, position) do + throw {:token, token, position} + end + + defp token_error(token, position, len) do + throw {:token, binary_part(token, position, len), position} + end + + @compile {:inline, continue: 6} + defp continue(rest, original, skip, stack, decode, value) do + case stack do + [@terminate | stack] -> + terminate(rest, original, skip, stack, decode, value) + [@array | stack] -> + array(rest, original, skip, stack, decode, value) + [@key | stack] -> + key(rest, original, skip, stack, decode, value) + [@object | stack] -> + object(rest, original, skip, stack, decode, value) + end + end + + defp terminate(<>, original, skip, stack, decode, value) + when byte in ~c'\s\n\r\t' do + terminate(rest, original, skip + 1, stack, decode, value) + end + defp terminate(<<>>, _original, _skip, _stack, _decode, value) do + value + end + defp terminate(<<_rest::bits>>, original, skip, _stack, _decode, _value) do + error(original, skip) + end +end diff --git a/plugins/mob_iap/deps/jason/lib/encode.ex b/plugins/mob_iap/deps/jason/lib/encode.ex new file mode 100644 index 0000000..ffff65c --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/encode.ex @@ -0,0 +1,657 @@ +defmodule Jason.EncodeError do + defexception [:message] + + @type t :: %__MODULE__{message: String.t} + + def new({:duplicate_key, key}) do + %__MODULE__{message: "duplicate key: #{key}"} + end + def new({:invalid_byte, byte, original}) do + %__MODULE__{message: "invalid byte #{inspect byte, base: :hex} in #{inspect original}"} + end +end + +defmodule Jason.Encode do + @moduledoc """ + Utilities for encoding elixir values to JSON. + """ + + import Bitwise + + alias Jason.{Codegen, EncodeError, Encoder, Fragment, OrderedObject} + + @typep escape :: (String.t -> iodata) + @typep encode_map :: (map, escape, encode_map -> iodata) + @opaque opts :: {escape, encode_map} + + @dialyzer :no_improper_lists + + # @compile :native + + @doc false + @spec encode(any, map) :: {:ok, iodata} | {:error, EncodeError.t | Exception.t} + def encode(value, opts) do + escape = escape_function(opts) + encode_map = encode_map_function(opts) + try do + {:ok, value(value, escape, encode_map)} + catch + :throw, %EncodeError{} = e -> + {:error, e} + :error, %Protocol.UndefinedError{protocol: Jason.Encoder} = e -> + {:error, e} + end + end + + defp encode_map_function(%{maps: maps}) do + case maps do + :naive -> &map_naive/3 + :strict -> &map_strict/3 + end + end + + defp escape_function(%{escape: escape}) do + case escape do + :json -> &escape_json/1 + :html_safe -> &escape_html/1 + :unicode_safe -> &escape_unicode/1 + :javascript_safe -> &escape_javascript/1 + # Keep for compatibility with Poison + :javascript -> &escape_javascript/1 + :unicode -> &escape_unicode/1 + end + end + + @doc """ + Equivalent to calling the `Jason.Encoder.encode/2` protocol function. + + Slightly more efficient for built-in types because of the internal dispatching. + """ + @spec value(term, opts) :: iodata + def value(value, {escape, encode_map}) do + value(value, escape, encode_map) + end + + @doc false + # We use this directly in the helpers and deriving for extra speed + def value(value, escape, _encode_map) when is_atom(value) do + encode_atom(value, escape) + end + + def value(value, escape, _encode_map) when is_binary(value) do + encode_string(value, escape) + end + + def value(value, _escape, _encode_map) when is_integer(value) do + integer(value) + end + + def value(value, _escape, _encode_map) when is_float(value) do + float(value) + end + + def value(value, escape, encode_map) when is_list(value) do + list(value, escape, encode_map) + end + + def value(%{__struct__: module} = value, escape, encode_map) do + struct(value, escape, encode_map, module) + end + + def value(value, escape, encode_map) when is_map(value) do + case Map.to_list(value) do + [] -> "{}" + keyword -> encode_map.(keyword, escape, encode_map) + end + end + + def value(value, escape, encode_map) do + Encoder.encode(value, {escape, encode_map}) + end + + @compile {:inline, integer: 1, float: 1} + + @spec atom(atom, opts) :: iodata + def atom(atom, {escape, _encode_map}) do + encode_atom(atom, escape) + end + + defp encode_atom(nil, _escape), do: "null" + defp encode_atom(true, _escape), do: "true" + defp encode_atom(false, _escape), do: "false" + defp encode_atom(atom, escape), + do: encode_string(Atom.to_string(atom), escape) + + @spec integer(integer) :: iodata + def integer(integer) do + Integer.to_string(integer) + end + + has_short_format = try do + :erlang.float_to_binary(1.0, [:short]) + catch + _, _ -> false + else + _ -> true + end + + @spec float(float) :: iodata + if has_short_format do + def float(float) do + :erlang.float_to_binary(float, [:short]) + end + else + def float(float) do + :io_lib_format.fwrite_g(float) + end + end + + @spec list(list, opts) :: iodata + def list(list, {escape, encode_map}) do + list(list, escape, encode_map) + end + + defp list([], _escape, _encode_map) do + "[]" + end + + defp list([head | tail], escape, encode_map) do + [?[, value(head, escape, encode_map) + | list_loop(tail, escape, encode_map)] + end + + defp list_loop([], _escape, _encode_map) do + ~c']' + end + + defp list_loop([head | tail], escape, encode_map) do + [?,, value(head, escape, encode_map) + | list_loop(tail, escape, encode_map)] + end + + @spec keyword(keyword, opts) :: iodata + def keyword(list, _) when list == [], do: "{}" + def keyword(list, {escape, encode_map}) when is_list(list) do + encode_map.(list, escape, encode_map) + end + + @spec map(map, opts) :: iodata + def map(value, {escape, encode_map}) do + case Map.to_list(value) do + [] -> "{}" + keyword -> encode_map.(keyword, escape, encode_map) + end + end + + defp map_naive([{key, value} | tail], escape, encode_map) do + ["{\"", key(key, escape), "\":", + value(value, escape, encode_map) + | map_naive_loop(tail, escape, encode_map)] + end + + defp map_naive_loop([], _escape, _encode_map) do + ~c'}' + end + + defp map_naive_loop([{key, value} | tail], escape, encode_map) do + [",\"", key(key, escape), "\":", + value(value, escape, encode_map) + | map_naive_loop(tail, escape, encode_map)] + end + + defp map_strict([{key, value} | tail], escape, encode_map) do + key = IO.iodata_to_binary(key(key, escape)) + visited = %{key => []} + ["{\"", key, "\":", + value(value, escape, encode_map) + | map_strict_loop(tail, escape, encode_map, visited)] + end + + defp map_strict_loop([], _encode_map, _escape, _visited) do + ~c'}' + end + + defp map_strict_loop([{key, value} | tail], escape, encode_map, visited) do + key = IO.iodata_to_binary(key(key, escape)) + case visited do + %{^key => _} -> + error({:duplicate_key, key}) + _ -> + visited = Map.put(visited, key, []) + [",\"", key, "\":", + value(value, escape, encode_map) + | map_strict_loop(tail, escape, encode_map, visited)] + end + end + + @spec struct(struct, opts) :: iodata + def struct(%module{} = value, {escape, encode_map}) do + struct(value, escape, encode_map, module) + end + + # TODO: benchmark the effect of inlining the to_iso8601 functions + for module <- [Date, Time, NaiveDateTime, DateTime] do + defp struct(value, _escape, _encode_map, unquote(module)) do + [?", unquote(module).to_iso8601(value), ?"] + end + end + + if Code.ensure_loaded?(Decimal) do + defp struct(value, _escape, _encode_map, Decimal) do + [?", Decimal.to_string(value, :normal), ?"] + end + end + + defp struct(value, escape, encode_map, Fragment) do + %{encode: encode} = value + encode.({escape, encode_map}) + end + + defp struct(value, escape, encode_map, OrderedObject) do + case value do + %{values: []} -> "{}" + %{values: values} -> encode_map.(values, escape, encode_map) + end + end + + defp struct(value, escape, encode_map, _module) do + Encoder.encode(value, {escape, encode_map}) + end + + @doc false + # This is used in the helpers and deriving implementation + def key(string, escape) when is_binary(string) do + escape.(string) + end + def key(atom, escape) when is_atom(atom) do + string = Atom.to_string(atom) + escape.(string) + end + def key(other, escape) do + string = String.Chars.to_string(other) + escape.(string) + end + + @spec string(String.t, opts) :: iodata + def string(string, {escape, _encode_map}) do + encode_string(string, escape) + end + + defp encode_string(string, escape) do + [?", escape.(string), ?"] + end + + slash_escapes = Enum.zip(~c'\b\t\n\f\r\"\\', ~c'btnfr"\\') + surogate_escapes = Enum.zip([0x2028, 0x2029], ["\\u2028", "\\u2029"]) + ranges = [{0x00..0x1F, :unicode} | slash_escapes] + html_ranges = [{0x00..0x1F, :unicode}, {?<, :unicode}, {?/, ?/} | slash_escapes] + escape_jt = Codegen.jump_table(html_ranges, :error) + + Enum.each(escape_jt, fn + {byte, :unicode} -> + sequence = List.to_string(:io_lib.format("\\u~4.16.0B", [byte])) + defp escape(unquote(byte)), do: unquote(sequence) + {byte, char} when is_integer(char) -> + defp escape(unquote(byte)), do: unquote(<>) + {byte, :error} -> + defp escape(unquote(byte)), do: throw(:error) + end) + + ## regular JSON escape + + json_jt = Codegen.jump_table(ranges, :chunk, 0x7F + 1) + + defp escape_json(data) do + escape_json(data, [], data, 0) + end + + Enum.map(json_jt, fn + {byte, :chunk} -> + defp escape_json(<>, acc, original, skip) + when byte === unquote(byte) do + escape_json_chunk(rest, acc, original, skip, 1) + end + {byte, _escape} -> + defp escape_json(<>, acc, original, skip) + when byte === unquote(byte) do + acc = [acc | escape(byte)] + escape_json(rest, acc, original, skip + 1) + end + end) + defp escape_json(<>, acc, original, skip) + when char <= 0x7FF do + escape_json_chunk(rest, acc, original, skip, 2) + end + defp escape_json(<>, acc, original, skip) + when char <= 0xFFFF do + escape_json_chunk(rest, acc, original, skip, 3) + end + defp escape_json(<<_char::utf8, rest::bits>>, acc, original, skip) do + escape_json_chunk(rest, acc, original, skip, 4) + end + defp escape_json(<<>>, acc, _original, _skip) do + acc + end + defp escape_json(<>, _acc, original, _skip) do + error({:invalid_byte, byte, original}) + end + + Enum.map(json_jt, fn + {byte, :chunk} -> + defp escape_json_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + escape_json_chunk(rest, acc, original, skip, len + 1) + end + {byte, _escape} -> + defp escape_json_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + part = binary_part(original, skip, len) + acc = [acc, part | escape(byte)] + escape_json(rest, acc, original, skip + len + 1) + end + end) + defp escape_json_chunk(<>, acc, original, skip, len) + when char <= 0x7FF do + escape_json_chunk(rest, acc, original, skip, len + 2) + end + defp escape_json_chunk(<>, acc, original, skip, len) + when char <= 0xFFFF do + escape_json_chunk(rest, acc, original, skip, len + 3) + end + defp escape_json_chunk(<<_char::utf8, rest::bits>>, acc, original, skip, len) do + escape_json_chunk(rest, acc, original, skip, len + 4) + end + defp escape_json_chunk(<<>>, acc, original, skip, len) do + part = binary_part(original, skip, len) + [acc | part] + end + defp escape_json_chunk(<>, _acc, original, _skip, _len) do + error({:invalid_byte, byte, original}) + end + + ## javascript safe JSON escape + + defp escape_javascript(data) do + escape_javascript(data, [], data, 0) + end + + Enum.map(json_jt, fn + {byte, :chunk} -> + defp escape_javascript(<>, acc, original, skip) + when byte === unquote(byte) do + escape_javascript_chunk(rest, acc, original, skip, 1) + end + {byte, _escape} -> + defp escape_javascript(<>, acc, original, skip) + when byte === unquote(byte) do + acc = [acc | escape(byte)] + escape_javascript(rest, acc, original, skip + 1) + end + end) + defp escape_javascript(<>, acc, original, skip) + when char <= 0x7FF do + escape_javascript_chunk(rest, acc, original, skip, 2) + end + Enum.map(surogate_escapes, fn {byte, escape} -> + defp escape_javascript(<>, acc, original, skip) do + acc = [acc | unquote(escape)] + escape_javascript(rest, acc, original, skip + 3) + end + end) + defp escape_javascript(<>, acc, original, skip) + when char <= 0xFFFF do + escape_javascript_chunk(rest, acc, original, skip, 3) + end + defp escape_javascript(<<_char::utf8, rest::bits>>, acc, original, skip) do + escape_javascript_chunk(rest, acc, original, skip, 4) + end + defp escape_javascript(<<>>, acc, _original, _skip) do + acc + end + defp escape_javascript(<>, _acc, original, _skip) do + error({:invalid_byte, byte, original}) + end + + Enum.map(json_jt, fn + {byte, :chunk} -> + defp escape_javascript_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + escape_javascript_chunk(rest, acc, original, skip, len + 1) + end + {byte, _escape} -> + defp escape_javascript_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + part = binary_part(original, skip, len) + acc = [acc, part | escape(byte)] + escape_javascript(rest, acc, original, skip + len + 1) + end + end) + defp escape_javascript_chunk(<>, acc, original, skip, len) + when char <= 0x7FF do + escape_javascript_chunk(rest, acc, original, skip, len + 2) + end + Enum.map(surogate_escapes, fn {byte, escape} -> + defp escape_javascript_chunk(<>, acc, original, skip, len) do + part = binary_part(original, skip, len) + acc = [acc, part | unquote(escape)] + escape_javascript(rest, acc, original, skip + len + 3) + end + end) + defp escape_javascript_chunk(<>, acc, original, skip, len) + when char <= 0xFFFF do + escape_javascript_chunk(rest, acc, original, skip, len + 3) + end + defp escape_javascript_chunk(<<_char::utf8, rest::bits>>, acc, original, skip, len) do + escape_javascript_chunk(rest, acc, original, skip, len + 4) + end + defp escape_javascript_chunk(<<>>, acc, original, skip, len) do + part = binary_part(original, skip, len) + [acc | part] + end + defp escape_javascript_chunk(<>, _acc, original, _skip, _len) do + error({:invalid_byte, byte, original}) + end + + ## HTML safe JSON escape + + html_jt = Codegen.jump_table(html_ranges, :chunk, 0x7F + 1) + + defp escape_html(data) do + escape_html(data, [], data, 0) + end + + Enum.map(html_jt, fn + {byte, :chunk} -> + defp escape_html(<>, acc, original, skip) + when byte === unquote(byte) do + escape_html_chunk(rest, acc, original, skip, 1) + end + {byte, _escape} -> + defp escape_html(<>, acc, original, skip) + when byte === unquote(byte) do + acc = [acc | escape(byte)] + escape_html(rest, acc, original, skip + 1) + end + end) + defp escape_html(<>, acc, original, skip) + when char <= 0x7FF do + escape_html_chunk(rest, acc, original, skip, 2) + end + Enum.map(surogate_escapes, fn {byte, escape} -> + defp escape_html(<>, acc, original, skip) do + acc = [acc | unquote(escape)] + escape_html(rest, acc, original, skip + 3) + end + end) + defp escape_html(<>, acc, original, skip) + when char <= 0xFFFF do + escape_html_chunk(rest, acc, original, skip, 3) + end + defp escape_html(<<_char::utf8, rest::bits>>, acc, original, skip) do + escape_html_chunk(rest, acc, original, skip, 4) + end + defp escape_html(<<>>, acc, _original, _skip) do + acc + end + defp escape_html(<>, _acc, original, _skip) do + error({:invalid_byte, byte, original}) + end + + Enum.map(html_jt, fn + {byte, :chunk} -> + defp escape_html_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + escape_html_chunk(rest, acc, original, skip, len + 1) + end + {byte, _escape} -> + defp escape_html_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + part = binary_part(original, skip, len) + acc = [acc, part | escape(byte)] + escape_html(rest, acc, original, skip + len + 1) + end + end) + defp escape_html_chunk(<>, acc, original, skip, len) + when char <= 0x7FF do + escape_html_chunk(rest, acc, original, skip, len + 2) + end + Enum.map(surogate_escapes, fn {byte, escape} -> + defp escape_html_chunk(<>, acc, original, skip, len) do + part = binary_part(original, skip, len) + acc = [acc, part | unquote(escape)] + escape_html(rest, acc, original, skip + len + 3) + end + end) + defp escape_html_chunk(<>, acc, original, skip, len) + when char <= 0xFFFF do + escape_html_chunk(rest, acc, original, skip, len + 3) + end + defp escape_html_chunk(<<_char::utf8, rest::bits>>, acc, original, skip, len) do + escape_html_chunk(rest, acc, original, skip, len + 4) + end + defp escape_html_chunk(<<>>, acc, original, skip, len) do + part = binary_part(original, skip, len) + [acc | part] + end + defp escape_html_chunk(<>, _acc, original, _skip, _len) do + error({:invalid_byte, byte, original}) + end + + ## unicode escape + + defp escape_unicode(data) do + escape_unicode(data, [], data, 0) + end + + Enum.map(json_jt, fn + {byte, :chunk} -> + defp escape_unicode(<>, acc, original, skip) + when byte === unquote(byte) do + escape_unicode_chunk(rest, acc, original, skip, 1) + end + {byte, _escape} -> + defp escape_unicode(<>, acc, original, skip) + when byte === unquote(byte) do + acc = [acc | escape(byte)] + escape_unicode(rest, acc, original, skip + 1) + end + end) + defp escape_unicode(<>, acc, original, skip) + when char <= 0xFF do + acc = [acc, "\\u00" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + 2) + end + defp escape_unicode(<>, acc, original, skip) + when char <= 0x7FF do + acc = [acc, "\\u0" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + 2) + end + defp escape_unicode(<>, acc, original, skip) + when char <= 0xFFF do + acc = [acc, "\\u0" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + 3) + end + defp escape_unicode(<>, acc, original, skip) + when char <= 0xFFFF do + acc = [acc, "\\u" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + 3) + end + defp escape_unicode(<>, acc, original, skip) do + char = char - 0x10000 + acc = + [ + acc, + "\\uD", Integer.to_string(0x800 ||| (char >>> 10), 16), + "\\uD" | Integer.to_string(0xC00 ||| (char &&& 0x3FF), 16) + ] + escape_unicode(rest, acc, original, skip + 4) + end + defp escape_unicode(<<>>, acc, _original, _skip) do + acc + end + defp escape_unicode(<>, _acc, original, _skip) do + error({:invalid_byte, byte, original}) + end + + Enum.map(json_jt, fn + {byte, :chunk} -> + defp escape_unicode_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + escape_unicode_chunk(rest, acc, original, skip, len + 1) + end + {byte, _escape} -> + defp escape_unicode_chunk(<>, acc, original, skip, len) + when byte === unquote(byte) do + part = binary_part(original, skip, len) + acc = [acc, part | escape(byte)] + escape_unicode(rest, acc, original, skip + len + 1) + end + end) + defp escape_unicode_chunk(<>, acc, original, skip, len) + when char <= 0xFF do + part = binary_part(original, skip, len) + acc = [acc, part, "\\u00" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + len + 2) + end + defp escape_unicode_chunk(<>, acc, original, skip, len) + when char <= 0x7FF do + part = binary_part(original, skip, len) + acc = [acc, part, "\\u0" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + len + 2) + end + defp escape_unicode_chunk(<>, acc, original, skip, len) + when char <= 0xFFF do + part = binary_part(original, skip, len) + acc = [acc, part, "\\u0" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + len + 3) + end + defp escape_unicode_chunk(<>, acc, original, skip, len) + when char <= 0xFFFF do + part = binary_part(original, skip, len) + acc = [acc, part, "\\u" | Integer.to_string(char, 16)] + escape_unicode(rest, acc, original, skip + len + 3) + end + defp escape_unicode_chunk(<>, acc, original, skip, len) do + char = char - 0x10000 + part = binary_part(original, skip, len) + acc = + [ + acc, part, + "\\uD", Integer.to_string(0x800 ||| (char >>> 10), 16), + "\\uD" | Integer.to_string(0xC00 ||| (char &&& 0x3FF), 16) + ] + escape_unicode(rest, acc, original, skip + len + 4) + end + defp escape_unicode_chunk(<<>>, acc, original, skip, len) do + part = binary_part(original, skip, len) + [acc | part] + end + defp escape_unicode_chunk(<>, _acc, original, _skip, _len) do + error({:invalid_byte, byte, original}) + end + + @compile {:inline, error: 1} + defp error(error) do + throw EncodeError.new(error) + end +end diff --git a/plugins/mob_iap/deps/jason/lib/encoder.ex b/plugins/mob_iap/deps/jason/lib/encoder.ex new file mode 100644 index 0000000..95fbf0c --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/encoder.ex @@ -0,0 +1,239 @@ +defprotocol Jason.Encoder do + @moduledoc """ + Protocol controlling how a value is encoded to JSON. + + ## Deriving + + The protocol allows leveraging the Elixir's `@derive` feature + to simplify protocol implementation in trivial cases. Accepted + options are: + + * `:only` - encodes only values of specified keys. + * `:except` - encodes all struct fields except specified keys. + + By default all keys except the `:__struct__` key are encoded. + + ## Example + + Let's assume a presence of the following struct: + + defmodule Test do + defstruct [:foo, :bar, :baz] + end + + If we were to call `@derive Jason.Encoder` just before `defstruct`, + an implementation similar to the following implementation would be generated: + + defimpl Jason.Encoder, for: Test do + def encode(value, opts) do + Jason.Encode.map(Map.take(value, [:foo, :bar, :baz]), opts) + end + end + + If we called `@derive {Jason.Encoder, only: [:foo]}`, an implementation + similar to the following implementation would be generated: + + defimpl Jason.Encoder, for: Test do + def encode(value, opts) do + Jason.Encode.map(Map.take(value, [:foo]), opts) + end + end + + If we called `@derive {Jason.Encoder, except: [:foo]}`, an implementation + similar to the following implementation would be generated: + + defimpl Jason.Encoder, for: Test do + def encode(value, opts) do + Jason.Encode.map(Map.take(value, [:bar, :baz]), opts) + end + end + + The actually generated implementations are more efficient computing some data + during compilation similar to the macros from the `Jason.Helpers` module. + + ## Explicit implementation + + If you wish to implement the protocol fully yourself, it is advised to + use functions from the `Jason.Encode` module to do the actual iodata + generation - they are highly optimized and verified to always produce + valid JSON. + """ + + @type t :: term + @type opts :: Jason.Encode.opts() + + @fallback_to_any true + + @doc """ + Encodes `value` to JSON. + + The argument `opts` is opaque - it can be passed to various functions in + `Jason.Encode` (or to the protocol function itself) for encoding values to JSON. + """ + @spec encode(t, opts) :: iodata + def encode(value, opts) +end + +defimpl Jason.Encoder, for: Any do + defmacro __deriving__(module, struct, opts) do + fields = fields_to_encode(struct, opts) + kv = Enum.map(fields, &{&1, generated_var(&1)}) + escape = quote(do: escape) + encode_map = quote(do: encode_map) + encode_args = [escape, encode_map] + kv_iodata = Jason.Codegen.build_kv_iodata(kv, encode_args) + + quote do + defimpl Jason.Encoder, for: unquote(module) do + require Jason.Helpers + + def encode(%{unquote_splicing(kv)}, {unquote(escape), unquote(encode_map)}) do + unquote(kv_iodata) + end + end + end + end + + # The same as Macro.var/2 except it sets generated: true and handles _ key + defp generated_var(:_) do + {:__, [generated: true], __MODULE__.Underscore} + end + defp generated_var(name) do + {name, [generated: true], __MODULE__} + end + + def encode(%_{} = struct, _opts) do + raise Protocol.UndefinedError, + protocol: @protocol, + value: struct, + description: """ + Jason.Encoder protocol must always be explicitly implemented. + + If you own the struct, you can derive the implementation specifying \ + which fields should be encoded to JSON: + + @derive {Jason.Encoder, only: [....]} + defstruct ... + + It is also possible to encode all fields, although this should be \ + used carefully to avoid accidentally leaking private information \ + when new fields are added: + + @derive Jason.Encoder + defstruct ... + + Finally, if you don't own the struct you want to encode to JSON, \ + you may use Protocol.derive/3 placed outside of any module: + + Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) + Protocol.derive(Jason.Encoder, NameOfTheStruct) + """ + end + + def encode(value, _opts) do + raise Protocol.UndefinedError, + protocol: @protocol, + value: value, + description: "Jason.Encoder protocol must always be explicitly implemented" + end + + defp fields_to_encode(struct, opts) do + fields = Map.keys(struct) + + cond do + only = Keyword.get(opts, :only) -> + case only -- fields do + [] -> + only + + error_keys -> + raise ArgumentError, + "`:only` specified keys (#{inspect(error_keys)}) that are not defined in defstruct: " <> + "#{inspect(fields -- [:__struct__])}" + + end + + except = Keyword.get(opts, :except) -> + case except -- fields do + [] -> + fields -- [:__struct__ | except] + + error_keys -> + raise ArgumentError, + "`:except` specified keys (#{inspect(error_keys)}) that are not defined in defstruct: " <> + "#{inspect(fields -- [:__struct__])}" + + end + + true -> + fields -- [:__struct__] + end + end +end + +# The following implementations are formality - they are already covered +# by the main encoding mechanism in Jason.Encode, but exist mostly for +# documentation purposes and if anybody had the idea to call the protocol directly. + +defimpl Jason.Encoder, for: Atom do + def encode(atom, opts) do + Jason.Encode.atom(atom, opts) + end +end + +defimpl Jason.Encoder, for: Integer do + def encode(integer, _opts) do + Jason.Encode.integer(integer) + end +end + +defimpl Jason.Encoder, for: Float do + def encode(float, _opts) do + Jason.Encode.float(float) + end +end + +defimpl Jason.Encoder, for: List do + def encode(list, opts) do + Jason.Encode.list(list, opts) + end +end + +defimpl Jason.Encoder, for: Map do + def encode(map, opts) do + Jason.Encode.map(map, opts) + end +end + +defimpl Jason.Encoder, for: BitString do + def encode(binary, opts) when is_binary(binary) do + Jason.Encode.string(binary, opts) + end + + def encode(bitstring, _opts) do + raise Protocol.UndefinedError, + protocol: @protocol, + value: bitstring, + description: "cannot encode a bitstring to JSON" + end +end + +defimpl Jason.Encoder, for: [Date, Time, NaiveDateTime, DateTime] do + def encode(value, _opts) do + [?", @for.to_iso8601(value), ?"] + end +end + +if Code.ensure_loaded?(Decimal) do + defimpl Jason.Encoder, for: Decimal do + def encode(value, _opts) do + [?", Decimal.to_string(value), ?"] + end + end +end + +defimpl Jason.Encoder, for: Jason.Fragment do + def encode(%{encode: encode}, opts) do + encode.(opts) + end +end diff --git a/plugins/mob_iap/deps/jason/lib/formatter.ex b/plugins/mob_iap/deps/jason/lib/formatter.ex new file mode 100644 index 0000000..560a2f5 --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/formatter.ex @@ -0,0 +1,255 @@ +defmodule Jason.Formatter do + @moduledoc ~S""" + Pretty-printing and minimizing functions for JSON-encoded data. + + Input is required to be in an 8-bit-wide encoding such as UTF-8 or Latin-1 + in `t:iodata/0` format. Input must have valid JSON, invalid JSON may produce + unexpected results or errors. + """ + + @type opts :: [ + {:indent, iodata} + | {:line_separator, iodata} + | {:record_separator, iodata} + | {:after_colon, iodata} + ] + + import Record + defrecordp :opts, [:indent, :line, :record, :colon] + + @dialyzer :no_improper_lists + + @doc ~S""" + Pretty-prints JSON-encoded `input`. + + `input` may contain multiple JSON objects or arrays, optionally separated + by whitespace (e.g., one object per line). Objects in output will be + separated by newlines. No trailing newline is emitted. + + ## Options + + * `:indent` - used for nested objects and arrays (default: two spaces - `" "`); + * `:line_separator` - used in nested objects (default: `"\n"`); + * `:record_separator` - separates root-level objects and arrays + (default is the value for `:line_separator` option); + * `:after_colon` - printed after a colon inside objects (default: one space - `" "`). + + ## Examples + + iex> Jason.Formatter.pretty_print(~s|{"a":{"b": [1, 2]}}|) + ~s|{ + "a": { + "b": [ + 1, + 2 + ] + } + }| + + """ + @spec pretty_print(iodata, opts) :: binary + def pretty_print(input, opts \\ []) do + input + |> pretty_print_to_iodata(opts) + |> IO.iodata_to_binary() + end + + @doc ~S""" + Pretty-prints JSON-encoded `input` and returns iodata. + + This function should be preferred to `pretty_print/2`, if the pretty-printed + JSON will be handed over to one of the IO functions or sent + over the socket. The Erlang runtime is able to leverage vectorised + writes and avoid allocating a continuous buffer for the whole + resulting string, lowering memory use and increasing performance. + """ + @spec pretty_print_to_iodata(iodata, opts) :: iodata + def pretty_print_to_iodata(input, opts \\ []) do + opts = parse_opts(opts, " ", "\n", nil, " ") + + depth = :first + empty = false + + {output, _state} = pp_iodata(input, [], depth, empty, opts) + + output + end + + @doc ~S""" + Minimizes JSON-encoded `input`. + + `input` may contain multiple JSON objects or arrays, optionally + separated by whitespace (e.g., one object per line). Minimized + output will contain one object per line. No trailing newline is emitted. + + ## Options + + * `:record_separator` - controls the string used as newline (default: `"\n"`). + + ## Examples + + iex> Jason.Formatter.minimize(~s|{ "a" : "b" , "c": \n\n 2}|) + ~s|{"a":"b","c":2}| + + """ + @spec minimize(iodata, opts) :: binary + def minimize(input, opts \\ []) do + input + |> minimize_to_iodata(opts) + |> IO.iodata_to_binary() + end + + @doc ~S""" + Minimizes JSON-encoded `input` and returns iodata. + + This function should be preferred to `minimize/2`, if the minimized + JSON will be handed over to one of the IO functions or sent + over the socket. The Erlang runtime is able to leverage vectorised + writes and avoid allocating a continuous buffer for the whole + resulting string, lowering memory use and increasing performance. + """ + @spec minimize_to_iodata(iodata, opts) :: iodata + def minimize_to_iodata(input, opts) do + record = Keyword.get(opts, :record_separator, "\n") + opts = opts(indent: "", line: "", record: record, colon: "") + + depth = :first + empty = false + + {output, _state} = pp_iodata(input, [], depth, empty, opts) + + output + end + + defp parse_opts([{option, value} | opts], indent, line, record, colon) do + value = IO.iodata_to_binary(value) + case option do + :indent -> parse_opts(opts, value, line, record, colon) + :record_separator -> parse_opts(opts, indent, line, value, colon) + :after_colon -> parse_opts(opts, indent, line, record, value) + :line_separator -> parse_opts(opts, indent, value, record || value, colon) + end + end + + defp parse_opts([], indent, line, record, colon) do + opts(indent: indent, line: line, record: record || line, colon: colon) + end + + for depth <- 1..16 do + defp tab(" ", unquote(depth)), do: unquote(String.duplicate(" ", depth)) + end + + defp tab("", _), do: "" + defp tab(indent, depth), do: List.duplicate(indent, depth) + + defp pp_iodata(<<>>, output_acc, depth, empty, opts) do + {output_acc, &pp_iodata(&1, &2, depth, empty, opts)} + end + + defp pp_iodata(<>, output_acc, depth, empty, opts) do + pp_byte(byte, rest, output_acc, depth, empty, opts) + end + + defp pp_iodata([], output_acc, depth, empty, opts) do + {output_acc, &pp_iodata(&1, &2, depth, empty, opts)} + end + + defp pp_iodata([byte | rest], output_acc, depth, empty, opts) when is_integer(byte) do + pp_byte(byte, rest, output_acc, depth, empty, opts) + end + + defp pp_iodata([head | tail], output_acc, depth, empty, opts) do + {output_acc, cont} = pp_iodata(head, output_acc, depth, empty, opts) + cont.(tail, output_acc) + end + + defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ~c' \n\r\t' do + pp_iodata(rest, output, depth, empty, opts) + end + + defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ~c'{[' do + {out, depth} = + cond do + depth == :first -> {byte, 1} + depth == 0 -> {[opts(opts, :record), byte], 1} + empty -> {[opts(opts, :line), tab(opts(opts, :indent), depth), byte], depth + 1} + true -> {byte, depth + 1} + end + + empty = true + pp_iodata(rest, [output, out], depth, empty, opts) + end + + defp pp_byte(byte, rest, output, depth, true = _empty, opts) when byte in ~c'}]' do + empty = false + depth = depth - 1 + pp_iodata(rest, [output, byte], depth, empty, opts) + end + + defp pp_byte(byte, rest, output, depth, false = empty, opts) when byte in ~c'}]' do + depth = depth - 1 + out = [opts(opts, :line), tab(opts(opts, :indent), depth), byte] + pp_iodata(rest, [output, out], depth, empty, opts) + end + + defp pp_byte(byte, rest, output, depth, _empty, opts) when byte in ~c',' do + empty = false + out = [byte, opts(opts, :line), tab(opts(opts, :indent), depth)] + pp_iodata(rest, [output, out], depth, empty, opts) + end + + defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ~c':' do + out = [byte, opts(opts, :colon)] + pp_iodata(rest, [output, out], depth, empty, opts) + end + + defp pp_byte(byte, rest, output, depth, empty, opts) do + out = if empty, do: [opts(opts, :line), tab(opts(opts, :indent), depth), byte], else: byte + empty = false + + if byte == ?" do + pp_string(rest, [output, out], _in_bs = false, &pp_iodata(&1, &2, depth, empty, opts)) + else + pp_iodata(rest, [output, out], depth, empty, opts) + end + end + + defp pp_string(<<>>, output_acc, in_bs, cont) do + {output_acc, &pp_string(&1, &2, in_bs, cont)} + end + + defp pp_string(binary, output_acc, true = _in_bs, cont) when is_binary(binary) do + <> = binary + pp_string(rest, [output_acc, byte], false, cont) + end + + defp pp_string(binary, output_acc, false = _in_bs, cont) when is_binary(binary) do + case :binary.match(binary, ["\"", "\\"]) do + :nomatch -> + {[output_acc | binary], &pp_string(&1, &2, false, cont)} + {pos, 1} -> + {head, tail} = :erlang.split_binary(binary, pos + 1) + case :binary.at(binary, pos) do + ?\\ -> pp_string(tail, [output_acc | head], true, cont) + ?" -> cont.(tail, [output_acc | head]) + end + end + end + + defp pp_string([], output_acc, in_bs, cont) do + {output_acc, &pp_string(&1, &2, in_bs, cont)} + end + + defp pp_string([byte | rest], output_acc, in_bs, cont) when is_integer(byte) do + cond do + in_bs -> pp_string(rest, [output_acc, byte], false, cont) + byte == ?" -> cont.(rest, [output_acc, byte]) + true -> pp_string(rest, [output_acc, byte], byte == ?\\, cont) + end + end + + defp pp_string([head | tail], output_acc, in_bs, cont) do + {output_acc, cont} = pp_string(head, output_acc, in_bs, cont) + cont.(tail, output_acc) + end +end diff --git a/plugins/mob_iap/deps/jason/lib/fragment.ex b/plugins/mob_iap/deps/jason/lib/fragment.ex new file mode 100644 index 0000000..db26c7c --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/fragment.ex @@ -0,0 +1,21 @@ +defmodule Jason.Fragment do + @moduledoc ~S""" + Provides a way to inject an already-encoded JSON structure into a + to-be-encoded structure in optimized fashion. + + This avoids a decoding/encoding round-trip for the subpart. + + This feature can be used for caching parts of the JSON, or delegating + the generation of the JSON to a third-party system (e.g. Postgres). + """ + + defstruct [:encode] + + def new(iodata) when is_list(iodata) or is_binary(iodata) do + %__MODULE__{encode: fn _ -> iodata end} + end + + def new(encode) when is_function(encode, 1) do + %__MODULE__{encode: encode} + end +end diff --git a/plugins/mob_iap/deps/jason/lib/helpers.ex b/plugins/mob_iap/deps/jason/lib/helpers.ex new file mode 100644 index 0000000..f94678d --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/helpers.ex @@ -0,0 +1,98 @@ +defmodule Jason.Helpers do + @moduledoc """ + Provides macro facilities for partial compile-time encoding of JSON. + """ + + alias Jason.{Codegen, Fragment} + + @doc ~S""" + Encodes a JSON map from a compile-time keyword. + + Encodes the keys at compile time and strives to create as flat iodata + structure as possible to achieve maximum efficiency. Does encoding + right at the call site, but returns an `%Jason.Fragment{}` struct + that needs to be passed to one of the "main" encoding functions - + for example `Jason.encode/2` for final encoding into JSON - this + makes it completely transparent for most uses. + + Only allows keys that do not require escaping in any of the supported + encoding modes. This means only ASCII characters from the range + 0x1F..0x7F excluding '\', '/' and '"' are allowed - this also excludes + all control characters like newlines. + + Preserves the order of the keys. + + ## Example + + iex> fragment = json_map(foo: 1, bar: 2) + iex> Jason.encode!(fragment) + "{\"foo\":1,\"bar\":2}" + + """ + defmacro json_map(kv) do + kv_values = Macro.expand(kv, __CALLER__) + kv_vars = Enum.map(kv_values, fn {key, _} -> {key, generated_var(key, Codegen)} end) + + values = Enum.map(kv_values, &elem(&1, 1)) + vars = Enum.map(kv_vars, &elem(&1, 1)) + + escape = quote(do: escape) + encode_map = quote(do: encode_map) + encode_args = [escape, encode_map] + kv_iodata = Codegen.build_kv_iodata(kv_vars, encode_args) + + quote do + {unquote_splicing(vars)} = {unquote_splicing(values)} + + %Fragment{ + encode: fn {unquote(escape), unquote(encode_map)} -> + unquote(kv_iodata) + end + } + end + end + + @doc ~S""" + Encodes a JSON map from a variable containing a map and a compile-time + list of keys. + + It is equivalent to calling `Map.take/2` before encoding. Otherwise works + similar to `json_map/2`. + + ## Example + + iex> map = %{a: 1, b: 2, c: 3} + iex> fragment = json_map_take(map, [:c, :b]) + iex> Jason.encode!(fragment) + "{\"c\":3,\"b\":2}" + + """ + defmacro json_map_take(map, take) do + take = Macro.expand(take, __CALLER__) + kv = Enum.map(take, &{&1, generated_var(&1, Codegen)}) + escape = quote(do: escape) + encode_map = quote(do: encode_map) + encode_args = [escape, encode_map] + kv_iodata = Codegen.build_kv_iodata(kv, encode_args) + + quote do + case unquote(map) do + %{unquote_splicing(kv)} -> + %Fragment{ + encode: fn {unquote(escape), unquote(encode_map)} -> + unquote(kv_iodata) + end + } + + other -> + raise ArgumentError, + "expected a map with keys: #{unquote(inspect(take))}, got: #{inspect(other)}" + end + end + end + + # The same as Macro.var/2 except it sets generated: true + defp generated_var(name, context) do + {name, [generated: true], context} + end +end diff --git a/plugins/mob_iap/deps/jason/lib/jason.ex b/plugins/mob_iap/deps/jason/lib/jason.ex new file mode 100644 index 0000000..2bfa013 --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/jason.ex @@ -0,0 +1,242 @@ +defmodule Jason do + @moduledoc """ + A blazing fast JSON parser and generator in pure Elixir. + """ + + alias Jason.{Encode, Decoder, DecodeError, EncodeError, Formatter} + + @type escape :: :json | :unicode_safe | :html_safe | :javascript_safe + @type maps :: :naive | :strict + + @type encode_opt :: {:escape, escape} | {:maps, maps} | {:pretty, boolean | Formatter.opts()} + + @type keys :: :atoms | :atoms! | :strings | :copy | (String.t() -> term) + + @type strings :: :reference | :copy + + @type floats :: :native | :decimals + + @type objects :: :maps | :ordered_objects + + @type decode_opt :: {:keys, keys} | {:strings, strings} | {:floats, floats} | {:objects, objects} + + @doc """ + Parses a JSON value from `input` iodata. + + ## Options + + * `:keys` - controls how keys in objects are decoded. Possible values are: + + * `:strings` (default) - decodes keys as binary strings, + * `:atoms` - keys are converted to atoms using `String.to_atom/1`, + * `:atoms!` - keys are converted to atoms using `String.to_existing_atom/1`, + * custom decoder - additionally a function accepting a string and returning a key + is accepted. + + * `:strings` - controls how strings (including keys) are decoded. Possible values are: + + * `:reference` (default) - when possible tries to create a sub-binary into the original + * `:copy` - always copies the strings. This option is especially useful when parts of the + decoded data will be stored for a long time (in ets or some process) to avoid keeping + the reference to the original data. + + * `:floats` - controls how floats are decoded. Possible values are: + + * `:native` (default) - Native conversion from binary to float using `:erlang.binary_to_float/1`, + * `:decimals` - uses `Decimal.new/1` to parse the binary into a Decimal struct with arbitrary precision. + + * `:objects` - controls how objects are decoded. Possible values are: + + * `:maps` (default) - objects are decoded as maps + * `:ordered_objects` - objects are decoded as `Jason.OrderedObject` structs + + ## Decoding keys to atoms + + The `:atoms` option uses the `String.to_atom/1` call that can create atoms at runtime. + Since the atoms are not garbage collected, this can pose a DoS attack vector when used + on user-controlled data. + + ## Examples + + iex> Jason.decode("{}") + {:ok, %{}} + + iex> Jason.decode("invalid") + {:error, %Jason.DecodeError{data: "invalid", position: 0, token: nil}} + """ + @spec decode(iodata, [decode_opt]) :: {:ok, term} | {:error, DecodeError.t()} + def decode(input, opts \\ []) do + input = IO.iodata_to_binary(input) + Decoder.parse(input, format_decode_opts(opts)) + end + + @doc """ + Parses a JSON value from `input` iodata. + + Similar to `decode/2` except it will unwrap the error tuple and raise + in case of errors. + + ## Examples + + iex> Jason.decode!("{}") + %{} + + iex> Jason.decode!("invalid") + ** (Jason.DecodeError) unexpected byte at position 0: 0x69 ("i") + + """ + @spec decode!(iodata, [decode_opt]) :: term | no_return + def decode!(input, opts \\ []) do + case decode(input, opts) do + {:ok, result} -> result + {:error, error} -> raise error + end + end + + @doc """ + Generates JSON corresponding to `input`. + + The generation is controlled by the `Jason.Encoder` protocol, + please refer to the module to read more on how to define the protocol + for custom data types. + + ## Options + + * `:escape` - controls how strings are encoded. Possible values are: + + * `:json` (default) - the regular JSON escaping as defined by RFC 7159. + * `:javascript_safe` - additionally escapes the LINE SEPARATOR (U+2028) + and PARAGRAPH SEPARATOR (U+2029) characters to make the produced JSON + valid JavaScript. + * `:html_safe` - similar to `:javascript_safe`, but also escapes the `/` + character to prevent XSS. + * `:unicode_safe` - escapes all non-ascii characters. + + * `:maps` - controls how maps are encoded. Possible values are: + + * `:strict` - checks the encoded map for duplicate keys and raises + if they appear. For example `%{:foo => 1, "foo" => 2}` would be + rejected, since both keys would be encoded to the string `"foo"`. + * `:naive` (default) - does not perform the check. + + * `:pretty` - controls pretty printing of the output. Possible values are: + + * `true` to pretty print with default configuration + * a keyword of options as specified by `Jason.Formatter.pretty_print/2`. + + ## Examples + + iex> Jason.encode(%{a: 1}) + {:ok, ~S|{"a":1}|} + + iex> Jason.encode("\\xFF") + {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}} + + """ + @spec encode(term, [encode_opt]) :: + {:ok, String.t()} | {:error, EncodeError.t() | Exception.t()} + def encode(input, opts \\ []) do + case do_encode(input, format_encode_opts(opts)) do + {:ok, result} -> {:ok, IO.iodata_to_binary(result)} + {:error, error} -> {:error, error} + end + end + + @doc """ + Generates JSON corresponding to `input`. + + Similar to `encode/1` except it will unwrap the error tuple and raise + in case of errors. + + ## Examples + + iex> Jason.encode!(%{a: 1}) + ~S|{"a":1}| + + iex> Jason.encode!("\\xFF") + ** (Jason.EncodeError) invalid byte 0xFF in <<255>> + + """ + @spec encode!(term, [encode_opt]) :: String.t() | no_return + def encode!(input, opts \\ []) do + case do_encode(input, format_encode_opts(opts)) do + {:ok, result} -> IO.iodata_to_binary(result) + {:error, error} -> raise error + end + end + + @doc """ + Generates JSON corresponding to `input` and returns iodata. + + This function should be preferred to `encode/2`, if the generated + JSON will be handed over to one of the IO functions or sent + over the socket. The Erlang runtime is able to leverage vectorised + writes and avoid allocating a continuous buffer for the whole + resulting string, lowering memory use and increasing performance. + + ## Examples + + iex> {:ok, iodata} = Jason.encode_to_iodata(%{a: 1}) + iex> IO.iodata_to_binary(iodata) + ~S|{"a":1}| + + iex> Jason.encode_to_iodata("\\xFF") + {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}} + + """ + @spec encode_to_iodata(term, [encode_opt]) :: + {:ok, iodata} | {:error, EncodeError.t() | Exception.t()} + def encode_to_iodata(input, opts \\ []) do + do_encode(input, format_encode_opts(opts)) + end + + @doc """ + Generates JSON corresponding to `input` and returns iodata. + + Similar to `encode_to_iodata/1` except it will unwrap the error tuple + and raise in case of errors. + + ## Examples + + iex> iodata = Jason.encode_to_iodata!(%{a: 1}) + iex> IO.iodata_to_binary(iodata) + ~S|{"a":1}| + + iex> Jason.encode_to_iodata!("\\xFF") + ** (Jason.EncodeError) invalid byte 0xFF in <<255>> + + """ + @spec encode_to_iodata!(term, [encode_opt]) :: iodata | no_return + def encode_to_iodata!(input, opts \\ []) do + case do_encode(input, format_encode_opts(opts)) do + {:ok, result} -> result + {:error, error} -> raise error + end + end + + defp do_encode(input, %{pretty: true} = opts) do + case Encode.encode(input, opts) do + {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded)} + other -> other + end + end + + defp do_encode(input, %{pretty: pretty} = opts) when pretty !== false do + case Encode.encode(input, opts) do + {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded, pretty)} + other -> other + end + end + + defp do_encode(input, opts) do + Encode.encode(input, opts) + end + + defp format_encode_opts(opts) do + Enum.into(opts, %{escape: :json, maps: :naive}) + end + + defp format_decode_opts(opts) do + Enum.into(opts, %{keys: :strings, strings: :reference, floats: :native, objects: :maps}) + end +end diff --git a/plugins/mob_iap/deps/jason/lib/ordered_object.ex b/plugins/mob_iap/deps/jason/lib/ordered_object.ex new file mode 100644 index 0000000..10d92f6 --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/ordered_object.ex @@ -0,0 +1,106 @@ +defmodule Jason.OrderedObject do + @doc """ + Struct implementing a JSON object retaining order of properties. + + A wrapper around a keyword (that supports non-atom keys) allowing for + proper protocol implementations. + + Implements the `Access` behaviour and `Enumerable` protocol with + complexity similar to keywords/lists. + """ + + @behaviour Access + + @type t :: %__MODULE__{values: [{String.Chars.t(), term()}]} + + defstruct values: [] + + @doc ~S""" + Creates a new ordered object from a list of key-value pairs. + + ## Example + + iex> %{a: 1, c: 3, b: 2} + ...> |> Enum.sort() + ...> |> Jason.OrderedObject.new() + ...> |> Jason.encode!() + "{\"a\":1,\"b\":2,\"c\":3}" + + """ + def new(values) when is_list(values) do + %__MODULE__{values: values} + end + + @impl Access + def fetch(%__MODULE__{values: values}, key) do + case :lists.keyfind(key, 1, values) do + {_, value} -> {:ok, value} + false -> :error + end + end + + @impl Access + def get_and_update(%__MODULE__{values: values} = obj, key, function) do + {result, new_values} = get_and_update(values, [], key, function) + {result, %{obj | values: new_values}} + end + + @impl Access + def pop(%__MODULE__{values: values} = obj, key, default \\ nil) do + case :lists.keyfind(key, 1, values) do + {_, value} -> {value, %{obj | values: delete_key(values, key)}} + false -> {default, obj} + end + end + + defp get_and_update([{key, current} | t], acc, key, fun) do + case fun.(current) do + {get, value} -> + {get, :lists.reverse(acc, [{key, value} | t])} + + :pop -> + {current, :lists.reverse(acc, t)} + + other -> + raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}" + end + end + + defp get_and_update([{_, _} = h | t], acc, key, fun), do: get_and_update(t, [h | acc], key, fun) + + defp get_and_update([], acc, key, fun) do + case fun.(nil) do + {get, update} -> + {get, [{key, update} | :lists.reverse(acc)]} + + :pop -> + {nil, :lists.reverse(acc)} + + other -> + raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}" + end + end + + defp delete_key([{key, _} | tail], key), do: delete_key(tail, key) + defp delete_key([{_, _} = pair | tail], key), do: [pair | delete_key(tail, key)] + defp delete_key([], _key), do: [] +end + +defimpl Enumerable, for: Jason.OrderedObject do + def count(%{values: []}), do: {:ok, 0} + def count(_obj), do: {:error, __MODULE__} + + def member?(%{values: []}, _value), do: {:ok, false} + def member?(_obj, _value), do: {:error, __MODULE__} + + def slice(%{values: []}), do: {:ok, 0, fn _, _ -> [] end} + def slice(_obj), do: {:error, __MODULE__} + + def reduce(%{values: values}, acc, fun), do: Enumerable.List.reduce(values, acc, fun) +end + +defimpl Jason.Encoder, for: Jason.OrderedObject do + def encode(%{values: values}, opts) do + Jason.Encode.keyword(values, opts) + end +end diff --git a/plugins/mob_iap/deps/jason/lib/sigil.ex b/plugins/mob_iap/deps/jason/lib/sigil.ex new file mode 100644 index 0000000..16aec39 --- /dev/null +++ b/plugins/mob_iap/deps/jason/lib/sigil.ex @@ -0,0 +1,84 @@ +defmodule Jason.Sigil do + @doc ~S""" + Handles the sigil `~j` for JSON strings. + + Calls `Jason.decode!/2` with modifiers mapped to options. + + Given a string literal without interpolations, decodes the + string at compile-time. + + ## Modifiers + + See `Jason.decode/2` for detailed descriptions. + + * `a` - equivalent to `{:keys, :atoms}` option + * `A` - equivalent to `{:keys, :atoms!}` option + * `r` - equivalent to `{:strings, :reference}` option + * `c` - equivalent to `{:strings, :copy}` option + + ## Examples + + iex> ~j"0" + 0 + + iex> ~j"[1, 2, 3]" + [1, 2, 3] + + iex> ~j'"string"'r + "string" + + iex> ~j"{}" + %{} + + iex> ~j'{"atom": "value"}'a + %{atom: "value"} + + iex> ~j'{"#{:j}": #{~c'"j"'}}'A + %{j: "j"} + + """ + defmacro sigil_j(term, modifiers) + + defmacro sigil_j({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do + Macro.escape(Jason.decode!(string, mods_to_opts(modifiers))) + end + + defmacro sigil_j(term, modifiers) do + quote(do: Jason.decode!(unquote(term), unquote(mods_to_opts(modifiers)))) + end + + @doc ~S""" + Handles the sigil `~J` for raw JSON strings. + + Decodes a raw string ignoring Elixir interpolations and + escape characters at compile-time. + + ## Examples + + iex> ~J'"#{string}"' + "\#{string}" + + iex> ~J'"\u0078\\y"' + "x\\y" + + iex> ~J'{"#{key}": "#{}"}'a + %{"\#{key}": "\#{}"} + """ + defmacro sigil_J(term, modifiers) + + defmacro sigil_J({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do + Macro.escape(Jason.decode!(string, mods_to_opts(modifiers))) + end + + @spec mods_to_opts(charlist) :: [Jason.decode_opt()] + defp mods_to_opts(modifiers) do + modifiers + |> Enum.map(fn + ?a -> {:keys, :atoms} + ?A -> {:keys, :atoms!} + ?r -> {:strings, :reference} + ?c -> {:strings, :copy} + m -> raise ArgumentError, "unknown sigil modifier #{<>}" + end) + end +end diff --git a/plugins/mob_iap/deps/jason/mix.exs b/plugins/mob_iap/deps/jason/mix.exs new file mode 100644 index 0000000..37e51c8 --- /dev/null +++ b/plugins/mob_iap/deps/jason/mix.exs @@ -0,0 +1,75 @@ +defmodule Jason.Mixfile do + use Mix.Project + + @source_url "https://github.com/michalmuskala/jason" + @version "1.4.5" + + def project() do + [ + app: :jason, + version: @version, + elixir: "~> 1.4", + start_permanent: Mix.env() == :prod, + consolidate_protocols: Mix.env() != :test, + deps: deps(), + preferred_cli_env: [docs: :docs], + dialyzer: dialyzer(), + description: description(), + package: package(), + docs: docs() + ] + end + + def application() do + [ + extra_applications: [] + ] + end + + defp deps() do + [ + {:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", optional: true}, + {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}, + {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, + ] ++ maybe_stream_data() + end + + defp maybe_stream_data() do + if Version.match?(System.version(), "~> 1.12") do + [{:stream_data, "~> 1.0", only: :test}] + else + [] + end + end + + defp dialyzer() do + [ + plt_add_apps: [:decimal] + ] + end + + defp description() do + """ + A blazing fast JSON parser and generator in pure Elixir. + """ + end + + defp package() do + [ + maintainers: ["Michał Muskała"], + licenses: ["Apache-2.0"], + links: %{"GitHub" => @source_url} + ] + end + + defp docs() do + [ + main: "readme", + name: "Jason", + source_ref: "v#{@version}", + canonical: "http://hexdocs.pm/jason", + source_url: @source_url, + extras: ["README.md", "CHANGELOG.md", "LICENSE"] + ] + end +end diff --git a/plugins/mob_iap/deps/makeup/.hex b/plugins/mob_iap/deps/makeup/.hex new file mode 100644 index 0000000000000000000000000000000000000000..9749a3c924a3444b43f326a0de16a3071f333b46 GIT binary patch literal 262 zcmZ9H%WlIU5JlZMeXQ~+5n~<$yZ%Wvz)(9W*mB~MeSf^T%kGYjj;?fn#>3dxe&h!o zsJnMv_fn6AGUpvLU3pvT>o>2rX)e2ahVyWS`j)BCXVYKJJ=H!>k4rsGrSSR@Mh@58 zmiPRfouB~0Xl)%Qsg*<<8+Za2l>kQRn2}kSS^BY|3{-V$|`Hn t3r{Q(lQuSj014obR2W#H(1zM3TjhqqC*uhrv 1.2.2` onwards to prepare for future deprecations. + +### 1.0.5 - 2020-10-02 + +#### Bugfix #1 + +Fix serious bug in the `Makeup.Lexer.Combinators.lexeme/1` when given a list of unicode characters. + +Before this fix, the following combinator: + +``` +import Makeup.Lexer.Combinators + +characters_lexeme = + utf8_char([]) + |> utf8_char([]) + |> lexeme() + |> token(:character_lexeme) +``` + +when given as input a string like `"àó"` would return the following invalid Unicode: + +``` +[{:character_lexeme, %{}, <<225, 242>>}] +``` + +instead of + +``` +[{:character_lexeme, %{}, "áò"}] +``` + +This was caused by the use of `IO.iodata_to_binary/1` instead of (the slower) `to_string()`. This was a problem because `IO.iodata_to_binary/1` would put any byte in a binary instead of (correctly) encoding bytes > 128 as a Unicode character. This is not a problem with `IO.iodata_to_binary/1` it's a problem with using that function when we want to encode characters as Unicode strings. + +#### Bugfix #2 + +Fixed an edge case in the HTML encoder in the formatter. + + +### 1.0.4 - 2020-09-25 + +Fix warnings and update NimbleParsec dependency. + + +### 1.0.3 - 2020-06-07 + +Allow styles to be given as atoms when generating stylesheets. + + +### 1.0.2 - 2020-05-28 + +Update NimbleParsec dependency. + + +### 1.0.1 - 2020-03-24 + +Remove warnings on recent Elixir versions. + + +### 1.0.0 - 2019-07-15 + +Upgrade the HTML formatter so that you can use different kinds of tags around the tokens. + +Previous versions always used the `` tag (e.g. `name`). +You can now use other HTML tags using the `:highlight_tag` option: + +```elixir +alias Makeup.Formatters.HTML.HTMLFormatter +HTMLFormatter.format_as_iolist(tokens, highlight_tag: "font") +``` + + +### 0.8.0 - 2019-01-01 + +This release adds a new file extension registry for lexers. +This means that it's now possible to lookup lexers by file extension. +Since the previous release it was already possible to lookup lexers by language name. +For example: + +```elixir +elixir_lexer = Makeup.Registry.fetch_lexer_by_extension!("elixir") +``` + +Now you can also do this this: + +```elixir +elixir_lexer = Makeup.Registry.get_lexer_by_extension!("ex") +``` + +Documentation of the registry functionality was also improved. + + +### 0.7.0 - 2018-12-30 + +Adds a register where lexer developers can register their lexers. +This allows one to pick a lexer based on the language name. + +The goal is for Makeup to be aware of the available lexers. +In a project such as ExDoc this allows Makeup to support an unbounded number of languages just by declaring the lexer as a dependency. + + +### 0.6.0 - 2018-12-22 + +Fixes the combinators affected by compatibility breaks in `nimble_parsec` from `0.4.x` to `0.5.x`. + +Pins `nimble_parsec` to version `0.5.0`. diff --git a/plugins/mob_iap/deps/makeup/LICENSE b/plugins/mob_iap/deps/makeup/LICENSE new file mode 100644 index 0000000..e1b1566 --- /dev/null +++ b/plugins/mob_iap/deps/makeup/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2021 by the respective authors (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/mob_iap/deps/makeup/README.md b/plugins/mob_iap/deps/makeup/README.md new file mode 100644 index 0000000..5847fdc --- /dev/null +++ b/plugins/mob_iap/deps/makeup/README.md @@ -0,0 +1,114 @@ +# Makeup + +[![Module Version](https://img.shields.io/hexpm/v/makeup.svg)](https://hex.pm/packages/makeup) +[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/makeup/) + +## Introduction + +Makeup is a "generic syntax highlighter suitable for use in code hosting, forums, wikis or other applications that need to prettify source code" . This tagline was shamelessly stolen from the [Pygments website](http://pygments.org/). + +Pygments the major inspiration for this package, and the structure is basically the same. +It has **lexers**, **formatters** and **styles**. + +* **Lexers** turn the source code into a list of tokens. +* **Formatters** turn the list of tokens into something else (HTML, TeX, images, etc.). + Currently only an HTML formatter exists. +* **Styles** customize the output of the formatter. + Makeup supports all Pygments' styles (in fact, they were converted from the Python's source). + New custom styles can be added to makeup itself, or defined in the Project that uses it. + +## Demo + +To see a sample of Makeup's output, go check the [demo](https://elixir-makeup.github.io/makeup_demo/). +Please note that not all styles define all differences between tokens. +In a given style, strings and characters might be rendered in the same color while in others , the colors might be different. + +That is style-dependent. + +Some of the richer styles are +the Tango style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#tango)), +the Colorful style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#colorful)), +the Default style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#default)), and +the Friendly style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#friendly)). + +## Supported Languages + +The supported source languages are: + + * [Elixir](https://github.com/elixir-makeup/makeup_elixir) + * [Erlang](https://github.com/elixir-makeup/makeup_erlang) + * [C](https://github.com/elixir-makeup/makeup_c) + * [HTML](https://github.com/elixir-makeup/makeup_html) + * [Diff](https://github.com/elixir-makeup/makeup_diff) + * [JSON](https://github.com/elixir-makeup/makeup_json) + * [(H)EEx](https://github.com/elixir-makeup/makeup_eex) + +## Installation + +The package can be installed by adding `makeup` and `makeup_elixir` (required +for the ElixirLexer) to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:makeup, "x.y.z"}, + {:makeup_elixir, "x.y.z"} + ] +end +``` + +Documentation can be found at [https://hexdocs.pm/makeup](https://hexdocs.pm/makeup). + +## Changes + +Changes from previous versions are details in the [Changelog](CHANGELOG.md). + +## Quickstart + +To highlight some Elixir code (newlines added for clarity): + +```elixir +Makeup.highlight(source) +# "
    
    +#   x 
    +#   + 1
    +# 
    \n" +``` + +As you can see, the default HTML formatter uses CSS classes. +You'll need a CSS stylesheet to get the different colors and styles. + +To generate a stylesheet: + +``` +Makeup.stylesheet(style) # by default, the StyleMap.default style is used. +# ... output omitted +``` + +## Advantages over Pygments + +One of the greatest advantages is that it runs on the BEAM, so it can be used with Elixir projects without external dependencies. + +Another advantage is that the way lexers are written, we can be a lot smarter than Pygments in processing the output. + +For the developer, lexers are also easier to write than the Pygments lexers, because we use a PEG parser. +Most Pygments lexers use something like a state table that works based on regex matches, +and uses the results of those matches to switch to another state. +Using a PEG parser we can define the grammar in a more natural way. + + +The lexers are written using the excellent [NimbleParsec](https://github.com/dashbitco/nimble_parsec) parser. + +## Disadvantages over Pygments + +It supports fewer languages. + +## Documentation on how to write a new lexer + +Contributions are highly appreciated. The most direct way you can contribute to Makeup is by writing a new lexer. You can find some information here: [CONTRIBUTING.md](CONTRIBUTING.md) + +## LICENSE + +Makeup is licensed under the BSD license. +This is the same license as the Pygments Makeup uses and +it seems to be compatible with the licenses used by all the dependencies. diff --git a/plugins/mob_iap/deps/makeup/hex_metadata.config b/plugins/mob_iap/deps/makeup/hex_metadata.config new file mode 100644 index 0000000..30db0f9 --- /dev/null +++ b/plugins/mob_iap/deps/makeup/hex_metadata.config @@ -0,0 +1,35 @@ +{<<"links">>, + [{<<"Changelog">>,<<"https://hexdocs.pm/makeup/changelog.html">>}, + {<<"Contributing">>,<<"https://hexdocs.pm/makeup/contributing.html">>}, + {<<"GitHub">>,<<"https://github.com/elixir-makeup/makeup">>}]}. +{<<"name">>,<<"makeup">>}. +{<<"version">>,<<"1.2.1">>}. +{<<"description">>, + <<"Syntax highlighter for source code in the style of Pygments.">>}. +{<<"elixir">>,<<"~> 1.12">>}. +{<<"app">>,<<"makeup">>}. +{<<"licenses">>,[<<"BSD-2-Clause">>]}. +{<<"requirements">>, + [[{<<"name">>,<<"nimble_parsec">>}, + {<<"app">>,<<"nimble_parsec">>}, + {<<"optional">>,false}, + {<<"requirement">>,<<"~> 1.4">>}, + {<<"repository">>,<<"hexpm">>}]]}. +{<<"files">>, + [<<"lib">>,<<"lib/makeup">>,<<"lib/makeup/token">>, + <<"lib/makeup/token/utils">>,<<"lib/makeup/token/utils/hierarchy.ex">>, + <<"lib/makeup/token/utils.ex">>,<<"lib/makeup/registry.ex">>, + <<"lib/makeup/lexer">>,<<"lib/makeup/lexer/postprocess.ex">>, + <<"lib/makeup/lexer/types.ex">>,<<"lib/makeup/lexer/groups.ex">>, + <<"lib/makeup/lexer/combinators.ex">>,<<"lib/makeup/formatter.ex">>, + <<"lib/makeup/formatters">>,<<"lib/makeup/formatters/html">>, + <<"lib/makeup/formatters/html/html_formatter.ex">>, + <<"lib/makeup/formatters/html/scripts">>, + <<"lib/makeup/formatters/html/scripts/group_highlighter_javascript.js">>, + <<"lib/makeup/styles">>,<<"lib/makeup/styles/html">>, + <<"lib/makeup/styles/html/token_style.ex">>, + <<"lib/makeup/styles/html/style_map.ex">>, + <<"lib/makeup/styles/html/style.ex">>,<<"lib/makeup/application.ex">>, + <<"lib/makeup/lexer.ex">>,<<"lib/makeup.ex">>,<<"mix.exs">>,<<"README.md">>, + <<"LICENSE">>,<<"CHANGELOG.md">>]}. +{<<"build_tools">>,[<<"mix">>]}. diff --git a/plugins/mob_iap/deps/makeup/lib/makeup.ex b/plugins/mob_iap/deps/makeup/lib/makeup.ex new file mode 100644 index 0000000..a8685a2 --- /dev/null +++ b/plugins/mob_iap/deps/makeup/lib/makeup.ex @@ -0,0 +1,82 @@ +defmodule Makeup do + @moduledoc """ + Syntax highlighting library for code, inspired by Pygments. + + By default, it doesn't include any lexers. You must import + them separately (even the Elixir lexer). + """ + alias Makeup.Formatters.HTML.HTMLFormatter + alias Makeup.Lexers.ElixirLexer + alias Makeup.Registry + + @doc """ + Highlights the given string using the given lexer and formatter. + + By default it highlight the Elixir language using + `Makeup.Formatters.HTML.HTMLFormatter`. + + ## Options: + + - `:lexer` - module name of the lexer to use (default: `Makeup.Lexers.ElixirLexer`) + - `:lexer_options` - list of options for the lexer + - `:formatter` - module name of the formatter to use (defult: `Makeup.Formatters.HTML.HTMLFormatter`) + - `:formatter_options` - list of options for the formatter. For the included HTMLFormatter, that's: + - `:css_class` - css class(es) of the main `
    ` element (default: `"highlight"`)
    +    - `:highlight_tag` - tag that wraps every token (default: `"span"`)
    +  """
    +  def highlight(source, options \\ []) do
    +    {lexer, lexer_options} = fetch_lexer(options)
    +
    +    formatter =
    +      case options[:formatter] do
    +        nil -> HTMLFormatter
    +        module when is_atom(module) -> module
    +      end
    +
    +    formatter_options = Keyword.get(options, :formatter_options, [])
    +    tokens = lexer.lex(source, lexer_options)
    +    formatter.format_as_binary(tokens, formatter_options)
    +  end
    +
    +  @doc """
    +  Convenience for formatting as the inner bits of HTML.
    +  """
    +  def highlight_inner_html(source, options \\ []) do
    +    {lexer, lexer_options} = fetch_lexer(options)
    +    formatter_options = Keyword.get(options, :formatter_options, [])
    +
    +    tokens = lexer.lex(source, lexer_options)
    +    HTMLFormatter.format_inner_as_binary(tokens, formatter_options)
    +  end
    +
    +  defp fetch_lexer(options) do
    +    {lexer, lexer_options} =
    +      case options[:lexer] do
    +        nil -> {ElixirLexer, []}
    +        module when is_atom(module) -> {module, []}
    +        name -> Registry.fetch_lexer_by_name!(name)
    +      end
    +
    +    {lexer, Keyword.merge(lexer_options, Keyword.get(options, :lexer_options, []))}
    +  end
    +
    +  @doc """
    +  Generates a CSS stylesheet for highlighted code for the given style.
    +
    +  It expects a `style`, either as an atom name or as "style map",
    +  and the `css_class` as the top level class for highlighted code.
    +
    +  For example, if the `css_class` is `"highlight"` (the default), the stylesheet
    +  has the form:
    +
    +  ```css
    +  .highlight .someclass {...}
    +  .highlight .anotherclass {...}
    +  ```
    +
    +  See `Makeup.Styles.HTML.StyleMap` for all style maps.
    +  """
    +  def stylesheet(style \\ :default_style, css_class \\ "highlight") do
    +    HTMLFormatter.stylesheet(style, css_class)
    +  end
    +end
    diff --git a/plugins/mob_iap/deps/makeup/lib/makeup/application.ex b/plugins/mob_iap/deps/makeup/lib/makeup/application.ex
    new file mode 100644
    index 0000000..b730716
    --- /dev/null
    +++ b/plugins/mob_iap/deps/makeup/lib/makeup/application.ex
    @@ -0,0 +1,12 @@
    +defmodule Makeup.Application do
    +  @moduledoc false
    +  use Application
    +
    +  alias Makeup.Registry
    +
    +  def start(_type, _args) do
    +    Registry.create_name_registry()
    +    Registry.create_extension_registry()
    +    Supervisor.start_link([], strategy: :one_for_one)
    +  end
    +end
    diff --git a/plugins/mob_iap/deps/makeup/lib/makeup/formatter.ex b/plugins/mob_iap/deps/makeup/lib/makeup/formatter.ex
    new file mode 100644
    index 0000000..c08e9c9
    --- /dev/null
    +++ b/plugins/mob_iap/deps/makeup/lib/makeup/formatter.ex
    @@ -0,0 +1,3 @@
    +defmodule Makeup.Formatter do
    +  @moduledoc false
    +end
    diff --git a/plugins/mob_iap/deps/makeup/lib/makeup/formatters/html/html_formatter.ex b/plugins/mob_iap/deps/makeup/lib/makeup/formatters/html/html_formatter.ex
    new file mode 100644
    index 0000000..27deabb
    --- /dev/null
    +++ b/plugins/mob_iap/deps/makeup/lib/makeup/formatters/html/html_formatter.ex
    @@ -0,0 +1,146 @@
    +defmodule Makeup.Formatters.HTML.HTMLFormatter do
    +  @moduledoc """
    +  Turns a list of tokens into HTML fragments.
    +  """
    +
    +  @group_highlight_js "lib/makeup/formatters/html/scripts/group_highlighter_javascript.js" |> File.read!
    +
    +  defp render_token(escaped_value, css_class, meta, highlight_tag) do
    +    group_id = meta[:group_id]
    +    selectable = Map.get(meta, :selectable, [])
    +
    +    classes = [
    +      css_class || [],
    +      if selectable == false do " unselectable" else [] end
    +    ]
    +
    +    [
    +      "<",
    +      highlight_tag,
    +      ~S( class="),
    +      classes,
    +      ~S("),
    +      if group_id do [~S( data-group-id="), group_id, ~S(")] else [] end,
    +      ">",
    +      escaped_value,
    +      "",
    +    ]
    +  end
    +
    +  @doc """
    +  Format a single token into an iolist.
    +  """
    +  def format_token({tag, meta, value}, highlight_tag) do
    +    escaped_value = escape(value)
    +    css_class = Makeup.Token.Utils.css_class_for_token_type(tag)
    +    render_token(escaped_value, css_class, meta, highlight_tag)
    +  end
    +
    +  defp escape_for(?&), do: "&"
    +
    +  defp escape_for(?<), do: "<"
    +
    +  defp escape_for(?>), do: ">"
    +
    +  defp escape_for(?"), do: """
    +
    +  defp escape_for(?'), do: "'"
    +
    +  defp escape_for(c) when is_integer(c) and c <= 127, do: c
    +
    +  defp escape_for(c) when is_integer(c) and c >= 128, do: << c :: utf8 >>
    +
    +  defp escape_for(string) when is_binary(string) do
    +    string
    +    |> to_charlist()
    +    |> Enum.map(&escape_for/1)
    +  end
    +
    +  defp escape(iodata) when is_list(iodata) do
    +    iodata
    +    |> :lists.flatten()
    +    |> Enum.map(&escape_for/1)
    +  end
    +
    +  defp escape(other) when is_binary(other) do
    +    escape_for(other)
    +  end
    +
    +  defp escape(c) when is_integer(c) do
    +    [escape_for(c)]
    +  end
    +
    +  defp escape(other) do
    +    raise "Found `#{inspect(other)}` inside what should be an iolist"
    +  end
    +
    +  @doc """
    +  Turns a list of tokens into an iolist which represents an HTML fragment.
    +  This fragment can be embedded directly into an HTML document.
    +  """
    +  def format_inner_as_iolist(tokens, opts) do
    +    highlight_tag = Keyword.get(opts, :highlight_tag, "span")
    +    Enum.map(tokens, &format_token(&1, highlight_tag))
    +  end
    +
    +  @doc """
    +  Turns a list of tokens into an HTML fragment.
    +  This fragment can be embedded directly into an HTML document.
    +  """
    +  def format_inner_as_binary(tokens, opts) do
    +    tokens
    +    |> format_inner_as_iolist(opts)
    +    |> IO.iodata_to_binary
    +  end
    +
    +  @doc """
    +  Turns a list of tokens into an iolist which represents an HTML fragment.
    +  This fragment can be embedded directly into an HTML document.
    +  """
    +  def format_as_iolist(tokens, opts \\ []) do
    +    css_class = Keyword.get(opts, :css_class, "highlight")
    +    inner = format_inner_as_iolist(tokens, opts)
    +
    +    [
    +      ~S(
    ),
    +      inner,
    +      ~S(
    ) + ] + end + + @doc """ + Turns a list of tokens into an HTML fragment. + This fragment can be embedded directly into an HTML document. + """ + def format_as_binary(tokens, opts \\ []) do + tokens + |> format_as_iolist(opts) + |> IO.iodata_to_binary + end + + @doc """ + Return the CSS stylesheet for a given style. + """ + def stylesheet(style \\ :default_style, css_class \\ "highlight") + + def stylesheet(style, css_class) when is_atom(style) do + stylesheet(apply(Makeup.Styles.HTML.StyleMap, style, []), css_class) + end + + def stylesheet(style, css_class) do + Makeup.Styles.HTML.Style.stylesheet(style, css_class) + end + + @doc """ + Return a JavaScript snippet to highlight code on mouseover. + This is "raw" javascript, and for inclusion in an HTML file + it must be wrapped in a ` - - - - - - - -``` - -For more details and configuration options, see the [KaTeX Auto-render Extension](https://katex.org/docs/autorender.html). - -### Rendering Vega-Lite plots - -Snippets are also objects you may want to render in a special manner. For example, assuming your Markdown includes Vega-Lite specification in `vega-lite` code snippets: - -```html - - - - -``` - -For more details and configuration options, see [vega/vega-embed](https://github.com/vega/vega-embed). - -### Rendering Mermaid graphs - -Similarly to the example above, if your Markdown includes Mermaid graph specification in `mermaid` code snippets: - -```html - - -``` - -For more details and configuration options, see the [Mermaid usage docs](https://mermaid-js.github.io/mermaid/#/usage). - -## Contributing - -The easiest way to test changes to ExDoc is to locally rebuild the app and its own documentation: - - 1. Run `mix setup` to install all dependencies - 2. Run `mix build` to generate the docs and open up the generated `doc/index.html` - 3. (optional) Run `erl -S httpd serve doc/` to serve the docs locally - 4. (optional) Run `npm run --prefix assets build:watch` if working on assets for automatic recompilation - 5. Run `mix lint` to check linting and formatting (and `mix fix` to automatically fix it) - 6. (important) Do not add the files in the `formatters/` directory to your commits, those will be handled by the maintainers - -See the README in the `assets/` directory for more information on working on the assets. - -## License - -ExDoc source code is released under the Apache 2 License. The generated contents, however, are under different licenses based on projects used to help render HTML, including CSS, JS, and other assets. - -Any documentation generated by ExDoc, or any documentation generated by any "Derivative Works" (as specified in the Apache 2 License), must include a direct, readable, and visible link to the [ExDoc repository](https://github.com/elixir-lang/ex_doc) on each rendered material. For HTML pages, every single page is a rendered material. For PDF, EPUB and other ebook formats, the whole body of documentation is a rendered material. diff --git a/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-4WIP524F.js b/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-4WIP524F.js deleted file mode 100644 index d711b70..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-4WIP524F.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{var s=document.querySelector.bind(document),o=document.querySelectorAll.bind(document);function r(e){document.readyState!=="loading"?e():document.addEventListener("DOMContentLoaded",e)}var l="hll";window.addEventListener("exdoc:loaded",t);function t(){o("[data-group-id]").forEach(e=>{e.addEventListener("mouseenter",i),e.addEventListener("mouseleave",i)})}function i(e){let n=e.currentTarget,a=e.type==="mouseenter",c=n.getAttribute("data-group-id");n.parentElement.querySelectorAll(`[data-group-id="${c}"]`).forEach(u=>{u.classList.toggle(l,a)})}r(()=>{t()});})(); diff --git a/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-elixir-A3GHWQ7R.css b/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-elixir-A3GHWQ7R.css deleted file mode 100644 index 3a3f6d7..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-elixir-A3GHWQ7R.css +++ /dev/null @@ -1 +0,0 @@ -:root{--main: hsl(250, 68%, 69%);--mainDark: hsl(250, 68%, 59%);--mainDarkest: hsl(250, 68%, 49%);--mainLight: hsl(250, 68%, 74%);--mainLightest: hsl(250, 68%, 79%);--searchBarFocusColor: #8E7CE6;--searchBarBorderColor: rgba(142, 124, 230, .25);--link-color: var(--mainDark);--link-visited-color: var(--mainDarkest)}body.dark{--link-color: var(--mainLightest);--link-visited-color: var(--mainLight)}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}.content-inner section.admonition{border-left:solid 4px;color:var(--black);font-size:.9em;line-height:1.4em;margin-bottom:1.5em;margin-left:5px;padding:7px 15px;page-break-inside:avoid}.content-inner section.admonition.warning{background-color:var(--warningBackground);border-left-color:var(--warningHeading)}.content-inner section.admonition.error{background-color:var(--errorBackground);border-left-color:var(--errorHeading)}.content-inner section.admonition.info{background-color:var(--infoBackground);border-left-color:var(--infoHeading)}.content-inner section.admonition.neutral{background-color:var(--neutralBackground);border-left-color:var(--neutralHeading)}.content-inner section.admonition.tip{background-color:var(--tipBackground);border-left-color:var(--tipHeading)}.content-inner section.admonition>.admonition-title{margin:0 10px 5px 0;font-style:normal;font-weight:700}.content-inner section.admonition>.admonition-title.warning{color:var(--warningHeading)}.content-inner section.admonition>.admonition-title.error{color:var(--errorHeading)}.content-inner section.admonition>.admonition-title.info{color:var(--infoHeading)}.content-inner section.admonition>.admonition-title.neutral{color:var(--neutralHeading)}.content-inner section.admonition>.admonition-title.tip{color:var(--tipHeading)}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}body{display:block;font-size:1em;line-height:1.2;padding-left:0;padding-right:0;margin:0 5pt}nav>ol{list-style-type:square}nav>ol ol{list-style-type:disc}.title-container{text-align:center}img[src*="#gh-dark-mode-only"]{display:none} diff --git a/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-erlang-IMUVAO3R.css b/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-erlang-IMUVAO3R.css deleted file mode 100644 index d454d82..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/epub/dist/epub-erlang-IMUVAO3R.css +++ /dev/null @@ -1 +0,0 @@ -:root{--main: hsl(0, 100%, 44%);--mainDark: hsl(0, 100%, 34%);--mainDarkest: hsl(0, 100%, 24%);--mainLight: hsl(0, 100%, 64%);--mainLightest: hsl(0, 100%, 74%);--searchBarFocusColor: hsl(0, 100%, 50%);--searchBarBorderColor: rgb(255, 71, 71, .1);--link-color: hsl(212, 96%, 45%);--link-visited-color: hsl(212, 96%, 40%)}body.dark{--link-color: hsl(212, 56%, 72%);--link-visited-color: hsl(212, 56%, 67%)}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}.content-inner section.admonition{border-left:solid 4px;color:var(--black);font-size:.9em;line-height:1.4em;margin-bottom:1.5em;margin-left:5px;padding:7px 15px;page-break-inside:avoid}.content-inner section.admonition.warning{background-color:var(--warningBackground);border-left-color:var(--warningHeading)}.content-inner section.admonition.error{background-color:var(--errorBackground);border-left-color:var(--errorHeading)}.content-inner section.admonition.info{background-color:var(--infoBackground);border-left-color:var(--infoHeading)}.content-inner section.admonition.neutral{background-color:var(--neutralBackground);border-left-color:var(--neutralHeading)}.content-inner section.admonition.tip{background-color:var(--tipBackground);border-left-color:var(--tipHeading)}.content-inner section.admonition>.admonition-title{margin:0 10px 5px 0;font-style:normal;font-weight:700}.content-inner section.admonition>.admonition-title.warning{color:var(--warningHeading)}.content-inner section.admonition>.admonition-title.error{color:var(--errorHeading)}.content-inner section.admonition>.admonition-title.info{color:var(--infoHeading)}.content-inner section.admonition>.admonition-title.neutral{color:var(--neutralHeading)}.content-inner section.admonition>.admonition-title.tip{color:var(--tipHeading)}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}body{display:block;font-size:1em;line-height:1.2;padding-left:0;padding-right:0;margin:0 5pt}nav>ol{list-style-type:square}nav>ol ol{list-style-type:disc}.title-container{text-align:center}img[src*="#gh-dark-mode-only"]{display:none} diff --git a/plugins/mob_iap/deps/ex_doc/formatters/epub/metainfo/com.apple.ibooks.display-options.xml b/plugins/mob_iap/deps/ex_doc/formatters/epub/metainfo/com.apple.ibooks.display-options.xml deleted file mode 100644 index d9f042a..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/epub/metainfo/com.apple.ibooks.display-options.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/plugins/mob_iap/deps/ex_doc/formatters/epub/metainfo/container.xml b/plugins/mob_iap/deps/ex_doc/formatters/epub/metainfo/container.xml deleted file mode 100644 index 8b80cd0..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/epub/metainfo/container.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-Y2MUTVIN.js b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-Y2MUTVIN.js deleted file mode 100644 index b3ec4cd..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-Y2MUTVIN.js +++ /dev/null @@ -1,210 +0,0 @@ -(()=>{var _s=Object.create;var jn=Object.defineProperty;var Ls=Object.getOwnPropertyDescriptor;var Ts=Object.getOwnPropertyNames;var Ps=Object.getPrototypeOf,Os=Object.prototype.hasOwnProperty;var _=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var As=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Ts(t))!Os.call(e,i)&&i!==n&&jn(e,i,{get:()=>t[i],enumerable:!(r=Ls(t,i))||r.enumerable});return e};var Y=(e,t,n)=>(n=e!=null?_s(Ps(e)):{},As(t||!e||!e.__esModule?jn(n,"default",{value:e,enumerable:!0}):n,e));var fr=_((mc,hr)=>{var dr="Expected a function",ur=NaN,Ds="[object Symbol]",Bs=/^\s+|\s+$/g,Qs=/^[-+]0x[0-9a-f]+$/i,qs=/^0b[01]+$/i,Fs=/^0o[0-7]+$/i,Vs=parseInt,$s=typeof global=="object"&&global&&global.Object===Object&&global,Us=typeof self=="object"&&self&&self.Object===Object&&self,js=$s||Us||Function("return this")(),Ws=Object.prototype,zs=Ws.toString,Gs=Math.max,Ks=Math.min,Ht=function(){return js.Date.now()};function Ys(e,t,n){var r,i,s,o,a,l,u=0,c=!1,d=!1,h=!0;if(typeof e!="function")throw new TypeError(dr);t=cr(t)||0,$e(n)&&(c=!!n.leading,d="maxWait"in n,s=d?Gs(cr(n.maxWait)||0,t):s,h="trailing"in n?!!n.trailing:h);function p(S){var I=r,$=i;return r=i=void 0,u=S,o=e.apply($,I),o}function g(S){return u=S,a=setTimeout(w,t),c?p(S):o}function m(S){var I=S-l,$=S-u,ee=t-I;return d?Ks(ee,s-$):ee}function v(S){var I=S-l,$=S-u;return l===void 0||I>=t||I<0||d&&$>=s}function w(){var S=Ht();if(v(S))return x(S);a=setTimeout(w,m(S))}function x(S){return a=void 0,h&&r?p(S):(r=i=void 0,o)}function P(){a!==void 0&&clearTimeout(a),u=0,r=l=i=a=void 0}function Q(){return a===void 0?o:x(Ht())}function V(){var S=Ht(),I=v(S);if(r=arguments,i=this,l=S,I){if(a===void 0)return g(l);if(d)return a=setTimeout(w,t),p(l)}return a===void 0&&(a=setTimeout(w,t)),o}return V.cancel=P,V.flush=Q,V}function Js(e,t,n){var r=!0,i=!0;if(typeof e!="function")throw new TypeError(dr);return $e(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),Ys(e,t,{leading:r,maxWait:t,trailing:i})}function $e(e){var t=typeof e;return!!e&&(t=="object"||t=="function")}function Xs(e){return!!e&&typeof e=="object"}function Zs(e){return typeof e=="symbol"||Xs(e)&&zs.call(e)==Ds}function cr(e){if(typeof e=="number")return e;if(Zs(e))return ur;if($e(e)){var t=typeof e.valueOf=="function"?e.valueOf():e;e=$e(t)?t+"":t}if(typeof e!="string")return e===0?e:+e;e=e.replace(Bs,"");var n=qs.test(e);return n||Fs.test(e)?Vs(e.slice(2),n?2:8):Qs.test(e)?ur:+e}hr.exports=Js});var q=_(H=>{"use strict";H.__esModule=!0;H.extend=Lr;H.indexOf=fo;H.escapeExpression=po;H.isEmpty=mo;H.createFrame=go;H.blockParams=vo;H.appendContextPath=yo;var lo={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`","=":"="},uo=/[&<>"'`=]/g,co=/[&<>"'`=]/;function ho(e){return lo[e]}function Lr(e){for(var t=1;t{"use strict";ze.__esModule=!0;var zt=["description","fileName","lineNumber","endLineNumber","message","name","number","stack"];function Gt(e,t){var n=t&&t.loc,r=void 0,i=void 0,s=void 0,o=void 0;n&&(r=n.start.line,i=n.end.line,s=n.start.column,o=n.end.column,e+=" - "+r+":"+s);for(var a=Error.prototype.constructor.call(this,e),l=0;l{"use strict";Ge.__esModule=!0;var Kt=q();Ge.default=function(e){e.registerHelper("blockHelperMissing",function(t,n){var r=n.inverse,i=n.fn;if(t===!0)return i(this);if(t===!1||t==null)return r(this);if(Kt.isArray(t))return t.length>0?(n.ids&&(n.ids=[n.name]),e.helpers.each(t,n)):r(this);if(n.data&&n.ids){var s=Kt.createFrame(n.data);s.contextPath=Kt.appendContextPath(n.data.contextPath,n.name),n={data:s}}return i(t,n)})};Or.exports=Ge.default});var Cr=_((Ke,Ir)=>{"use strict";Ke.__esModule=!0;function wo(e){return e&&e.__esModule?e:{default:e}}var be=q(),bo=X(),Eo=wo(bo);Ke.default=function(e){e.registerHelper("each",function(t,n){if(!n)throw new Eo.default("Must pass iterator to #each");var r=n.fn,i=n.inverse,s=0,o="",a=void 0,l=void 0;n.data&&n.ids&&(l=be.appendContextPath(n.data.contextPath,n.ids[0])+"."),be.isFunction(t)&&(t=t.call(this)),n.data&&(a=be.createFrame(n.data));function u(g,m,v){a&&(a.key=g,a.index=m,a.first=m===0,a.last=!!v,l&&(a.contextPath=l+g)),o=o+r(t[g],{data:a,blockParams:be.blockParams([t[g],g],[l+g,null])})}if(t&&typeof t=="object")if(be.isArray(t))for(var c=t.length;s{"use strict";Ye.__esModule=!0;function xo(e){return e&&e.__esModule?e:{default:e}}var ko=X(),So=xo(ko);Ye.default=function(e){e.registerHelper("helperMissing",function(){if(arguments.length!==1)throw new So.default('Missing helper: "'+arguments[arguments.length-1].name+'"')})};Rr.exports=Ye.default});var Br=_((Je,Dr)=>{"use strict";Je.__esModule=!0;function _o(e){return e&&e.__esModule?e:{default:e}}var Nr=q(),Lo=X(),Hr=_o(Lo);Je.default=function(e){e.registerHelper("if",function(t,n){if(arguments.length!=2)throw new Hr.default("#if requires exactly one argument");return Nr.isFunction(t)&&(t=t.call(this)),!n.hash.includeZero&&!t||Nr.isEmpty(t)?n.inverse(this):n.fn(this)}),e.registerHelper("unless",function(t,n){if(arguments.length!=2)throw new Hr.default("#unless requires exactly one argument");return e.helpers.if.call(this,t,{fn:n.inverse,inverse:n.fn,hash:n.hash})})};Dr.exports=Je.default});var qr=_((Xe,Qr)=>{"use strict";Xe.__esModule=!0;Xe.default=function(e){e.registerHelper("log",function(){for(var t=[void 0],n=arguments[arguments.length-1],r=0;r{"use strict";Ze.__esModule=!0;Ze.default=function(e){e.registerHelper("lookup",function(t,n,r){return t&&r.lookupProperty(t,n)})};Fr.exports=Ze.default});var Ur=_((et,$r)=>{"use strict";et.__esModule=!0;function To(e){return e&&e.__esModule?e:{default:e}}var Ee=q(),Po=X(),Oo=To(Po);et.default=function(e){e.registerHelper("with",function(t,n){if(arguments.length!=2)throw new Oo.default("#with requires exactly one argument");Ee.isFunction(t)&&(t=t.call(this));var r=n.fn;if(Ee.isEmpty(t))return n.inverse(this);var i=n.data;return n.data&&n.ids&&(i=Ee.createFrame(n.data),i.contextPath=Ee.appendContextPath(n.data.contextPath,n.ids[0])),r(t,{data:i,blockParams:Ee.blockParams([t],[i&&i.contextPath])})})};$r.exports=et.default});var Yt=_(tt=>{"use strict";tt.__esModule=!0;tt.registerDefaultHelpers=Uo;tt.moveHelperToHooks=jo;function se(e){return e&&e.__esModule?e:{default:e}}var Ao=Ar(),Io=se(Ao),Co=Cr(),Ro=se(Co),Mo=Mr(),No=se(Mo),Ho=Br(),Do=se(Ho),Bo=qr(),Qo=se(Bo),qo=Vr(),Fo=se(qo),Vo=Ur(),$o=se(Vo);function Uo(e){Io.default(e),Ro.default(e),No.default(e),Do.default(e),Qo.default(e),Fo.default(e),$o.default(e)}function jo(e,t,n){e.helpers[t]&&(e.hooks[t]=e.helpers[t],n||delete e.helpers[t])}});var Wr=_((nt,jr)=>{"use strict";nt.__esModule=!0;var Wo=q();nt.default=function(e){e.registerDecorator("inline",function(t,n,r,i){var s=t;return n.partials||(n.partials={},s=function(o,a){var l=r.partials;r.partials=Wo.extend({},l,n.partials);var u=t(o,a);return r.partials=l,u}),n.partials[i.args[0]]=i.fn,s})};jr.exports=nt.default});var zr=_(Jt=>{"use strict";Jt.__esModule=!0;Jt.registerDefaultDecorators=Yo;function zo(e){return e&&e.__esModule?e:{default:e}}var Go=Wr(),Ko=zo(Go);function Yo(e){Ko.default(e)}});var Xt=_((rt,Gr)=>{"use strict";rt.__esModule=!0;var Jo=q(),fe={methodMap:["debug","info","warn","error"],level:"info",lookupLevel:function(t){if(typeof t=="string"){var n=Jo.indexOf(fe.methodMap,t.toLowerCase());n>=0?t=n:t=parseInt(t,10)}return t},log:function(t){if(t=fe.lookupLevel(t),typeof console<"u"&&fe.lookupLevel(fe.level)<=t){var n=fe.methodMap[t];console[n]||(n="log");for(var r=arguments.length,i=Array(r>1?r-1:0),s=1;s{"use strict";Zt.__esModule=!0;Zt.createNewLookupObject=Zo;var Xo=q();function Zo(){for(var e=arguments.length,t=Array(e),n=0;n{"use strict";xe.__esModule=!0;xe.createProtoAccessControl=ra;xe.resultIsAllowed=ia;xe.resetLoggedProperties=oa;function ea(e){return e&&e.__esModule?e:{default:e}}var Yr=Kr(),ta=Xt(),na=ea(ta),it=Object.create(null);function ra(e){var t=Object.create(null);t.constructor=!1,t.__defineGetter__=!1,t.__defineSetter__=!1,t.__lookupGetter__=!1;var n=Object.create(null);return n.__proto__=!1,{properties:{whitelist:Yr.createNewLookupObject(n,e.allowedProtoProperties),defaultValue:e.allowProtoPropertiesByDefault},methods:{whitelist:Yr.createNewLookupObject(t,e.allowedProtoMethods),defaultValue:e.allowProtoMethodsByDefault}}}function ia(e,t,n){return Jr(typeof e=="function"?t.methods:t.properties,n)}function Jr(e,t){return e.whitelist[t]!==void 0?e.whitelist[t]===!0:e.defaultValue!==void 0?e.defaultValue:(sa(t),!1)}function sa(e){it[e]!==!0&&(it[e]=!0,na.default.log("error",'Handlebars: Access has been denied to resolve the property "'+e+`" because it is not an "own property" of its parent. -You can add a runtime option to disable the check or this warning: -See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details`))}function oa(){Object.keys(it).forEach(function(e){delete it[e]})}});var sn=_(U=>{"use strict";U.__esModule=!0;U.HandlebarsEnvironment=rn;function Xr(e){return e&&e.__esModule?e:{default:e}}var oe=q(),aa=X(),tn=Xr(aa),la=Yt(),ua=zr(),ca=Xt(),st=Xr(ca),da=en(),ha="4.7.8";U.VERSION=ha;var fa=8;U.COMPILER_REVISION=fa;var pa=7;U.LAST_COMPATIBLE_COMPILER_REVISION=pa;var ma={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};U.REVISION_CHANGES=ma;var nn="[object Object]";function rn(e,t,n){this.helpers=e||{},this.partials=t||{},this.decorators=n||{},la.registerDefaultHelpers(this),ua.registerDefaultDecorators(this)}rn.prototype={constructor:rn,logger:st.default,log:st.default.log,registerHelper:function(t,n){if(oe.toString.call(t)===nn){if(n)throw new tn.default("Arg not supported with multiple helpers");oe.extend(this.helpers,t)}else this.helpers[t]=n},unregisterHelper:function(t){delete this.helpers[t]},registerPartial:function(t,n){if(oe.toString.call(t)===nn)oe.extend(this.partials,t);else{if(typeof n>"u")throw new tn.default('Attempting to register a partial called "'+t+'" as undefined');this.partials[t]=n}},unregisterPartial:function(t){delete this.partials[t]},registerDecorator:function(t,n){if(oe.toString.call(t)===nn){if(n)throw new tn.default("Arg not supported with multiple decorators");oe.extend(this.decorators,t)}else this.decorators[t]=n},unregisterDecorator:function(t){delete this.decorators[t]},resetLoggedPropertyAccesses:function(){da.resetLoggedProperties()}};var ga=st.default.log;U.log=ga;U.createFrame=oe.createFrame;U.logger=st.default});var ei=_((ot,Zr)=>{"use strict";ot.__esModule=!0;function on(e){this.string=e}on.prototype.toString=on.prototype.toHTML=function(){return""+this.string};ot.default=on;Zr.exports=ot.default});var ti=_(an=>{"use strict";an.__esModule=!0;an.wrapHelper=va;function va(e,t){if(typeof e!="function")return e;var n=function(){var i=arguments[arguments.length-1];return arguments[arguments.length-1]=t(i),e.apply(this,arguments)};return n}});var oi=_(Z=>{"use strict";Z.__esModule=!0;Z.checkRevision=ka;Z.template=Sa;Z.wrapProgram=at;Z.resolvePartial=_a;Z.invokePartial=La;Z.noop=ii;function ya(e){return e&&e.__esModule?e:{default:e}}function wa(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var ba=q(),z=wa(ba),Ea=X(),G=ya(Ea),K=sn(),ni=Yt(),xa=ti(),ri=en();function ka(e){var t=e&&e[0]||1,n=K.COMPILER_REVISION;if(!(t>=K.LAST_COMPATIBLE_COMPILER_REVISION&&t<=K.COMPILER_REVISION))if(t{"use strict";lt.__esModule=!0;lt.default=function(e){(function(){typeof globalThis!="object"&&(Object.prototype.__defineGetter__("__magic__",function(){return this}),__magic__.globalThis=__magic__,delete Object.prototype.__magic__)})();var t=globalThis.Handlebars;e.noConflict=function(){return globalThis.Handlebars===e&&(globalThis.Handlebars=t),e}};ai.exports=lt.default});var fi=_((ut,hi)=>{"use strict";ut.__esModule=!0;function un(e){return e&&e.__esModule?e:{default:e}}function cn(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var Aa=sn(),ui=cn(Aa),Ia=ei(),Ca=un(Ia),Ra=X(),Ma=un(Ra),Na=q(),ln=cn(Na),Ha=oi(),ci=cn(Ha),Da=li(),Ba=un(Da);function di(){var e=new ui.HandlebarsEnvironment;return ln.extend(e,ui),e.SafeString=Ca.default,e.Exception=Ma.default,e.Utils=ln,e.escapeExpression=ln.escapeExpression,e.VM=ci,e.template=function(t){return ci.template(t,e)},e}var ke=di();ke.create=di;Ba.default(ke);ke.default=ke;ut.default=ke;hi.exports=ut.default});var ae=_((Rc,pi)=>{pi.exports=fi().default});var Ui=_((Vi,$i)=>{(function(){var e=function(t){var n=new e.Builder;return n.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),n.searchPipeline.add(e.stemmer),t.call(n,n),n.build()};e.version="2.3.9";e.utils={},e.utils.warn=(function(t){return function(n){t.console&&console.warn&&console.warn(n)}})(this),e.utils.asString=function(t){return t==null?"":t.toString()},e.utils.clone=function(t){if(t==null)return t;for(var n=Object.create(null),r=Object.keys(t),i=0;i0){var c=e.utils.clone(n)||{};c.position=[a,u],c.index=s.length,s.push(new e.Token(r.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/;e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,n){n in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+n),t.label=n,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var n=t.label&&t.label in this.registeredFunctions;n||e.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. -`,t)},e.Pipeline.load=function(t){var n=new e.Pipeline;return t.forEach(function(r){var i=e.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(n){e.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},e.Pipeline.prototype.after=function(t,n){e.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(t);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},e.Pipeline.prototype.before=function(t,n){e.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(t);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},e.Pipeline.prototype.remove=function(t){var n=this._stack.indexOf(t);n!=-1&&this._stack.splice(n,1)},e.Pipeline.prototype.run=function(t){for(var n=this._stack.length,r=0;r1&&(ot&&(r=s),o!=t);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==t||o>t)return s*2;if(ol?c+=2:a==l&&(n+=r[u+1]*i[c+1],u+=2,c+=2);return n},e.Vector.prototype.similarity=function(t){return this.dot(t)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var t=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new e.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}s.str.length==1&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c=s.str.charAt(0),d=s.str.charAt(1),h;d in s.node.edges?h=s.node.edges[d]:(h=new e.TokenSet,s.node.edges[d]=h),s.str.length==1&&(h.final=!0),i.push({node:h,editsRemaining:s.editsRemaining-1,str:c+s.str.slice(2)})}}}return r},e.TokenSet.fromString=function(t){for(var n=new e.TokenSet,r=n,i=0,s=t.length;i=t;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};e.Index=function(t){this.invertedIndex=t.invertedIndex,this.fieldVectors=t.fieldVectors,this.tokenSet=t.tokenSet,this.fields=t.fields,this.pipeline=t.pipeline},e.Index.prototype.search=function(t){return this.query(function(n){var r=new e.QueryParser(t,n);r.parse()})},e.Index.prototype.query=function(t){for(var n=new e.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=t},e.Builder.prototype.k1=function(t){this._k1=t},e.Builder.prototype.add=function(t,n){var r=t[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,n;do t=this.next(),n=t.charCodeAt(0);while(n>47&&n<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var n=t.next();if(n==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){t.escapeCharacter();continue}if(n==":")return e.QueryLexer.lexField;if(n=="~")return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if(n=="^")return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if(n=="+"&&t.width()===1||n=="-"&&t.width()===1)return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(n.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}},e.QueryParser=function(t,n){this.lexer=new e.QueryLexer(t),this.query=n,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var t=this.peekLexeme();return this.lexemeIdx+=1,t},e.QueryParser.prototype.nextClause=function(){var t=this.currentClause;this.query.clause(t),this.currentClause={}},e.QueryParser.parseClause=function(t){var n=t.peekLexeme();if(n!=null)switch(n.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new e.QueryParseError(r,n.start,n.end)}},e.QueryParser.parsePresence=function(t){var n=t.consumeLexeme();if(n!=null){switch(n.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new e.QueryParseError(r,n.start,n.end)}var i=t.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new e.QueryParseError(r,n.start,n.end)}switch(i.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new e.QueryParseError(r,i.start,i.end)}}},e.QueryParser.parseField=function(t){var n=t.consumeLexeme();if(n!=null){if(t.query.allFields.indexOf(n.str)==-1){var r=t.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.fields=[n.str];var s=t.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new e.QueryParseError(i,n.start,n.end)}if(s.type===e.QueryLexer.TERM)return e.QueryParser.parseTerm;var i="expecting term, found '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}},e.QueryParser.parseTerm=function(t){var n=t.consumeLexeme();if(n!=null){t.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var r=t.peekLexeme();if(r==null){t.nextClause();return}switch(r.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new e.QueryParseError(i,r.start,r.end)}}},e.QueryParser.parseEditDistance=function(t){var n=t.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.editDistance=r;var s=t.peekLexeme();if(s==null){t.nextClause();return}switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var n=t.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.boost=r;var s=t.peekLexeme();if(s==null){t.nextClause();return}switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}}},(function(t,n){typeof define=="function"&&define.amd?define(n):typeof Vi=="object"?$i.exports=n():t.lunr=n()})(this,function(){return e})})()});var ie={plain:"plain",function:"function",module:"module"},Is=[{href:"typespecs.html#basic-types",hint:{kind:ie.plain,description:"Basic type"}},{href:"typespecs.html#literals",hint:{kind:ie.plain,description:"Literal"}},{href:"typespecs.html#built-in-types",hint:{kind:ie.plain,description:"Built-in type"}}],Me={cancelHintFetching:null};function Wn(e){if(Gn(e))return!0;let t=/#.*\//;return e.includes("#")&&!t.test(e)?!1:e.includes(".html")}function zn(e){let t=Gn(e);return t?Promise.resolve(t):Cs(e)}function Gn(e){let t=Is.find(n=>e.includes(n.href));return t?t.hint:null}function Cs(e){let t=e.replace(".html",".html?hint=true");return new Promise((n,r)=>{let i=document.createElement("iframe");i.setAttribute("src",t),i.style.display="none";function s(a){let{href:l,hint:u}=a.data;t===l&&(o(),n(u))}Me.cancelHintFetching=()=>{o(),r(new Error("cancelled"))};function o(){i.remove(),window.removeEventListener("message",s),Me.cancelHintFetching=null}window.addEventListener("message",s),document.body.appendChild(i)})}function Kn(){Me.cancelHintFetching&&Me.cancelHintFetching()}function Yn(e){let n=e.querySelector("h1").textContent,r=e.querySelector(".docstring > p"),i=r?r.innerHTML:"";return{kind:ie.function,title:n.trim(),description:i.trim()}}function Jn(e){let n=e.querySelector("h1 > span").textContent,r=e.querySelector("#moduledoc p"),i=r?r.innerHTML:"";return{kind:ie.module,title:n.trim(),description:i.trim()}}var f=document.querySelector.bind(document),A=document.querySelectorAll.bind(document);function Xn(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function He(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function De(){return document.getElementById("main").dataset.type}var Ne=["H1","H2","H3","H4","H5","H6"];function Zn(e=!1){let t=window.location.hash.replace(/^#/,"");if(!t)return e?document.getElementById("top-content"):null;let n=document.getElementById(t);if(!n)return null;if(n.matches(".detail"))return n;if(Ne.includes(n.tagName))return Tt(n);let r=Rs(n);return r||document.getElementById("top-content")}function Rs(e){let t=e.previousElementSibling;for(;t;){if(Ne.includes(t.tagName))return Tt(t);t=t.previousElementSibling}let n=e.parentNode;for(;n;){for(t=n.previousElementSibling;t;){if(Ne.includes(t.tagName))return Tt(t);t=t.previousElementSibling}n=n.parentNode}return null}function Tt(e){let t=document.createElement("div"),n=[e],r=e;for(;(r=r.nextSibling)&&!(Ne.includes(r.tagName)&&r.tagName<=e.tagName);)n.push(r);return t.append(...n),t}function er(e){return new URLSearchParams(window.location.search).get(e)}function Pt(e){return fetch(e).then(t=>t.ok).catch(()=>!1)}function he(e){return!e||e.trim()===""}function tr(e,t){let n;return function(...i){clearTimeout(n),n=setTimeout(()=>{n=null,e(...i)},t)}}function Be(){return document.head.querySelector("meta[name=project][content]").content}function Qe(){return document.documentElement.classList.contains("apple-os")}function E(e,t,n){let r=document.createElement(e);for(let i in t)t[i]!=null&&r.setAttribute(i,t[i]);return n&&r.replaceChildren(...n),r}var Ms=new URLSearchParams(window.location.search),Ns=window.self!==window.parent,Ot=Ns&&Ms.has("hint"),C=Ot;function qe(){return window.sidebarNodes||{}}function nr(){return window.versionNodes||[]}if(Ot&&C){let e=Zn(),t=e?Yn(e):["modules","tasks"].includes(De())?Jn(f(".content-inner")):null;if(t){let n={hint:{...t,version:Be()},href:window.location.href};window.parent.postMessage(n,"*")}f(".content-inner")?.replaceChildren()}var At="ex_doc:settings",rr="dark",It="system",Ct="dark",Rt="light";var Hs={tooltips:!0,theme:null,livebookUrl:null},Mt=class{constructor(){this._subscribers=[],this._settings=Hs,this._loadSettings()}get(){return this._settings}update(t){let n=this._settings;this._settings={...this._settings,...t},this._subscribers.forEach(r=>r(this._settings,n)),this._storeSettings()}getAndSubscribe(t){this._subscribers.push(t),t(this._settings)}_loadSettings(){try{let t=localStorage.getItem(At);if(t){let n=JSON.parse(t);this._settings={...this._settings,...n}}this._loadSettingsLegacy()}catch(t){console.error(`Failed to load settings: ${t}`)}}_storeSettings(){try{this._storeSettingsLegacy(),localStorage.setItem(At,JSON.stringify(this._settings))}catch(t){console.error(`Failed to persist settings: ${t}`)}}_loadSettingsLegacy(){localStorage.getItem("tooltipsDisabled")!==null&&(this._settings={...this._settings,tooltips:!1}),localStorage.getItem("night-mode")==="true"&&(this._settings={...this._settings,nightMode:!0}),this._settings.nightMode===!0&&(this._settings={...this._settings,theme:"dark"})}_storeSettingsLegacy(){this._settings.tooltips?localStorage.removeItem("tooltipsDisabled"):localStorage.setItem("tooltipsDisabled","true"),this._settings.nightMode!==null?localStorage.setItem("night-mode",this._settings.nightMode===!0?"true":"false"):localStorage.removeItem("night-mode"),this._settings.theme!==null?(localStorage.setItem("night-mode",this._settings.theme==="dark"?"true":"false"),this._settings.nightMode=this._settings.theme==="dark"):(delete this._settings.nightMode,localStorage.removeItem("night-mode"))}},R=new Mt;var ir=!1,Fe=null,J=null;function Ve(e){ir||(ir=!0,J=document.getElementById("toast"),J?.addEventListener("click",()=>{clearTimeout(Fe),J.classList.remove("show")})),J&&(clearTimeout(Fe),J.innerText=e,J.classList.add("show"),Fe=setTimeout(()=>{J.classList.remove("show"),Fe=setTimeout(function(){J.innerText=""},1e3)},5e3))}var Nt=[It,Ct,Rt],sr=window.matchMedia("(prefers-color-scheme: dark)");R.getAndSubscribe(or);sr.addEventListener("change",or);function or(){let e=lr(),t=e===Ct||e!==Rt&&sr.matches;document.body.classList.toggle(rr,t)}function ar(){let e=Nt[Nt.indexOf(lr())+1]||Nt[0];R.update({theme:e}),Ve(`Set theme to "${e}"`)}function lr(){return new URLSearchParams(window.location.search).get("theme")||R.get().theme||It}var Er=Y(fr());var Dt="sidebar_state",Bt="closed",pr="open",mr="sidebar_width";var Ue="sidebar-open",je="sidebar-transition";var gr=!1;function vr(){if(gr)return;gr=!0;let e=document.getElementById("sidebar-list-nav");if(!e)return;let t=De(),n={extras:e.dataset.extras,modules:"Modules",tasks:'Mix Tasks'};Object.entries(n).forEach(([r,i])=>{let s=qe()[r];if(!s?.length)return;let o=`${r}-list-tab-button`,a=`${r}-tab-panel`,l=r===t,u=E("button",{id:o,role:"tab",tabindex:l?0:-1,"aria-selected":l||void 0,"aria-controls":a});u.innerHTML=i,u.addEventListener("keydown",no),u.addEventListener("click",ro),e.appendChild(E("li",{},[u]));let c=E("ul",{class:"full-list"});c.addEventListener("click",io);let d=E("div",{id:a,class:"sidebar-tabpanel",role:"tabpanel","aria-labelledby":o,hidden:l?void 0:""},[c]);document.getElementById("sidebar").appendChild(d);let h="",p,g;c.replaceChildren(...s.flatMap(m=>{let v=[],w=Array.isArray(m.headers),x=w?void 0:"no",P=m?.url||`${m.id}.html`;return m.group!==h&&(v.push(E("li",{class:"group",translate:x},[m.group])),h=m.group,p=void 0),m.nested_context&&m.nested_context!==p?(p=m.nested_context,g!==p&&v.push(E("li",{class:"nesting-context",translate:"no","aria-hidden":!0},[p]))):g=m.title,v.push(E("li",{},[E("a",{href:P,translate:x,...m.url?{rel:"nofollow"}:{}},[m.nested_title||m.title,m.url?E("i",{class:"external-link ri-external-link-line"}):null].filter(Boolean)),...qt(`node-${m.id}-headers`,w?eo(m):to(m))])),v}))}),Qt(),requestAnimationFrame(yr),window.addEventListener("hashchange",Qt),window.addEventListener("exdoc:loaded",Qt)}function qt(e,t){return t.length?[E("button",{"aria-label":"expand","aria-expanded":!1,"aria-controls":e}),E("ul",{id:e},t)]:[]}function eo(e){return e.headers.map(({id:t,anchor:n})=>E("li",{},[E("a",{href:`${e.id}.html#${n}`},[t])]))}function to(e){let t=[];return e.sections?.length&&t.push(E("li",{},[E("a",{href:`${e.id}.html#content`},["Sections"]),...qt(`${e.id}-sections-list`,e.sections.map(({id:n,anchor:r})=>E("li",{},[E("a",{href:`${e.id}.html#${r}`},[n])])))])),e.nodeGroups&&(t.push(E("li",{},[E("a",{href:`${e.id}.html#summary`},["Summary"])])),t.push(...e.nodeGroups.map(({key:n,name:r,nodes:i})=>E("li",{},[E("a",{href:`${e.id}.html#${n}`},[r]),...qt(`node-${e.id}-group-${n}-list`,i.map(({anchor:s,title:o,id:a})=>E("li",{},[E("a",{href:`${e.id}.html#${s}`,title:o,translate:"no"},[a])])))])))),t}function Ft(e){let t=document.getElementById("sidebar-list-nav").querySelector("[aria-selected]");t!==e&&(t&&(t.removeAttribute("aria-selected"),t.setAttribute("tabindex","-1"),document.getElementById(t.getAttribute("aria-controls")).setAttribute("hidden","hidden")),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0"),document.getElementById(e.getAttribute("aria-controls")).removeAttribute("hidden"))}function yr(){f("#sidebar [role=tabpanel]:not([hidden]) a[aria-selected]")?.scrollIntoView()}function Qt(){let e=document.getElementById("sidebar"),{pathname:t,hash:n}=window.location,r=t.split("/").pop().replace(/\.html$/,"")+".html",i=e.querySelector(`li a[href="${r+n}"]`)||e.querySelector(`li a[href="${r}"]`);if(!i)return;e.querySelectorAll(".full-list a[aria-selected]").forEach(o=>{o.removeAttribute("aria-selected")}),e.querySelectorAll(".full-list button[aria-expanded=true]").forEach(o=>{o.setAttribute("aria-expanded",!1)});let s=i.parentElement;for(;s;){if(s.tagName==="LI"){let o=s.firstChild;o.setAttribute("aria-selected",o.getAttribute("href")===r?"page":"true");let a=o.nextSibling;a?.tagName==="BUTTON"&&a.setAttribute("aria-expanded",!0)}else if(s.role==="tabpanel"){s.hasAttribute("hidden")&&Ft(document.getElementById(s.getAttribute("aria-labelledby")));break}s=s.parentElement}}function no(e){if(!["ArrowRight","ArrowLeft"].includes(e.key))return;let t=Array.from(A('#sidebar-list-nav [role="tab"]')),r=t.indexOf(e.currentTarget)+(e.key==="ArrowRight"?1:-1),i=t.at(r%t.length);Ft(i),i.focus()}function ro(e){Ft(e.currentTarget),yr()}function io(e){let t=e.target;t.tagName==="BUTTON"&&t.setAttribute("aria-expanded",t.getAttribute("aria-expanded")==="false")}var oo=300,xr=".sidebar-toggle",Vt=window.matchMedia(`screen and (max-width: ${768}px)`);if(!C){window.addEventListener("exdoc:loaded",wr);let e=document.getElementById("sidebar"),t=f(xr);t.addEventListener("click",We),document.body.addEventListener("click",i=>{Vt.matches&&$t()&&!e.contains(i.target)&&!t.contains(i.target)&&We()});let n=window.innerWidth;window.addEventListener("resize",(0,Er.default)(()=>{n!==window.innerWidth&&(n=window.innerWidth,wr())},100));let r=new ResizeObserver(([i])=>{if(!i)return;let s=i.contentRect.width;sessionStorage.setItem(mr,s),document.body.style.setProperty("--sidebarWidth",`${s}px`)});e.addEventListener("mousedown",()=>r.observe(e)),e.addEventListener("mouseup",()=>r.unobserve(e)),window.addEventListener("hashchange",ao)}function wr(){let t=sessionStorage.getItem(Dt)!==Bt&&!Vt.matches;Sr(t)}function We(){let e=!$t();return sessionStorage.setItem(Dt,e?pr:Bt),Ut(e)}function $t(){return document.body.classList.contains(Ue)}function kr(){return document.body.classList.contains(Ue)&&!document.body.classList.contains(je)}function Sr(e){e&&vr(),document.body.classList.toggle(Ue,e),f(xr).setAttribute("aria-expanded",e?"true":"false")}var br;function Ut(e){return new Promise(t=>{document.body.classList.add(je),document.body.scrollTop,Sr(e),clearTimeout(br),br=setTimeout(()=>{document.body.classList.remove(je),t()},oo)})}function _r(){return Ut(!0)}function ao(){Vt.matches&&$t()&&Ut(!1)}var mi=Y(ae());var dn=Y(ae());dn.registerHelper("isArray",function(e,t){return Array.isArray(e)?t.fn(this):t.inverse(this)});dn.registerHelper("isNonEmptyArray",function(e,t){return Array.isArray(e)&&e.length>0?t.fn(this):t.inverse(this)});var gi=mi.template({1:function(e,t,n,r,i){var s,o,a=t??(e.nullContext||{}),l=e.hooks.helperMissing,u="function",c=e.escapeExpression,d=e.lookupProperty||function(h,p){if(Object.prototype.hasOwnProperty.call(h,p))return h[p]};return' -`},2:function(e,t,n,r,i){return" selected disabled"},4:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return` -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`
    - -`+((s=a(n,"if").call(o,t!=null?a(t,"latestVersion"):t,{name:"if",hash:{},fn:e.program(4,i,0),inverse:e.noop,data:i,loc:{start:{line:13,column:2},end:{line:19,column:9}}}))!=null?s:"")+`
    -`},useData:!0});var Qa=".sidebar-projectVersion",vi=".sidebar-projectVersion select",qa=".sidebar-staleVersion a";if(!C){let e=nr(),t=f(Qa);if(e.length>0||!t){let n=t.textContent.trim(),i=(e.some(u=>u.version===n)?e:[{version:n,url:"#"},...e]).map(u=>({...u,isCurrentVersion:u.version===n})),s=e.find(u=>u.latest),o=s?.version!==n?s?.url:null;n.includes("-")&&e.findIndex(u=>u.version===n)u.latest)&&(o=null),t.innerHTML=gi({nodes:i,latestVersion:o});let a=f(vi);a.addEventListener("change",Va),Fa(a);let l=f(qa);l&&l.addEventListener("click",$a)}}function Fa(e){let t=document.createElement("span");t.style.visibility="hidden",t.style.position="absolute",t.style.whiteSpace="nowrap",t.style.font=window.getComputedStyle(e).font,t.textContent=e.options[e.selectedIndex].text,document.body.appendChild(t),e.style.width=`${t.offsetWidth+20}px`,document.body.removeChild(t)}function Va(e){let t=e.target.value,n=window.location.pathname.split("/").pop()+window.location.hash,r=`${t}/${n}`;Pt(r).then(i=>{i?window.location.href=r:window.location.href=t})}function $a(e){let t=this.href,n=window.location.pathname.split("/").pop()+window.location.hash,r=`${t}/${n}`;e.preventDefault(),Pt(r).then(i=>{i?window.location.href=r:window.location.href=t})}function hn(){let e=f(vi);e&&(e.focus(),e.addEventListener("keydown",t=>{(t.key==="Escape"||t.key==="v")&&(t.preventDefault(),e.blur())}),navigator.userActivation.isActive&&"showPicker"in HTMLSelectElement.prototype&&e.showPicker())}var Ua="content",ja="tabs-open",Wa="tabs-close",za="H3",Ga="tabset";window.addEventListener("exdoc:loaded",Ka);function Ka(){let e=[],t=[],n=document.createNodeIterator(document.getElementById(Ua),NodeFilter.SHOW_COMMENT,i=>i.nodeValue.trim()===ja?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT),r;for(;r=n.nextNode();){let i=[];e.push([r,i]);let s,o=r;for(;o=o.nextSibling;)if(o.nodeName===za){s=[];let a=o.querySelector(".text")?.childNodes||o.childNodes;i.push([a,s]),t.push(o)}else if(o.nodeName==="#comment"&&o.nodeValue.trim()===Wa){t.push(o);break}else s&&s.push(o)}e.forEach(([i,s],o)=>{let a=E("div",{class:Ga});i.parentNode.replaceChild(a,i);let l=E("div",{role:"tablist",class:"tabset-tablist"});a.appendChild(l),s.forEach(([u,c],d)=>{let h=d===0,p=`tab-${o}-${d}`,g=`tabpanel-${o}-${d}`,m=E("button",{role:"tab",id:p,class:"tabset-tab",tabindex:h?0:-1,"aria-selected":h,"aria-controls":g},u);m.addEventListener("click",Ya),m.addEventListener("keydown",Ja),l.appendChild(m);let v=E("div",{role:"tabpanel",id:g,class:"tabset-panel",hidden:h?void 0:"","aria-labelledby":p},c);a.appendChild(v)})}),t.forEach(i=>{i.parentNode.removeChild(i)})}function Ya(e){wi(e.currentTarget)}function Ja(e){if(yi[e.code]){e.preventDefault();let t=[...e.currentTarget.parentNode.childNodes],n=t.indexOf(e.currentTarget),r=yi[e.code](n,t.length);wi(t.at(r%t.length))}}var yi={ArrowLeft:e=>e-1,ArrowRight:e=>e+1,Home:()=>0,End:(e,t)=>t-1};function wi(e){let t=e.parentNode.querySelector("[aria-selected=true]");if(t===e)return;t.setAttribute("aria-selected","false"),t.tabIndex=-1,e.setAttribute("aria-selected","true"),e.tabIndex=0,e.focus();let n=document.getElementById(t.getAttribute("aria-controls"));n.setAttribute("hidden",""),n.tabIndex=-1;let r=document.getElementById(e.getAttribute("aria-controls"));r.removeAttribute("hidden"),r.tabIndex=0}window.addEventListener("exdoc:loaded",Xa);function Xa(){let e=window.location.pathname.replace(/(\.html)?$/,".livemd"),t=encodeURIComponent(new URL(e,window.location.href).toString());R.getAndSubscribe(({livebookUrl:n})=>{let r=n?`${n}/import?url=${t}`:`https://livebook.dev/run?url=${t}`;for(let i of A(".livebook-badge"))i.href=r})}var Za="hll";window.addEventListener("exdoc:loaded",el);function el(){A("[data-group-id]").forEach(e=>{e.addEventListener("mouseenter",bi),e.addEventListener("mouseleave",bi)})}function bi(e){let t=e.currentTarget,n=e.type==="mouseenter",r=t.getAttribute("data-group-id");t.parentElement.querySelectorAll(`[data-group-id="${r}"]`).forEach(i=>{i.classList.toggle(Za,n)})}var j={module:"module",moduleChild:"module-child",mixTask:"mix-task",extra:"extra",section:"section"},tl=10;function xi(e,t=null){let n=t||window.autocompleteLimit||tl;if(he(e))return[];let r=qe(),i=[...fn(r.modules,e,j.module,"module"),...nl(r.modules,e,j.moduleChild),...fn(r.tasks,e,j.mixTask,"mix task"),...fn(r.extras,e,j.extra,"page"),...pn(r.modules,e,j.section,"module"),...pn(r.tasks,e,j.section,"mix task"),...pn(r.extras,e,j.section,"page")].filter(s=>s!==null);return ul(i).slice(0,n)}function fn(e,t,n,r){return e.map(i=>i.searchData?null:il(i,t,n,r))}function nl(e,t,n){return e.filter(r=>r.nodeGroups).flatMap(r=>r.nodeGroups.flatMap(({key:i,nodes:s})=>{let o=ll(i);return s.map(a=>sl(a,r.id,t,n,o)||al(a,r.id,t,n,o))}))}function pn(e,t,n,r){return e.flatMap(i=>rl(i).map(s=>ol(i,s,t,n,r)))}function rl(e){return e.searchData?e.searchData:(e.sections||[]).concat(e.headers||[])}function il(e,t,n,r){return dt(e.title,t)?{link:`${e.id}.html`,title:pt(e.title,t),description:null,matchQuality:ht(e.title,t),deprecated:e.deprecated,labels:[r],category:n}:null}function sl(e,t,n,r,i){return dt(e.id,n)?{link:`${t}.html#${e.anchor}`,title:pt(e.id,n),labels:[i],description:t,matchQuality:ht(e.id,n),deprecated:e.deprecated,category:r}:null}function ol(e,t,n,r,i){if(!ki(t.id,n))return null;let s;return t.anchor===""?s=`${e.id}.html`:s=`${e.id}.html#${t.anchor}`,{link:s,title:pt(t.id,n),description:e.title,matchQuality:ht(t.id,n),labels:t.labels||[i,"section"],category:r}}function al(e,t,n,r,i){let s=`${t}.${e.id}`,o=`${t}:${e.id}`,a,l;if(dt(s,n))a=s,l=/\./g;else if(dt(o,n))a=o,l=/:/g;else return null;let u=n.replace(l," ");return ki(e.id,u)?{link:`${t}.html#${e.anchor}`,title:pt(e.id,u),label:i,description:t,matchQuality:ht(a,n),deprecated:e.deprecated,category:r}:null}function ll(e){switch(e){case"callbacks":return"callback";case"types":return"type";default:return"function"}}function ul(e){return e.slice().sort((t,n)=>t.matchQuality!==n.matchQuality?n.matchQuality-t.matchQuality:Ei(t.category)-Ei(n.category))}function Ei(e){switch(e){case j.module:return 1;case j.moduleChild:return 2;case j.mixTask:return 3;default:return 4}}function ki(e,t){return ft(t).some(r=>Si(e,r))}function dt(e,t){return ft(t).every(r=>Si(e,r))}function Si(e,t){return e.toLowerCase().includes(t.toLowerCase())}function ht(e,t){let n=ft(t),i=n.map(o=>o.length).reduce((o,a)=>o+a,0)/e.length,s=cl(e,n[0])?1:0;return i+s}function cl(e,t){return e.toLowerCase().startsWith(t.toLowerCase())}function ft(e){return e.trim().split(/\s+/)}function pt(e,t){let n=ft(t).sort((r,i)=>i.length-r.length);return ct(e,n)}function ct(e,t){if(t.length===0)return e;let[n,...r]=t,i=e.match(new RegExp(`(.*)(${Xn(n)})(.*)`,"i"));if(i){let[,s,o,a]=i;return ct(s,t)+""+He(o)+""+ct(a,t)}else return ct(e,r)}var _i=Y(ae());var Li=_i.template({1:function(e,t,n,r,i){return` Autocompletion results within this project -`},3:function(e,t,n,r,i){return` No autocompletion results within this project -`},5:function(e,t,n,r,i){var s,o,a=t??(e.nullContext||{}),l=e.hooks.helperMissing,u="function",c=e.escapeExpression,d=e.lookupProperty||function(h,p){if(Object.prototype.hasOwnProperty.call(h,p))return h[p]};return' - `+((s=(o=(o=d(n,"title")||(t!=null?d(t,"title"):t))!=null?o:l,typeof o===u?o.call(a,{name:"title",hash:{},data:i,loc:{start:{line:19,column:44},end:{line:19,column:55}}}):o))!=null?s:"")+` -`+((s=d(n,"if").call(a,t!=null?d(t,"description"):t,{name:"if",hash:{},fn:e.program(6,i,0),inverse:e.noop,data:i,loc:{start:{line:20,column:8},end:{line:23,column:15}}}))!=null?s:"")+((s=d(n,"if").call(a,t!=null?d(t,"deprecated"):t,{name:"if",hash:{},fn:e.program(8,i,0),inverse:e.noop,data:i,loc:{start:{line:24,column:8},end:{line:26,column:15}}}))!=null?s:"")+((s=d(n,"each").call(a,t!=null?d(t,"labels"):t,{name:"each",hash:{},fn:e.program(10,i,0),inverse:e.noop,data:i,loc:{start:{line:27,column:8},end:{line:29,column:17}}}))!=null?s:"")+` -`},6:function(e,t,n,r,i){var s,o,a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return` - `+((s=(o=(o=a(n,"description")||(t!=null?a(t,"description"):t))!=null?o:e.hooks.helperMissing,typeof o=="function"?o.call(t??(e.nullContext||{}),{name:"description",hash:{},data:i,loc:{start:{line:22,column:49},end:{line:22,column:66}}}):o))!=null?s:"")+` -`},8:function(e,t,n,r,i){return` deprecated -`},10:function(e,t,n,r,i){return' '+e.escapeExpression(e.lambda(t,t))+` -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`
    -
    -
    -
    - -`+((s=a(n,"if").call(o,(s=t!=null?a(t,"suggestions"):t)!=null?a(s,"length"):s,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:6,column:8},end:{line:10,column:15}}}))!=null?s:"")+` - - Press RETURN for full-text search - -
    -
    -`+((s=a(n,"each").call(o,t!=null?a(t,"suggestions"):t,{name:"each",hash:{},fn:e.program(5,i,0),inverse:e.noop,data:i,loc:{start:{line:17,column:6},end:{line:31,column:15}}}))!=null?s:"")+`
    -
    -
    -`},useData:!0});var pe=".autocomplete",mn=".autocomplete-suggestions",Ti=".autocomplete-suggestion",W={autocompleteSuggestions:[],selectedIdx:-1};function dl(){f(pe).classList.add("shown")}function gn(){f(pe).classList.remove("shown")}function Pi(){return f(pe).classList.contains("shown")}function vn(e){W.autocompleteSuggestions=xi(e),W.selectedIdx=-1,he(e)?gn():(hl({term:e,suggestions:W.autocompleteSuggestions}),mt(0),dl())}function hl({term:e,suggestions:t}){let n=Li({suggestions:t,term:e}),r=f(pe);r.innerHTML=n}function Oi(){return W.selectedIdx===-1?null:W.autocompleteSuggestions[W.selectedIdx]}function mt(e){fl(pl(e))}function fl(e){W.selectedIdx=e;let t=f(mn),n=f(`${Ti}.selected`),r=f(`${Ti}[data-index="${W.selectedIdx}"]`);n&&n.classList.remove("selected"),r?(r.classList.add("selected"),r.scrollIntoView({block:"nearest"})):t&&(t.scrollTop=0)}function pl(e){let t=W.autocompleteSuggestions.length+1;return(W.selectedIdx+e+1+t)%t-1}var me="form.search-bar input",ml="form.search-bar .search-close-button",gl=".engine-selector",_e=".engine-button",Le=".engine-dropdown",yt=null;C||window.addEventListener("exdoc:loaded",vl);function vl(){yl(),El(),bl()}function yl(){let e=document.querySelectorAll(".engine-option");if(e.length===0){yt="search.html?q=";return}let t=window.location.pathname;if(t.endsWith("/search.html")||t.endsWith("/search")){let n=Array.from(e).find(r=>r.dataset.engineUrl.startsWith("search.html"));if(n){Ri(n);return}}yt=e[0].dataset.engineUrl}function Ri(e){yt=e.dataset.engineUrl;let n=f(_e)?.querySelector(".engine-name");n&&(n.textContent=e.querySelector(".name").textContent),f(Le)?.querySelectorAll(".engine-option").forEach(i=>{i.setAttribute("aria-checked",i===e?"true":"false")})}function Ai(){let e=f(_e),t=f(Le);if(!e||!t)return;e.getAttribute("aria-expanded")==="true"?Se():wl()}function wl(){let e=f(_e),t=f(Le);if(!e||!t)return;e.setAttribute("aria-expanded","true"),t.removeAttribute("hidden"),t.querySelector(".engine-option")?.focus()}function Se(){let e=f(_e),t=f(Le);!e||!t||(e.setAttribute("aria-expanded","false"),t.setAttribute("hidden",""))}function Mi(e){let t=f(me);t.value=e}function Ni(){let e=f(me);document.body.classList.add("search-focused"),e.focus()}function bl(){let e=f(me),t=parseInt(document.querySelector('meta[name="exdoc:autocomplete-limit"]')?.content);t&&(window.autocompleteLimit=t),e.setAttribute("autocomplete-limit",t)}function El(){let e=f(me);if(document.querySelector('meta[name="exdoc:autocomplete"][content="off"]'))return e.addEventListener("keydown",r=>{r.key==="Enter"&&Ii(r)}),!0;e.addEventListener("keydown",r=>{let i=Qe();r.key==="Escape"?(gt(),e.blur()):r.key==="Enter"?Ii(r):r.key==="ArrowUp"||i&&r.ctrlKey&&r.key==="p"?(mt(-1),r.preventDefault()):(r.key==="ArrowDown"||i&&r.ctrlKey&&r.key==="n")&&(mt(1),r.preventDefault())}),e.addEventListener("input",r=>{vn(r.target.value)}),e.addEventListener("focus",r=>{document.body.classList.contains("search-focused")||(document.body.classList.add("search-focused"),vn(r.target.value))}),e.addEventListener("blur",r=>{let i=r.relatedTarget,s=f(mn);if(i&&s&&s.contains(i))return setTimeout(()=>{Pi()&&e.focus()},1e3),null;vt()}),f(pe).addEventListener("click",r=>{r.shiftKey||r.ctrlKey?e.focus():(gt(),vt())}),f(ml).addEventListener("click",r=>{gt(),vt(),e.blur()});let t=f(_e);t&&(t.addEventListener("click",r=>{r.stopPropagation(),Ai()}),t.addEventListener("keydown",r=>{r.key==="Enter"||r.key===" "?(r.preventDefault(),Ai()):r.key==="Escape"&&Se()})),document.addEventListener("click",r=>{let i=f(gl);i&&!i.contains(r.target)&&Se()});let n=f(Le);n&&(n.querySelectorAll(".engine-option").forEach(r=>{r.addEventListener("click",()=>{Ri(r),Se()})}),n.addEventListener("keydown",r=>{let i=Array.from(n.querySelectorAll(".engine-option")),s=i.indexOf(document.activeElement);if(r.key==="ArrowDown"){r.preventDefault();let o=(s+1)%i.length;i[o]?.focus()}else if(r.key==="ArrowUp"){r.preventDefault();let o=(s-1+i.length)%i.length;i[o]?.focus()}else r.key==="Escape"?(r.preventDefault(),Se(),t?.focus()):(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),document.activeElement?.click())}))}function Ii(e){let t=f(me),n=e.shiftKey||e.ctrlKey,r=Oi();e.preventDefault();let i=n?"_blank":"_self",s=document.createElement("a");s.setAttribute("target",i),r?s.setAttribute("href",r.link):s.setAttribute("href",`${yt}${encodeURIComponent(t.value)}`),s.click(),n||(gt(),vt())}function gt(){let e=f(me);e.value=""}function vt(){document.body.classList.remove("search-focused"),gn()}var yn,Ci=2;window.addEventListener("scroll",function(){let e=window.scrollY;if(yn!==void 0){let t=e-yn;e===0||t>Ci?document.body.classList.remove("scroll-sticky"):e>0&&-t>Ci&&document.body.classList.add("scroll-sticky")}yn=Math.max(0,e)},!1);var Hi=Y(ae());var Di=Hi.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
    - `+e.escapeExpression(e.lambda((s=t!=null?o(t,"hint"):t)!=null?o(s,"description"):s,t))+` -
    -`},3:function(e,t,n,r,i){var s,o=e.lambda,a=e.escapeExpression,l=e.lookupProperty||function(u,c){if(Object.prototype.hasOwnProperty.call(u,c))return u[c]};return`
    -

    - `+a(o((s=t!=null?l(t,"hint"):t)!=null?l(s,"title"):s,t))+` -
    `+a(o((s=t!=null?l(t,"hint"):t)!=null?l(s,"version"):s,t))+`
    -

    -
    -`+((s=l(n,"if").call(t??(e.nullContext||{}),(s=t!=null?l(t,"hint"):t)!=null?l(s,"description"):s,{name:"if",hash:{},fn:e.program(4,i,0),inverse:e.noop,data:i,loc:{start:{line:12,column:2},end:{line:16,column:9}}}))!=null?s:"")},4:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
    - `+((s=e.lambda((s=t!=null?o(t,"hint"):t)!=null?o(s,"description"):s,t))!=null?s:"")+` -
    -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"isPlain"):t,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:1,column:0},end:{line:17,column:7}}}))!=null?s:""},useData:!0});var xl='
    ',kl='.content a:not([data-no-tooltip=""])',wn=".tooltip",Bi=".tooltip .tooltip-body",Qi="body .content-inner",Sl="#content",qi="tooltip-shown",Te=10,_l=Te*4,Ll=768,Tl=450,Pl=100,le={currentLinkElement:null,hoverDelayTimeout:null};window.addEventListener("exdoc:loaded",Ol);function Ol(){A(kl).forEach(e=>{Al(e)&&(e.addEventListener("mouseenter",Cl),e.addEventListener("mouseleave",Ml))})}function Al(e){return!(Il(e.href)||!Wn(e.href))}function Il(e){let t=e.replace(Sl,"");return window.location.href.split("#")[0]===t}function Cl(e){if(window.innerWidth{zn(t.href).then(Rl).catch(()=>{})},Pl)}function Rl(e){let t=Di({isPlain:e.kind===ie.plain,hint:e}),n=f(Bi);n||(f(Qi).insertAdjacentHTML("beforeend",xl),n=f(Bi)),n.innerHTML=t,Nl(),f(wn).classList.add(qi)}function Ml(){le.currentLinkElement&&(clearTimeout(le.hoverDelayTimeout),Kn(),le.currentLinkElement=null,f(wn)?.classList.remove(qi))}function Nl(){if(!le.currentLinkElement)return;let e=f(wn),t=le.currentLinkElement.getBoundingClientRect(),n=f(Qi).getBoundingClientRect(),r=e.getBoundingClientRect(),i=Hl(t,n);if(t.left+r.width+Te code:first-child):not(:has(.copy-button))").forEach(e=>{if(!bn){let r=document.createElement("div");r.innerHTML=Fi,bn=r.firstChild}let t=bn.cloneNode(!0);e.appendChild(t);let n;t.addEventListener("click",()=>{clearTimeout(n);let r=Array.from(e.querySelectorAll("code > *")).filter(i=>!i.classList.contains("unselectable")||i.textContent!=="$ ").map(i=>i.textContent).join("");navigator.clipboard.writeText(r),t.classList.add("clicked"),t.disabled=!0,n=setTimeout(()=>{t.classList.remove("clicked"),t.disabled=!1},3e3)})})}window.addEventListener("exdoc:loaded",Bl);function Bl(){"clipboard"in navigator&&A("a.copy-markdown").forEach(e=>{e.addEventListener("click",Ql)})}function Ql(e){if(e.ctrlKey||e.metaKey)return;e.preventDefault();let n=e.currentTarget.href,r=new ClipboardItem({"text/plain":fetch(n).then(i=>{if(!i.ok)throw new Error("Failed to fetch markdown");return i.text()}).then(i=>new Blob([i],{type:"text/plain"}))});navigator.clipboard.write([r]).then(()=>{Ve("Page copied as Markdown to clipboard")}).catch(i=>{console.log("Copying Markdown failed:",i),window.confirm("Could not copy to clipboard. Do you want to open the Markdown page instead?")&&(window.location.href=n)})}var F=Y(Ui());var ji=Y(ae());var Wi=ji.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" Search results for "+e.escapeExpression((s=(s=o(n,"value")||(t!=null?o(t,"value"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"value",hash:{},data:i,loc:{start:{line:3,column:27},end:{line:3,column:36}}}):s))+` -`},3:function(e,t,n,r,i){return` Invalid search -`},5:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"each").call(t??(e.nullContext||{}),t!=null?o(t,"results"):t,{name:"each",hash:{},fn:e.program(6,i,0),inverse:e.noop,data:i,loc:{start:{line:10,column:2},end:{line:21,column:11}}}))!=null?s:""},6:function(e,t,n,r,i){var s,o=e.lambda,a=e.escapeExpression,l=e.lookupProperty||function(u,c){if(Object.prototype.hasOwnProperty.call(u,c))return u[c]};return`
    -

    - - `+a(o(t!=null?l(t,"title"):t,t))+" ("+a(o(t!=null?l(t,"type"):t,t))+`) - -

    -`+((s=l(n,"each").call(t??(e.nullContext||{}),t!=null?l(t,"excerpts"):t,{name:"each",hash:{},fn:e.program(7,i,0),inverse:e.noop,data:i,loc:{start:{line:17,column:8},end:{line:19,column:17}}}))!=null?s:"")+`
    -`},7:function(e,t,n,r,i){var s;return'

    '+((s=e.lambda(t,t))!=null?s:"")+`

    -`},9:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return((s=(o(n,"isArray")||t&&o(t,"isArray")||e.hooks.helperMissing).call(t??(e.nullContext||{}),t!=null?o(t,"results"):t,{name:"isArray",hash:{},fn:e.program(10,i,0),inverse:e.program(12,i,0),data:i,loc:{start:{line:23,column:2},end:{line:29,column:14}}}))!=null?s:"")+` -

    The search functionality is full-text based. Here are some tips:

    - -
      -
    • Multiple words (such as foo bar) are searched as OR
    • -
    • Use * anywhere (such as fo*) as wildcard
    • -
    • Use + before a word (such as +foo) to make its presence required
    • -
    • Use - before a word (such as -foo) to make its absence required
    • -
    • Use : to search on a particular field (such as field:word). The available fields are title, doc and type
    • -
    • Use WORD^NUMBER (such as foo^2) to boost the given word
    • -
    • Use WORD~NUMBER (such as foo~2) to do a search with edit distance on word
    • -
    - -

    To quickly go to a module, type, or function, use the autocompletion feature in the sidebar search.

    -`},10:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return"

    Sorry, we couldn't find anything for "+e.escapeExpression((s=(s=o(n,"value")||(t!=null?o(t,"value"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"value",hash:{},data:i,loc:{start:{line:24,column:48},end:{line:24,column:57}}}):s))+`.

    -`},12:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"value"):t,{name:"if",hash:{},fn:e.program(13,i,0),inverse:e.program(15,i,0),data:i,loc:{start:{line:25,column:2},end:{line:29,column:2}}}))!=null?s:""},13:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return"

    Invalid search: "+e.escapeExpression((s=(s=o(n,"errorMessage")||(t!=null?o(t,"errorMessage"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"errorMessage",hash:{},data:i,loc:{start:{line:26,column:23},end:{line:26,column:39}}}):s))+`.

    -`},15:function(e,t,n,r,i){return`

    Please type something into the search bar to perform a search.

    - `},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`

    -`+((s=a(n,"if").call(o,t!=null?a(t,"value"):t,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:2,column:2},end:{line:6,column:9}}}))!=null?s:"")+`

    - -`+((s=(a(n,"isNonEmptyArray")||t&&a(t,"isNonEmptyArray")||e.hooks.helperMissing).call(o,t!=null?a(t,"results"):t,{name:"isNonEmptyArray",hash:{},fn:e.program(5,i,0),inverse:e.program(9,i,0),data:i,loc:{start:{line:9,column:0},end:{line:44,column:20}}}))!=null?s:"")},useData:!0});var ql="#search";F.default.tokenizer.separator=/\s+/;F.default.QueryLexer.termSeparator=/\s+/;F.default.Pipeline.registerFunction(Ki,"docTokenSplitter");F.default.Pipeline.registerFunction(Yi,"docTrimmer");window.addEventListener("exdoc:loaded",Fl);function Fl(){let e=window.location.pathname;if(e.endsWith("/search.html")||e.endsWith("/search")){let t=er("q");Vl(t)}}async function Vl(e){if(he(e))En({value:e});else{Mi(e);let t=await $l();try{let n=e.replaceAll(/(\B|\\):/g,"\\:"),r=Zl(t.search(n));En({value:e,results:r})}catch(n){En({value:e,errorMessage:n.message})}}}function En({value:e,results:t,errorMessage:n}){let r=f(ql),i=Wi({value:e,results:t,errorMessage:n});r.innerHTML=i}async function $l(){let e=await Ul();if(e)return e;let t=Yl();return jl(t),t}async function Ul(){try{let e=sessionStorage.getItem(Gi());if(e){let t=await zl(e);return F.default.Index.load(t)}else return null}catch(e){return console.error("Failed to load index: ",e),null}}async function jl(e){try{let t=await Wl(e);sessionStorage.setItem(Gi(),t)}catch(t){console.error("Failed to save index: ",t)}}async function Wl(e){let t=new Blob([JSON.stringify(e)],{type:"application/json"}).stream().pipeThrough(new window.CompressionStream("gzip")),r=await(await new Response(t).blob()).arrayBuffer();return Gl(r)}async function zl(e){let t=new Blob([Kl(e)],{type:"application/json"}).stream().pipeThrough(new window.DecompressionStream("gzip")),n=await new Response(t).text();return JSON.parse(n)}function Gl(e){let t="",n=new Uint8Array(e),r=n.byteLength;for(let i=0;i{this.add(e)})})}function Jl(e){e.pipeline.before(F.default.stemmer,Ki)}function Ki(e){let t=[e],n=/\/\d+$/,r=/\:|\./,i=e.toString();if(i.replace(/^[.,;?!]+|[.,;]+$/g,""),i.startsWith("`")&&i.endsWith("`")&&(i=i.slice(1,-1)),n.test(i)){let o=e.toString().replace(n,"");t.push(e.clone().update(()=>o));let a=o.split(r);if(a.length>1){for(let u of a)t.push(e.clone().update(()=>u));let l=e.toString().split(r);t.push(e.clone().update(()=>l[l.length-1]))}i=a[a.length-1]}else i.startsWith("@")?(i=i.substring(1),t.push(e.clone().update(()=>i))):i.startsWith(":")&&(i=i.substring(1),t.push(e.clone().update(()=>i)));let s=i.split(/\_|\-/);if(s.length>1)for(let o of s)t.push(e.clone().update(()=>o));return t}function Xl(e){e.pipeline.before(F.default.stemmer,Yi)}function Yi(e){return e.update(function(t){return t.replace(/^[^@:\w]+/,"").replace(/[^\?\!\w]+$/,"")})}function Zl(e){return e.filter(t=>zi(t.ref)).map(t=>{let n=zi(t.ref),r=t.matchData.metadata;return{...n,metadata:r,excerpts:eu(n,r)}})}function zi(e){return searchData.items.find(t=>t.ref===e)||null}function eu(e,t){let{doc:n}=e,r=Object.keys(t);return[tu(n,r)]}function tu(e,t){let r=e.split(`\r -\r -`).flatMap(a=>a.split(` - -`)),i;r.length>=2?r[0].trim().startsWith("#")?i=r[1]:i=r[0]:r.length===1?i=r[0]:i=e;let s;i.length>200?s=i.slice(0,200)+"...":s=i;let o=He(s);for(let a of t){if(a.trim()==="")continue;let l=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),u=new RegExp(l,"gi");o=o.replace(u,c=>`${c}`)}return o}var Ji='';var nu='button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',M=null,Sn=null,xn=null,kn=!1;function ru(){M||(document.body.insertAdjacentHTML("beforeend",Ji),M=f(".modal"),M.addEventListener("keydown",e=>{e.key==="Escape"&&ue()}),M.querySelector(".modal-close").addEventListener("click",ue),M.addEventListener("click",e=>{e.target===M&&ue()}))}function Xi(e){if(!kn)if(M.contains(e.target))xn=e.target;else{kn=!0;let t=M.querySelectorAll(nu);xn===t[0]?t[t.length-1].focus():t[0].focus(),kn=!1,xn=document.activeElement}}function wt({title:e,body:t}){ru(),Sn=document.activeElement,document.addEventListener("focus",Xi,!0),M.querySelector(".modal-title").innerHTML=e,M.querySelector(".modal-body").innerHTML=t,M.classList.add("shown"),M.focus()}function ue(){M?.classList.remove("shown"),document.removeEventListener("focus",Xi,!0),Sn?.focus(),Sn=null}function Zi(){return!!M?.classList.contains("shown")}var es='
    ';var iu="https://hexdocs.pm/%%",su="https://www.erlang.org/doc/apps/%%",ou="https://hex.pm/api/packages?search=name:%%*",au=".display-quick-switch",ns="#quick-switch-input",rs="#quick-switch-results",lu=300,uu=9,is=["erts","asn1","common_test","compiler","crypto","debugger","dialyzer","diameter","edoc","eldap","erl_interface","et","eunit","ftp","inets","jinterface","kernel","megaco","mnesia","observer","odbc","os_mon","parsetools","public_key","reltool","runtime_tools","sasl","snmp","ssh","ssl","stdlib","syntax_tools","tftp","tools","wx","xmerl"],cu=["elixir","eex","ex_unit","hex","iex","logger","mix"].concat(is).map(e=>({name:e})),ss=2,D={autocompleteResults:[],selectedIdx:null};C||window.addEventListener("exdoc:loaded",du);function du(){A(au).forEach(e=>{e.addEventListener("click",Ln)})}function hu(e){if(e.key==="Enter"){let t=e.target.value;pu(t),e.preventDefault()}else e.key==="ArrowUp"?(ts(-1),e.preventDefault()):e.key==="ArrowDown"&&(ts(1),e.preventDefault())}function fu(e){let t=e.target.value;if(t.lengthn.json()).then(n=>{Array.isArray(n)&&(D.autocompleteResults=yu(e,n),D.selectedIdx=null,f(ns).value.length>=ss&&vu(D.autocompleteResults))})}function vu(e){f(rs).replaceChildren(...e.map(({name:t},n)=>{let r=E("div",{class:"quick-switch-result","data-index":n},[t]);return r.addEventListener("click",()=>_n(t)),r}))}function yu(e,t){return cu.concat(t).filter(n=>n.name.toLowerCase().includes(e.toLowerCase())).filter(n=>n.releases===void 0||n.releases[0].has_docs===!0).slice(0,uu)}function ts(e){D.selectedIdx=wu(e);let t=f(".quick-switch-result.selected"),n=f(`.quick-switch-result[data-index="${D.selectedIdx}"]`);t&&t.classList.remove("selected"),n&&n.classList.add("selected")}function wu(e){let t=D.autocompleteResults.length;if(D.selectedIdx===null){if(e>=0)return 0;if(e<0)return t-1}return(D.selectedIdx+e+t)%t}var bu="#settings-modal-content",On=[{key:"c",description:"Toggle sidebar",action:We},{key:"n",description:"Cycle themes",action:ar},{key:"s",description:"Focus search bar",displayAs:"/ or s",action:Tn},{key:"/",action:Tn},{key:"k",hasModifier:!0,action:Tn},{key:"v",description:"Open/focus version select",action:ku},{key:"g",description:"Go to package docs",displayAs:"g",action:Ln},{key:"?",displayAs:"?",description:"Bring up this modal",action:Su}],Pn={shortcutBeingPressed:null};C||(document.addEventListener("keydown",Eu),document.addEventListener("keyup",xu));function Eu(e){if(Pn.shortcutBeingPressed||e.target.matches("input, select, textarea"))return;let t=On.find(n=>n.hasModifier?Qe()&&e.metaKey||e.ctrlKey?n.key===e.key:!1:e.ctrlKey||e.metaKey||e.altKey?!1:n.key===e.key);t&&(Pn.shortcutBeingPressed=t,e.preventDefault(),t.action(e))}function xu(e){Pn.shortcutBeingPressed=null}function Tn(e){ue(),Ni()}function ku(){ue(),kr()?hn():_r().then(hn)}function Su(){_u()?ue():An()}function _u(){return Zi()&&f(bu)}var os=Y(ae());var as=os.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"description"):t,{name:"if",hash:{},fn:e.program(2,i,0),inverse:e.noop,data:i,loc:{start:{line:40,column:6},end:{line:53,column:13}}}))!=null?s:""},2:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
    -
    - `+e.escapeExpression(e.lambda(t!=null?o(t,"description"):t,t))+` -
    -
    -`+((s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"displayAs"):t,{name:"if",hash:{},fn:e.program(3,i,0),inverse:e.program(5,i,0),data:i,loc:{start:{line:46,column:12},end:{line:50,column:19}}}))!=null?s:"")+`
    -
    -`},3:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" "+((s=e.lambda(t!=null?o(t,"displayAs"):t,t))!=null?s:"")+` -`},5:function(e,t,n,r,i){var s=e.lookupProperty||function(o,a){if(Object.prototype.hasOwnProperty.call(o,a))return o[a]};return" "+e.escapeExpression(e.lambda(t!=null?s(t,"key"):t,t))+` -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
    -
    - - - - -
    - -
    -`},useData:!0});var Lu=".display-settings",Tu="#settings-modal-content",In="#modal-settings-tab",Cn="#modal-keyboard-shortcuts-tab",us="#settings-content",cs="#keyboard-shortcuts-content",Pu=[{title:"Settings",id:"modal-settings-tab"},{title:"Keyboard shortcuts",id:"modal-keyboard-shortcuts-tab"}];window.addEventListener("exdoc:loaded",Ou);function Ou(){A(Lu).forEach(e=>{e.addEventListener("click",An)})}function ls(){f(Cn).classList.remove("active"),f(In).classList.add("active"),f(us).classList.remove("hidden"),f(cs).classList.add("hidden")}function Au(){f(Cn).classList.add("active"),f(In).classList.remove("active"),f(cs).classList.remove("hidden"),f(us).classList.add("hidden")}function An(){wt({title:Pu.map(({id:s,title:o})=>``).join(""),body:as({shortcuts:On})});let e=f(Tu),t=e.querySelector('[name="theme"]'),n=e.querySelector('[name="tooltips"]'),r=e.querySelector('[name="direct_livebook_url"]'),i=e.querySelector('[name="livebook_url"]');R.getAndSubscribe(s=>{t.value=s.theme||"system",n.checked=s.tooltips,s.livebookUrl===null?(r.checked=!1,i.classList.add("hidden"),i.tabIndex=-1):(r.checked=!0,i.classList.remove("hidden"),i.tabIndex=0,i.value=s.livebookUrl)}),t.addEventListener("change",s=>{R.update({theme:s.target.value})}),n.addEventListener("change",s=>{R.update({tooltips:s.target.checked})}),r.addEventListener("change",s=>{let o=s.target.checked?i.value:null;R.update({livebookUrl:o})}),i.addEventListener("input",s=>{R.update({livebookUrl:s.target.value})}),f(In).addEventListener("click",s=>{ls()}),f(Cn).addEventListener("click",s=>{Au()}),ls()}var Rn=new WeakMap;function Mn(e,t,n,r){if(!e&&!Rn.has(t))return!1;let i=Rn.get(t)??new WeakMap;Rn.set(t,i);let s=i.get(n)??new Set;i.set(n,s);let o=s.has(r);return e?s.add(r):s.delete(r),o&&e}function Iu(e,t){let n=e.target;if(n instanceof Text&&(n=n.parentElement),n instanceof Element&&e.currentTarget instanceof Node){let r=n.closest(t);if(r&&e.currentTarget.contains(r))return r}}function Cu(e,t,n,r={}){let{signal:i,base:s=document}=r;if(i?.aborted)return;let{once:o,...a}=r,l=s instanceof Document?s.documentElement:s,u=!!(typeof r=="object"?r.capture:r),c=p=>{let g=Iu(p,String(e));if(g){let m=Object.assign(p,{delegateTarget:g});n.call(l,m),o&&(l.removeEventListener(t,c,a),Mn(!1,l,n,d))}},d=JSON.stringify({selector:e,type:t,capture:u});Mn(!0,l,n,d)||l.addEventListener(t,c,a),i?.addEventListener("abort",()=>{Mn(!1,l,n,d)})}var bt=Cu;function O(){return O=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).toLowerCase().replace(/[\s/_.]+/g,"-").replace(/[^\w-]+/g,"").replace(/--+/g,"-").replace(/^-+|-+$/g,"")||t||"",Oe=({hash:e}={})=>window.location.pathname+window.location.search+(e?window.location.hash:""),Ru=(e,t={})=>{let n=O({url:e=e||Oe({hash:!0}),random:Math.random(),source:"swup"},t);window.history.pushState(n,"",e)},Pe=(e=null,t={})=>{e=e||Oe({hash:!0});let n=O({},window.history.state||{},{url:e,random:Math.random(),source:"swup"},t);window.history.replaceState(n,"",e)},Mu=(e,t,n,r)=>{let i=new AbortController;return r=O({},r,{signal:i.signal}),bt(e,t,n,r),{destroy:()=>i.abort()}},N=class e extends URL{constructor(t,n=document.baseURI){super(t.toString(),n),Object.setPrototypeOf(this,e.prototype)}get url(){return this.pathname+this.search}static fromElement(t){let n=t.getAttribute("href")||t.getAttribute("xlink:href")||"";return new e(n)}static fromUrl(t){return new e(t)}};var ge=class extends Error{constructor(t,n){super(t),this.url=void 0,this.status=void 0,this.aborted=void 0,this.timedOut=void 0,this.name="FetchError",this.url=n.url,this.status=n.status,this.aborted=n.aborted||!1,this.timedOut=n.timedOut||!1}};async function Nu(e,t={}){var n;e=N.fromUrl(e).url;let{visit:r=this.visit}=t,i=O({},this.options.requestHeaders,t.headers),s=(n=t.timeout)!=null?n:this.options.timeout,o=new AbortController,{signal:a}=o;t=O({},t,{headers:i,signal:a});let l,u=!1,c=null;s&&s>0&&(c=setTimeout(()=>{u=!0,o.abort("timeout")},s));try{l=await this.hooks.call("fetch:request",r,{url:e,options:t},(v,{url:w,options:x})=>fetch(w,x)),c&&clearTimeout(c)}catch(v){throw u?(this.hooks.call("fetch:timeout",r,{url:e}),new ge(`Request timed out: ${e}`,{url:e,timedOut:u})):v?.name==="AbortError"||a.aborted?new ge(`Request aborted: ${e}`,{url:e,aborted:!0}):v}let{status:d,url:h}=l,p=await l.text();if(d===500)throw this.hooks.call("fetch:error",r,{status:d,response:l,url:h}),new ge(`Server error: ${h}`,{status:d,url:h});if(!p)throw new ge(`Empty response: ${h}`,{status:d,url:h});let{url:g}=N.fromUrl(h),m={url:g,html:p};return!r.cache.write||t.method&&t.method!=="GET"||e!==g||this.cache.set(m.url,m),m}var Hn=class{constructor(t){this.swup=void 0,this.pages=new Map,this.swup=t}get size(){return this.pages.size}get all(){let t=new Map;return this.pages.forEach((n,r)=>{t.set(r,O({},n))}),t}has(t){return this.pages.has(this.resolve(t))}get(t){let n=this.pages.get(this.resolve(t));return n&&O({},n)}set(t,n){n=O({},n,{url:t=this.resolve(t)}),this.pages.set(t,n),this.swup.hooks.callSync("cache:set",void 0,{page:n})}update(t,n){t=this.resolve(t);let r=O({},this.get(t),n,{url:t});this.pages.set(t,r)}delete(t){this.pages.delete(this.resolve(t))}clear(){this.pages.clear(),this.swup.hooks.callSync("cache:clear",void 0,void 0)}prune(t){this.pages.forEach((n,r)=>{t(r,n)&&this.delete(r)})}resolve(t){let{url:n}=N.fromUrl(t);return this.swup.resolveUrl(n)}},Dn=(e,t=document)=>t.querySelector(e),qn=(e,t=document)=>Array.from(t.querySelectorAll(e)),ms=()=>new Promise(e=>{requestAnimationFrame(()=>{requestAnimationFrame(()=>{e()})})});function gs(e){return!!e&&(typeof e=="object"||typeof e=="function")&&typeof e.then=="function"}function Hu(e,t=[]){return new Promise((n,r)=>{let i=e(...t);gs(i)?i.then(n,r):n(i)})}function ds(e,t){let n=e?.closest(`[${t}]`);return n!=null&&n.hasAttribute(t)?n?.getAttribute(t)||!0:void 0}var Bn=class{constructor(t){this.swup=void 0,this.swupClasses=["to-","is-changing","is-rendering","is-popstate","is-animating","is-leaving"],this.swup=t}get selectors(){let{scope:t}=this.swup.visit.animation;return t==="containers"?this.swup.visit.containers:t==="html"?["html"]:Array.isArray(t)?t:[]}get selector(){return this.selectors.join(",")}get targets(){return this.selector.trim()?qn(this.selector):[]}add(...t){this.targets.forEach(n=>n.classList.add(...t))}remove(...t){this.targets.forEach(n=>n.classList.remove(...t))}clear(){this.targets.forEach(t=>{let n=t.className.split(" ").filter(r=>this.isSwupClass(r));t.classList.remove(...n)})}isSwupClass(t){return this.swupClasses.some(n=>t.startsWith(n))}},kt=class{constructor(t,n){this.id=void 0,this.state=void 0,this.from=void 0,this.to=void 0,this.containers=void 0,this.animation=void 0,this.trigger=void 0,this.cache=void 0,this.history=void 0,this.scroll=void 0,this.meta=void 0;let{to:r,from:i,hash:s,el:o,event:a}=n;this.id=Math.random(),this.state=1,this.from={url:i??t.location.url,hash:t.location.hash},this.to={url:r,hash:s},this.containers=t.options.containers,this.animation={animate:!0,wait:!1,name:void 0,native:t.options.native,scope:t.options.animationScope,selector:t.options.animationSelector},this.trigger={el:o,event:a},this.cache={read:t.options.cache,write:t.options.cache},this.history={action:"push",popstate:!1,direction:void 0},this.scroll={reset:!0,target:void 0},this.meta={}}advance(t){this.state=7}};function Du(e){return new kt(this,e)}var Qn=class{constructor(t){this.swup=void 0,this.registry=new Map,this.hooks=["animation:out:start","animation:out:await","animation:out:end","animation:in:start","animation:in:await","animation:in:end","animation:skip","cache:clear","cache:set","content:replace","content:scroll","enable","disable","fetch:request","fetch:error","fetch:timeout","history:popstate","link:click","link:self","link:anchor","link:newtab","page:load","page:view","scroll:top","scroll:anchor","visit:start","visit:transition","visit:abort","visit:end"],this.swup=t,this.init()}init(){this.hooks.forEach(t=>this.create(t))}create(t){this.registry.has(t)||this.registry.set(t,new Map)}exists(t){return this.registry.has(t)}get(t){let n=this.registry.get(t);if(n)return n;console.error(`Unknown hook '${t}'`)}clear(){this.registry.forEach(t=>t.clear())}on(t,n,r={}){let i=this.get(t);if(!i)return console.warn(`Hook '${t}' not found.`),()=>{};let s=O({},r,{id:i.size+1,hook:t,handler:n});return i.set(n,s),()=>this.off(t,n)}before(t,n,r={}){return this.on(t,n,O({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,O({},r,{replace:!0}))}once(t,n,r={}){return this.on(t,n,O({},r,{once:!0}))}off(t,n){let r=this.get(t);r&&n?r.delete(n)||console.warn(`Handler for hook '${t}' not found.`):r&&r.clear()}async call(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);await this.run(l,s,o);let[d]=await this.run(u,s,o,!0);return await this.run(c,s,o),this.dispatchDomEvent(t,s,o),d}callSync(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);this.runSync(l,s,o);let[d]=this.runSync(u,s,o,!0);return this.runSync(c,s,o),this.dispatchDomEvent(t,s,o),d}parseCallArgs(t,n,r,i){return n instanceof kt||typeof n!="object"&&typeof r!="function"?[n,r,i]:[void 0,n,r]}async run(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=await Hu(a,[n,r,l]);s.push(c)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}runSync(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=a(n,r,l);s.push(c),gs(c)&&console.warn(`Swup will not await Promises in handler for synchronous hook '${o}'.`)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}getHandlers(t,n){let r=this.get(t);if(!r)return{found:!1,before:[],handler:[],after:[],replaced:!1};let i=Array.from(r.values()),s=this.sortRegistrations,o=i.filter(({before:d,replace:h})=>d&&!h).sort(s),a=i.filter(({replace:d})=>d).filter(d=>!0).sort(s),l=i.filter(({before:d,replace:h})=>!d&&!h).sort(s),u=a.length>0,c=[];if(n&&(c=[{id:0,hook:t,handler:n}],u)){let d=a.length-1,{handler:h,once:p}=a[d],g=m=>{let v=a[m-1];return v?(w,x)=>v.handler(w,x,g(m-1)):n};c=[{id:0,hook:t,once:p,handler:h,defaultHandler:g(d)}]}return{found:!0,before:o,handler:c,after:l,replaced:u}}sortRegistrations(t,n){var r,i;return((r=t.priority)!=null?r:0)-((i=n.priority)!=null?i:0)||t.id-n.id||0}dispatchDomEvent(t,n,r){if(n!=null&&n.done)return;let i={hook:t,args:r,visit:n||this.swup.visit};document.dispatchEvent(new CustomEvent("swup:any",{detail:i,bubbles:!0})),document.dispatchEvent(new CustomEvent(`swup:${t}`,{detail:i,bubbles:!0}))}parseName(t){let[n,...r]=t.split(".");return[n,r.reduce((i,s)=>O({},i,{[s]:!0}),{})]}},Bu=e=>{if(e&&e.charAt(0)==="#"&&(e=e.substring(1)),!e)return null;let t=decodeURIComponent(e),n=document.getElementById(e)||document.getElementById(t)||Dn(`a[name='${CSS.escape(e)}']`)||Dn(`a[name='${CSS.escape(t)}']`);return n||e!=="top"||(n=document.body),n},Et="transition",Nn="animation";async function Qu({selector:e,elements:t}){if(e===!1&&!t)return;let n=[];if(t)n=Array.from(t);else if(e&&(n=qn(e,document.body),!n.length))return void console.warn(`[swup] No elements found matching animationSelector \`${e}\``);let r=n.map(i=>(function(s){let{type:o,timeout:a,propCount:l}=(function(u){let c=window.getComputedStyle(u),d=xt(c,`${Et}Delay`),h=xt(c,`${Et}Duration`),p=hs(d,h),g=xt(c,`${Nn}Delay`),m=xt(c,`${Nn}Duration`),v=hs(g,m),w=Math.max(p,v),x=w>0?p>v?Et:Nn:null;return{type:x,timeout:w,propCount:x?x===Et?h.length:m.length:0}})(s);return!(!o||!a)&&new Promise(u=>{let c=`${o}end`,d=performance.now(),h=0,p=()=>{s.removeEventListener(c,g),u()},g=m=>{m.target===s&&((performance.now()-d)/1e3=l&&p())};setTimeout(()=>{h0?await Promise.all(r):e&&console.warn(`[swup] No CSS animation duration defined on elements matching \`${e}\``)}function xt(e,t){return(e[t]||"").split(", ")}function hs(e,t){for(;e.lengthfs(n)+fs(e[r])))}function fs(e){return 1e3*parseFloat(e)}function qu(e,t={},n={}){if(typeof e!="string")throw new Error("swup.navigate() requires a URL parameter");if(this.shouldIgnoreVisit(e,{el:n.el,event:n.event}))return void window.location.assign(e);let{url:r,hash:i}=N.fromUrl(e),s=this.createVisit(O({},n,{to:r,hash:i}));this.performNavigation(s,t)}async function Fu(e,t={}){if(this.navigating){if(this.visit.state>=6)return e.state=2,void(this.onVisitEnd=()=>this.performNavigation(e,t));await this.hooks.call("visit:abort",this.visit,void 0),delete this.visit.to.document,this.visit.state=8}this.navigating=!0,this.visit=e;let{el:n}=e.trigger;t.referrer=t.referrer||this.location.url,t.animate===!1&&(e.animation.animate=!1),e.animation.animate||this.classes.clear();let r=t.history||ds(n,"data-swup-history");typeof r=="string"&&["push","replace"].includes(r)&&(e.history.action=r);let i=t.animation||ds(n,"data-swup-animation");var s,o;typeof i=="string"&&(e.animation.name=i),e.meta=t.meta||{},typeof t.cache=="object"?(e.cache.read=(s=t.cache.read)!=null?s:e.cache.read,e.cache.write=(o=t.cache.write)!=null?o:e.cache.write):t.cache!==void 0&&(e.cache={read:!!t.cache,write:!!t.cache}),delete t.cache;try{await this.hooks.call("visit:start",e,void 0),e.state=3;let a=this.hooks.call("page:load",e,{options:t},async(u,c)=>{let d;return u.cache.read&&(d=this.cache.get(u.to.url)),c.page=d||await this.fetchPage(u.to.url,c.options),c.cache=!!d,c.page});a.then(({html:u})=>{e.advance(5),e.to.html=u,e.to.document=new DOMParser().parseFromString(u,"text/html")});let l=e.to.url+e.to.hash;if(e.history.popstate||(e.history.action==="replace"||e.to.url===this.location.url?Pe(l):(this.currentHistoryIndex++,Ru(l,{index:this.currentHistoryIndex}))),this.location=N.fromUrl(l),e.history.popstate&&this.classes.add("is-popstate"),e.animation.name&&this.classes.add(`to-${ps(e.animation.name)}`),e.animation.wait&&await a,e.done||(await this.hooks.call("visit:transition",e,void 0,async()=>{if(!e.animation.animate)return await this.hooks.call("animation:skip",void 0),void await this.renderPage(e,await a);e.advance(4),await this.animatePageOut(e),e.animation.native&&document.startViewTransition?await document.startViewTransition(async()=>await this.renderPage(e,await a)).finished:await this.renderPage(e,await a),await this.animatePageIn(e)}),e.done))return;await this.hooks.call("visit:end",e,void 0,()=>this.classes.clear()),e.state=7,this.navigating=!1,this.onVisitEnd&&(this.onVisitEnd(),this.onVisitEnd=void 0)}catch(a){if(!a||a!=null&&a.aborted)return void(e.state=8);e.state=9,console.error(a),this.options.skipPopStateHandling=()=>(window.location.assign(e.to.url+e.to.hash),!0),window.history.back()}finally{delete e.to.document}}var Vu=async function(e){await this.hooks.call("animation:out:start",e,void 0,()=>{this.classes.add("is-changing","is-animating","is-leaving")}),await this.hooks.call("animation:out:await",e,{skip:!1},(t,{skip:n})=>{if(!n)return this.awaitAnimations({selector:t.animation.selector})}),await this.hooks.call("animation:out:end",e,void 0)},$u=function(e){var t;let n=e.to.document;if(!n)return!1;let r=((t=n.querySelector("title"))==null?void 0:t.innerText)||"";document.title=r;let i=qn('[data-swup-persist]:not([data-swup-persist=""])'),s=e.containers.map(o=>{let a=document.querySelector(o),l=n.querySelector(o);return a&&l?(a.replaceWith(l.cloneNode(!0)),!0):(a||console.warn(`[swup] Container missing in current document: ${o}`),l||console.warn(`[swup] Container missing in incoming document: ${o}`),!1)}).filter(Boolean);return i.forEach(o=>{let a=o.getAttribute("data-swup-persist"),l=Dn(`[data-swup-persist="${a}"]`);l&&l!==o&&l.replaceWith(o)}),s.length===e.containers.length},Uu=function(e){let t={behavior:"auto"},{target:n,reset:r}=e.scroll,i=n??e.to.hash,s=!1;return i&&(s=this.hooks.callSync("scroll:anchor",e,{hash:i,options:t},(o,{hash:a,options:l})=>{let u=this.getAnchorElement(a);return u&&u.scrollIntoView(l),!!u})),r&&!s&&(s=this.hooks.callSync("scroll:top",e,{options:t},(o,{options:a})=>(window.scrollTo(O({top:0,left:0},a)),!0))),s},ju=async function(e){if(e.done)return;let t=this.hooks.call("animation:in:await",e,{skip:!1},(n,{skip:r})=>{if(!r)return this.awaitAnimations({selector:n.animation.selector})});await ms(),await this.hooks.call("animation:in:start",e,void 0,()=>{this.classes.remove("is-animating")}),await t,await this.hooks.call("animation:in:end",e,void 0)},Wu=async function(e,t){if(e.done)return;e.advance(6);let{url:n}=t;this.isSameResolvedUrl(Oe(),n)||(Pe(n),this.location=N.fromUrl(n),e.to.url=this.location.url,e.to.hash=this.location.hash),await this.hooks.call("content:replace",e,{page:t},(r,{})=>{if(this.classes.remove("is-leaving"),r.animation.animate&&this.classes.add("is-rendering"),!this.replaceContent(r))throw new Error("[swup] Container mismatch, aborting");r.animation.animate&&(this.classes.add("is-changing","is-animating","is-rendering"),r.animation.name&&this.classes.add(`to-${ps(r.animation.name)}`))}),await this.hooks.call("content:scroll",e,void 0,()=>this.scrollToContent(e)),await this.hooks.call("page:view",e,{url:this.location.url,title:document.title})},zu=function(e){var t;if(t=e,!!t?.isSwupPlugin){if(e.swup=this,!e._checkRequirements||e._checkRequirements())return e._beforeMount&&e._beforeMount(),e.mount(),this.plugins.push(e),this.plugins}else console.error("Not a swup plugin instance",e)};function Gu(e){let t=this.findPlugin(e);if(t)return t.unmount(),t._afterUnmount&&t._afterUnmount(),this.plugins=this.plugins.filter(n=>n!==t),this.plugins;console.error("No such plugin",t)}function Ku(e){return this.plugins.find(t=>t===e||t.name===e||t.name===`Swup${String(e)}`)}function Yu(e){if(typeof this.options.resolveUrl!="function")return console.warn("[swup] options.resolveUrl expects a callback function."),e;let t=this.options.resolveUrl(e);return t&&typeof t=="string"?t.startsWith("//")||t.startsWith("http")?(console.warn("[swup] options.resolveUrl needs to return a relative url"),e):t:(console.warn("[swup] options.resolveUrl needs to return a url"),e)}function Ju(e,t){return this.resolveUrl(e)===this.resolveUrl(t)}var Xu={animateHistoryBrowsing:!1,animationSelector:'[class*="transition-"]',animationScope:"html",cache:!0,containers:["#swup"],hooks:{},ignoreVisit:(e,{el:t}={})=>!(t==null||!t.closest("[data-no-swup]")),linkSelector:"a[href]",linkToSelf:"scroll",native:!1,plugins:[],resolveUrl:e=>e,requestHeaders:{"X-Requested-With":"swup",Accept:"text/html, application/xhtml+xml"},skipPopStateHandling:e=>{var t;return((t=e.state)==null?void 0:t.source)!=="swup"},timeout:0},St=class{get currentPageUrl(){return this.location.url}constructor(t={}){var n,r;this.version="4.8.2",this.options=void 0,this.defaults=Xu,this.plugins=[],this.visit=void 0,this.cache=void 0,this.hooks=void 0,this.classes=void 0,this.location=N.fromUrl(window.location.href),this.currentHistoryIndex=void 0,this.clickDelegate=void 0,this.navigating=!1,this.onVisitEnd=void 0,this.use=zu,this.unuse=Gu,this.findPlugin=Ku,this.log=()=>{},this.navigate=qu,this.performNavigation=Fu,this.createVisit=Du,this.delegateEvent=Mu,this.fetchPage=Nu,this.awaitAnimations=Qu,this.renderPage=Wu,this.replaceContent=$u,this.animatePageIn=ju,this.animatePageOut=Vu,this.scrollToContent=Uu,this.getAnchorElement=Bu,this.getCurrentUrl=Oe,this.resolveUrl=Yu,this.isSameResolvedUrl=Ju,this.options=O({},this.defaults,t),this.handleLinkClick=this.handleLinkClick.bind(this),this.handlePopState=this.handlePopState.bind(this),this.cache=new Hn(this),this.classes=new Bn(this),this.hooks=new Qn(this),this.visit=this.createVisit({to:""}),this.currentHistoryIndex=(n=(r=window.history.state)==null?void 0:r.index)!=null?n:1,this.enable()}async enable(){var t;let{linkSelector:n}=this.options;this.clickDelegate=this.delegateEvent(n,"click",this.handleLinkClick),window.addEventListener("popstate",this.handlePopState),this.options.animateHistoryBrowsing&&(window.history.scrollRestoration="manual"),this.options.native=this.options.native&&!!document.startViewTransition,this.options.plugins.forEach(r=>this.use(r));for(let[r,i]of Object.entries(this.options.hooks)){let[s,o]=this.hooks.parseName(r);this.hooks.on(s,i,o)}((t=window.history.state)==null?void 0:t.source)!=="swup"&&Pe(null,{index:this.currentHistoryIndex}),await ms(),await this.hooks.call("enable",void 0,void 0,()=>{let r=document.documentElement;r.classList.add("swup-enabled"),r.classList.toggle("swup-native",this.options.native)})}async destroy(){this.clickDelegate.destroy(),window.removeEventListener("popstate",this.handlePopState),this.cache.clear(),this.options.plugins.forEach(t=>this.unuse(t)),await this.hooks.call("disable",void 0,void 0,()=>{let t=document.documentElement;t.classList.remove("swup-enabled"),t.classList.remove("swup-native")}),this.hooks.clear()}shouldIgnoreVisit(t,{el:n,event:r}={}){let{origin:i,url:s,hash:o}=N.fromUrl(t);return i!==window.location.origin||!(!n||!this.triggerWillOpenNewWindow(n))||!!this.options.ignoreVisit(s+o,{el:n,event:r})}handleLinkClick(t){let n=t.delegateTarget,{href:r,url:i,hash:s}=N.fromElement(n);if(this.shouldIgnoreVisit(r,{el:n,event:t}))return;if(this.navigating&&i===this.visit.to.url)return void t.preventDefault();let o=this.createVisit({to:i,hash:s,el:n,event:t});t.metaKey||t.ctrlKey||t.shiftKey||t.altKey?this.hooks.callSync("link:newtab",o,{href:r}):t.button===0&&this.hooks.callSync("link:click",o,{el:n,event:t},()=>{var a;let l=(a=o.from.url)!=null?a:"";t.preventDefault(),i&&i!==l?this.isSameResolvedUrl(i,l)||this.performNavigation(o):s?this.hooks.callSync("link:anchor",o,{hash:s},()=>{Pe(i+s),this.scrollToContent(o)}):this.hooks.callSync("link:self",o,void 0,()=>{this.options.linkToSelf==="navigate"?this.performNavigation(o):(Pe(i),this.scrollToContent(o))})})}handlePopState(t){var n,r,i,s;let o=(n=(r=t.state)==null?void 0:r.url)!=null?n:window.location.href;if(this.options.skipPopStateHandling(t)||this.isSameResolvedUrl(Oe(),this.location.url))return;let{url:a,hash:l}=N.fromUrl(o),u=this.createVisit({to:a,hash:l,event:t});u.history.popstate=!0;let c=(i=(s=t.state)==null?void 0:s.index)!=null?i:0;c&&c!==this.currentHistoryIndex&&(u.history.direction=c-this.currentHistoryIndex>0?"forwards":"backwards",this.currentHistoryIndex=c),u.animation.animate=!1,u.scroll.reset=!1,u.scroll.target=!1,this.options.animateHistoryBrowsing&&(u.animation.animate=!0,u.scroll.reset=!0),this.hooks.callSync("history:popstate",u,{event:t},()=>{this.performNavigation(u)})}triggerWillOpenNewWindow(t){return!!t.matches('[download], [target="_blank"]')}};function Ae(){return Ae=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).split(".").map(t=>String(parseInt(t||"0",10))).concat(["0","0"]).slice(0,3).join("."),ve=class{constructor(){this.isSwupPlugin=!0,this.swup=void 0,this.version=void 0,this.requires={},this.handlersToUnregister=[]}mount(){}unmount(){this.handlersToUnregister.forEach(t=>t()),this.handlersToUnregister=[]}_beforeMount(){if(!this.name)throw new Error("You must define a name of plugin when creating a class.")}_afterUnmount(){}_checkRequirements(){return typeof this.requires!="object"||Object.entries(this.requires).forEach(([t,n])=>{if(!(function(r,i,s){let o=(function(a,l){var u;if(a==="swup")return(u=l.version)!=null?u:"";{var c;let d=l.findPlugin(a);return(c=d?.version)!=null?c:""}})(r,s);return!!o&&((a,l)=>l.every(u=>{let[,c,d]=u.match(/^([\D]+)?(.*)$/)||[];var h,p;return((g,m)=>{let v={"":w=>w===0,">":w=>w>0,">=":w=>w>=0,"<":w=>w<0,"<=":w=>w<=0};return(v[m]||v[""])(g)})((p=d,h=vs(h=a),p=vs(p),h.localeCompare(p,void 0,{numeric:!0})),c||">=")}))(o,i)})(t,n=Array.isArray(n)?n:[n],this.swup)){let r=`${t} ${n.join(", ")}`;throw new Error(`Plugin version mismatch: ${this.name} requires ${r}`)}}),!0}on(t,n,r={}){var i;n=!(i=n).name.startsWith("bound ")||i.hasOwnProperty("prototype")?n.bind(this):n;let s=this.swup.hooks.on(t,n,r);return this.handlersToUnregister.push(s),s}once(t,n,r={}){return this.on(t,n,Ae({},r,{once:!0}))}before(t,n,r={}){return this.on(t,n,Ae({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,Ae({},r,{replace:!0}))}off(t,n){return this.swup.hooks.off(t,n)}};(function(){if(!(typeof window>"u"||typeof document>"u"||typeof HTMLElement>"u")){var e=!1;try{var t=document.createElement("div");t.addEventListener("focus",function(s){s.preventDefault(),s.stopPropagation()},!0),t.focus(Object.defineProperty({},"preventScroll",{get:function(){if(navigator&&typeof navigator.userAgent<"u"&&navigator.userAgent&&navigator.userAgent.match(/Edge\/1[7-8]/))return e=!1;e=!0}}))}catch{}if(HTMLElement.prototype.nativeFocus===void 0&&!e){HTMLElement.prototype.nativeFocus=HTMLElement.prototype.focus;var n=function(s){for(var o=s.parentNode,a=[],l=document.scrollingElement||document.documentElement;o&&o!==l;)(o.offsetHeightn.replace(`{${r}}`,t[r]||""),e||"")}var Vn=class{constructor(){var t;this.id="swup-announcer",this.style="position:absolute;top:0;left:0;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden;white-space:nowrap;word-wrap:normal;width:1px;height:1px;",this.region=void 0,this.region=(t=this.getRegion())!=null?t:this.createRegion()}getRegion(){return document.getElementById(this.id)}createRegion(){let t=(function(n){let r=document.createElement("template");return r.innerHTML=n,r.content.children[0]})(`

    `);return document.body.appendChild(t),t}announce(t,n=0){return new Promise(r=>{setTimeout(()=>{this.region.textContent===t&&(t=`${t}.`),this.region.textContent="",this.region.textContent=t,r()},n)})}};function ws(e){let t;if(t=typeof e=="string"?document.querySelector(e):e,!(t instanceof HTMLElement))return;let n=t.getAttribute("tabindex");t.setAttribute("tabindex","-1"),t.focus({preventScroll:!0}),n!==null&&t.setAttribute("tabindex",n)}var _t=class extends ve{constructor(t={}){super(),this.name="SwupA11yPlugin",this.requires={swup:">=4"},this.defaults={headingSelector:"h1",respectReducedMotion:!0,autofocus:!1,announcements:{visit:"Navigated to: {title}",url:"New page at {url}"}},this.options=void 0,this.announcer=void 0,this.announcementDelay=100,this.rootSelector="body",this.handleAnchorScroll=(n,{hash:r})=>{let i=this.swup.getAnchorElement(r);i instanceof HTMLElement&&ws(i)},this.options=Fn({},this.defaults,t),this.announcer=new Vn}mount(){this.swup.hooks.create("content:announce"),this.swup.hooks.create("content:focus"),this.before("visit:start",this.prepareVisit),this.on("visit:start",this.markAsBusy),this.on("visit:end",this.unmarkAsBusy),this.on("visit:end",this.focusContent),this.on("visit:end",this.announceContent),this.on("scroll:anchor",this.handleAnchorScroll),this.before("visit:start",this.disableAnimations),this.before("link:self",this.disableAnimations),this.before("link:anchor",this.disableAnimations),this.swup.announce=this.announce.bind(this)}unmount(){this.swup.announce=void 0}async announce(t){await this.announcer.announce(t)}markAsBusy(){document.documentElement.setAttribute("aria-busy","true")}unmarkAsBusy(){document.documentElement.removeAttribute("aria-busy")}prepareVisit(t){t.a11y={announce:void 0,focus:this.rootSelector}}announceContent(t){this.swup.hooks.callSync("content:announce",t,void 0,n=>{n.a11y.announce===void 0&&(n.a11y.announce=this.getPageAnnouncement()),n.a11y.announce&&this.announcer.announce(n.a11y.announce,this.announcementDelay)})}focusContent(t){this.swup.hooks.callSync("content:focus",t,void 0,n=>{n.a11y.focus&&(this.options.autofocus&&(function(){let r=(function(){let i=document.querySelector("body [autofocus]");if(i&&!i.closest('[inert], [aria-disabled], [aria-hidden="true"]'))return i})();return!!r&&(r!==document.activeElement&&r.focus(),!0)})()===!0||ws(n.a11y.focus))})}getPageAnnouncement(){let{headingSelector:t,announcements:n}=this.options;return(function({headingSelector:r="h1",announcements:i={}}){var s,o;let a=document.documentElement.lang||"*",{href:l,url:u,pathname:c}=N.fromUrl(window.location.href),d=(s=(o=i[a])!=null?o:i["*"])!=null?s:i;if(typeof d!="object")return;let h=document.querySelector(r);h||console.warn(`SwupA11yPlugin: No main heading (${r}) found on new page`);let p=h?.getAttribute("aria-label")||h?.textContent||document.title||ys(d.url,{href:l,url:u,path:c});return ys(d.visit,{title:p,href:l,url:u,path:c})})({headingSelector:t,announcements:n})}disableAnimations(t){this.options.respectReducedMotion&&window.matchMedia("(prefers-reduced-motion: reduce)").matches&&(t.animation.animate=!1,t.scroll.animate=!1)}};function $n(){return $n=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let a=Math.random()*this.trickleValue;this.setValue(this.value+a)},t!==void 0&&(this.className=String(t)),n!==void 0&&(this.styleAttr=String(n)),r!==void 0&&(this.animationDuration=Number(r)),i!==void 0&&(this.minValue=Number(i)),s!==void 0&&(this.initialValue=Number(s)),o!==void 0&&(this.trickleValue=Number(o)),this.styleElement=this.createStyleElement(),this.progressElement=this.createProgressElement()}get defaultStyles(){return` - .${this.className} { - position: fixed; - display: block; - top: 0; - left: 0; - width: 100%; - height: 3px; - background-color: black; - z-index: 9999; - transition: - transform ${this.animationDuration}ms ease-out, - opacity ${this.animationDuration/2}ms ${this.animationDuration/2}ms ease-in; - transform: translate3d(0, 0, 0) scaleX(var(--progress, 0)); - transform-origin: 0; - } - `}show(){this.visible||(this.visible=!0,this.installStyleElement(),this.installProgressElement(),this.startTrickling())}hide(){this.visible&&!this.hiding&&(this.hiding=!0,this.fadeProgressElement(()=>{this.uninstallProgressElement(),this.stopTrickling(),this.visible=!1,this.hiding=!1}))}setValue(t){this.value=Math.min(1,Math.max(this.minValue,t)),this.refresh()}installStyleElement(){document.head.prepend(this.styleElement)}installProgressElement(){this.progressElement.style.setProperty("--progress",String(0)),this.progressElement.style.opacity="1",document.body.prepend(this.progressElement),this.progressElement.scrollTop=0,this.setValue(Math.random()*this.initialValue)}fadeProgressElement(t){this.progressElement.style.opacity="0",setTimeout(t,1.5*this.animationDuration)}uninstallProgressElement(){this.progressElement.remove()}startTrickling(){this.trickleInterval||(this.trickleInterval=window.setInterval(this.trickle,this.animationDuration))}stopTrickling(){window.clearInterval(this.trickleInterval),delete this.trickleInterval}refresh(){requestAnimationFrame(()=>{this.progressElement.style.setProperty("--progress",String(this.value))})}createStyleElement(){let t=document.createElement("style");return this.styleAttr.split(" ").forEach(n=>t.setAttribute(n,"")),t.textContent=this.defaultStyles,t}createProgressElement(){let t=document.createElement("div");return t.className=this.className,t.setAttribute("aria-hidden","true"),t}},Lt=class extends ve{constructor(t={}){super(),this.name="SwupProgressPlugin",this.defaults={className:"swup-progress-bar",delay:300,transition:300,minValue:.1,initialValue:.25,finishAnimation:!0},this.options=void 0,this.progressBar=void 0,this.showProgressBarTimeout=void 0,this.hideProgressBarTimeout=void 0,this.options=$n({},this.defaults,t);let{className:n,minValue:r,initialValue:i,transition:s}=this.options;this.progressBar=new Un({className:n,minValue:r,initialValue:i,animationDuration:s})}mount(){this.on("visit:start",this.startShowingProgress),this.on("page:view",this.stopShowingProgress)}startShowingProgress(){this.progressBar.setValue(0),this.showProgressBarAfterDelay()}stopShowingProgress(){this.progressBar.setValue(1),this.options.finishAnimation?this.finishAnimationAndHideProgressBar():this.hideProgressBar()}showProgressBar(){this.cancelHideProgressBarTimeout(),this.progressBar.show()}showProgressBarAfterDelay(){this.cancelShowProgressBarTimeout(),this.cancelHideProgressBarTimeout(),this.showProgressBarTimeout=window.setTimeout(this.showProgressBar.bind(this),this.options.delay)}hideProgressBar(){this.cancelShowProgressBarTimeout(),this.progressBar.hide()}finishAnimationAndHideProgressBar(){this.cancelShowProgressBarTimeout(),this.hideProgressBarTimeout=window.setTimeout(this.hideProgressBar.bind(this),this.options.transition)}cancelShowProgressBarTimeout(){window.clearTimeout(this.showProgressBarTimeout),delete this.showProgressBarTimeout}cancelHideProgressBarTimeout(){window.clearTimeout(this.hideProgressBarTimeout),delete this.hideProgressBarTimeout}};var bs=()=>{window.dispatchEvent(new Event("exdoc:loaded"))},Zu=(e,{page:t})=>{//i.test(t.html)&&(e.abort(),window.location.reload())};window.addEventListener("DOMContentLoaded",bs);!C&&window.location.protocol!=="file:"&&new St({animationSelector:!1,containers:["#main"],ignoreVisit:e=>{let t=e.split("#")[0];return t===window.location.pathname||t===window.location.pathname+".html"},linkSelector:'a[href]:not([href^="/"]):not([href^="http"])[href$=".html"]',hooks:{"page:load":Zu,"page:view":bs},plugins:[new _t,new Lt({delay:500})]});})(); -/*! Bundled license information: - -lunr/lunr.js: - (** - * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 - * Copyright (C) 2020 Oliver Nightingale - * @license MIT - *) - (*! - * lunr.utils - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Set - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.tokenizer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Pipeline - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Vector - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.stemmer - * Copyright (C) 2020 Oliver Nightingale - * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt - *) - (*! - * lunr.stopWordFilter - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.trimmer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.TokenSet - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Index - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Builder - * Copyright (C) 2020 Oliver Nightingale - *) -*/ diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-elixir-NHQLCD6Y.css b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-elixir-NHQLCD6Y.css deleted file mode 100644 index d3a7bb7..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-elixir-NHQLCD6Y.css +++ /dev/null @@ -1,6 +0,0 @@ -:root{--main: hsl(250, 68%, 69%);--mainDark: hsl(250, 68%, 59%);--mainDarkest: hsl(250, 68%, 49%);--mainLight: hsl(250, 68%, 74%);--mainLightest: hsl(250, 68%, 79%);--searchBarFocusColor: #8E7CE6;--searchBarBorderColor: rgba(142, 124, 230, .25);--link-color: var(--mainDark);--link-visited-color: var(--mainDarkest)}body.dark{--link-color: var(--mainLightest);--link-visited-color: var(--mainLight)}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-ext-400-normal-N27NCBWW.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-400-normal-W7754I4D.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-ext-700-normal-Q2L5DVMW.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-700-normal-2XVSBPG4.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}body.dark{--background: var(--gray900);--contrast: var(--white);--textBody: var(--gray200);--textHeaders: var(--gray100);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--gray700);--iconAction: var(--coldGray-lightened-10);--iconActionHover: var(--white);--blockquoteBackground: var(--coldGray-opacity-10);--blockquoteBorder: var(--coldGrayDim);--tableHeadBorder: var(--gray600);--tableBodyBorder: var(--gray700);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray900);--admMixAmt: 40%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 40%, 14%) 100%);--warningHeading: hsl(var(--warningHue), 100%, 50%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 40%, 14%) 100%);--errorHeading: hsl(var(--errorHue), 100%, 52%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 60%, 15%) 100%);--infoHeading: hsl(var(--infoHue), 100%, 52%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 14%) 100%);--neutralHeading: hsl(var(--neutralHue), 50%, 80%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 14%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 48%);--fnSpecAttr: var(--gray400);--fnDeprecated: var(--yellowDark);--blink: var(--gray600);--codeBackground: var(--gray800);--codeBorder: var(--gray650);--codeScrollThumb: var(--gray500);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--black-opacity-30);--admCodeBorder: var(--white-opacity-10);--admCodeColor: var(--gray100);--tabBorder: var(--gray700);--tabBorderTop: var(--gray700);--tabShadow: var(--black);--bottomActionsBtnBorder: var(--white-opacity-10);--bottomActionsBtnTitle: var(--mainLightest);--modalBackground: var(--gray800);--settingsInput: var(--white);--settingsInputBackground: var(--gray700);--settingsInputBorder: var(--gray700);--settingsSectionBorder: var(--gray700);--quickSwitchInput: var(--gray300);--quickSwitchContour: var(--gray500);--success: var(--green-lightened-10);--progressBarColor: var(--gray300);--sidebarAccentMain: var(--gray50);--sidebarBackground: var(--gray800);--sidebarHeader: var(--gray700);--sidebarMuted: var(--gray300);--sidebarHover: var(--white);--sidebarStaleVersion: var(--orangeLight);--sidebarSubheadings: var(--gray400);--sidebarItem: var(--gray200);--sidebarInactiveItemBorder: var(--gray400);--sidebarInactiveItemMarker: var(--gray600);--sidebarLanguageAccentBar: var(--mainLight);--sidebarActiveItem: var(--mainLightest);--searchBarBorder: var(--gray500);--searchAccentMain: var(--gray300);--searchSearch: var(--gray900);--autocompleteBorder: rgba(28,42,60,.75);--autocompletePreview: var(--gray750);--autocompleteSelected: var(--gray750);--autocompleteHover: var(--gray700);--autocompleteBackground: var(--gray800);--suggestionBorder: var(--gray600);--autocompleteResults: var(--gray200);--autocompleteResultsBold: var(--gray100);--autocompleteLabelBack: var(--gray600);--autocompleteLabelFont: rgba(255, 255, 255, .8)}:root:has(body.dark){color-scheme:dark}*,:before,:after{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}@font-face{font-family:remixicon;src:url("./remixicon-ZWU5GQ4X.woff2") format("woff2");font-display:swap}[class^=ri-],[class*=" ri-"],.remix-icon{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--icon-arrow-up-s: "\ea78";--icon-arrow-down-s: "\ea4e";--icon-arrow-right-s: "\ea6e";--icon-add: "\ea13";--icon-subtract: "\f1af";--icon-error-warning: "\eca1";--icon-external-link-line: "\ecaf";--icon-information: "\ee59";--icon-alert: "\ea21";--icon-double-quotes-l: "\ec51";--icon-link-m: "\eeaf";--icon-close-line: "\eb99";--icon-code-s-slash-line: "\ebad";--icon-menu-line: "\ef3e";--icon-search-2-line: "\f0cd";--icon-settings-3-line: "\f0e6";--icon-printer-line: "\f029";--icon-markdown-line: "\ef1e"}.ri-lg{font-size:1.3333em;line-height:.75em;vertical-align:-.0667em}.ri-settings-3-line:before{content:var(--icon-settings-3-line)}.ri-add-line:before{content:var(--icon-add)}.ri-subtract-line:before{content:var(--icon-subtract)}.ri-arrow-up-s-line:before{content:var(--icon-arrow-up-s)}.ri-arrow-down-s-line:before{content:var(--icon-arrow-down-s)}.ri-arrow-right-s-line:before{content:var(--icon-arrow-right-s)}.ri-external-link-line:before{content:var(--icon-external-link-line)}.ri-search-2-line:before{content:var(--icon-search-2-line)}.ri-menu-line:before{content:var(--icon-menu-line)}.ri-close-line:before{content:var(--icon-close-line)}.ri-link-m:before{content:var(--icon-link-m)}.ri-code-s-slash-line:before{content:var(--icon-code-s-slash-line)}.ri-error-warning-line:before{content:var(--icon-error-warning)}.ri-information-line:before{content:var(--icon-information)}.ri-alert-line:before{content:var(--icon-alert)}.ri-double-quotes-l:before{content:var(--icon-double-quotes-l)}.ri-printer-line:before{content:var(--icon-printer-line)}.ri-markdown-line:before{content:var(--icon-markdown-line)}html,body{box-sizing:border-box;height:100%;width:100%}body{--sidebarWidth: 300px;--sidebarMinWidth: 300px;--sidebarTransitionDuration: .3s;background-color:var(--background);color:var(--textBody);font-size:var(--text-md);line-height:1.6875em;outline:none!important}*,*:before,*:after{box-sizing:inherit}.body-wrapper{display:flex;height:100%}.sidebar{display:none;flex-direction:column;width:var(--sidebarWidth);min-width:var(--sidebarMinWidth);max-width:50vw;height:100%;position:fixed;top:0;left:calc(-1 * var(--sidebarWidth));z-index:100;resize:horizontal}.sidebar-button{padding:20px 12px 18px 19px;position:fixed;z-index:200;top:0;left:0;will-change:transform;transform:translate(0)}.content{left:0;width:100%;height:100%;position:absolute}.content .content-inner{container:content / inline-size;max-width:var(--content-width);min-height:100%;margin:0 auto;padding:0 var(--content-gutter) 10px}.content-inner:focus{outline:none}.sidebar-transition .sidebar,.sidebar-transition .sidebar-button,.sidebar-transition .content{transition:all var(--sidebarTransitionDuration) ease-in-out allow-discrete}.sidebar-open .sidebar,.sidebar-transition .sidebar{display:flex}.sidebar-open .sidebar{left:0}.sidebar-open .sidebar-button{transform:translate(calc(var(--sidebarWidth) - 100%))}.sidebar-open .content{width:calc(100% - var(--sidebarWidth));left:var(--sidebarWidth)}@media screen and (max-width:768px){.sidebar-open .content{left:0;width:100%}.sidebar{max-width:90vw}body:not(.sidebar-open) .sidebar-button{position:absolute}}.swup-progress-bar{height:2px;background-color:var(--progressBarColor)}.sidebar{--sidebarFontSize: 16px;--sidebarLineHeight: 20px;font-family:var(--sansFontFamily);font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);background-color:var(--sidebarBackground);color:var(--sidebarAccentMain);overflow:hidden;.sidebar-tabpanel{scrollbar-width:thin}}.apple-os .sidebar{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar ul{list-style:none}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:var(--sidebarAccentMain);text-decoration:none;transition:var(--transition-colors)}.sidebar a:hover{color:var(--sidebarHover)}.sidebar .external-link{margin-left:2.5px}.sidebar .sidebar-header{background-color:var(--sidebarHeader);width:100%}.sidebar .sidebar-projectInfo{display:flex;justify-content:start;align-items:center;gap:8px;margin:8px 34px 12px 14px}.sidebar .sidebar-projectInfo>div{flex:1}.sidebar .sidebar-projectImage{align-self:flex-end}.sidebar .sidebar-projectImage img{display:block;max-width:48px;max-height:48px}.sidebar .sidebar-projectName{font-weight:700;font-size:var(--text-xl);line-height:24px;color:var(--sidebarAccentMain);margin:0;padding:0;word-wrap:break-word;display:block;width:calc(100% - 12px)}.sidebar .sidebar-projectVersion{display:block;position:relative;margin:0;padding:0;font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);color:var(--sidebarMuted);width:calc(100% - 12px)}.sidebar .sidebar-projectVersion form{display:flex}.sidebar .sidebar-projectVersion select{cursor:pointer;position:relative;margin:0;padding:0 0 0 10px;border:none;-webkit-appearance:none;appearance:none;background-color:transparent;color:var(--sidebarMuted);z-index:2}.sidebar .sidebar-projectVersion option{color:initial}.sidebar .sidebar-projectVersionsCaret{position:absolute;left:0;top:2px;z-index:1;font-size:8px;color:var(--sidebarMuted)}.sidebar .sidebar-projectVersion select::-ms-expand{display:none}.sidebar .sidebar-staleVersion{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--sidebarStaleVersion)}.sidebar .sidebar-staleVersion>a{color:var(--sidebarStaleVersion);font-weight:400}.sidebar .sidebar-staleIcon{font-size:var(--text-lg);line-height:0}.sidebar .sidebar-list-nav{display:flex;margin:0;padding:0;overflow:auto;scrollbar-width:thin}.sidebar .sidebar-list-nav :is(li,li button){text-transform:uppercase;letter-spacing:.02em;font-size:var(--text-sm);color:var(--sidebarSubheadings);white-space:nowrap}.sidebar .sidebar-list-nav li{display:inline-block;padding:0}.sidebar .sidebar-list-nav button{background:none;border:0;border-radius:0;-webkit-appearance:none;text-align:inherit;color:inherit;font-weight:inherit;cursor:pointer;display:inline-block;line-height:27px;padding:4px 14px;transition:var(--transition-all)}.sidebar .sidebar-list-nav button{border-bottom:var(--navTabBorderWidth) solid transparent}.sidebar .sidebar-list-nav button:not([aria-selected]):hover{border-bottom:var(--navTabBorderWidth) solid var(--sidebarInactiveItemBorder);color:var(--sidebarAccentMain);transition:var(--transition-all)}.sidebar .sidebar-list-nav button[aria-selected]{border-bottom:var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar);color:var(--sidebarAccentMain)}.sidebar .sidebar-tabpanel{flex:1 1 .01%;overflow-y:auto;overscroll-behavior:contain;position:relative;-webkit-overflow-scrolling:touch;padding-top:12px;scroll-padding-top:40px}.sidebar .full-list{margin:0;padding:0 0 20px;position:relative}.sidebar .full-list :is(li,a){display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar .full-list li{padding:0;line-height:27px}.sidebar .full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:1.5em 0 0;line-height:1.8em;color:var(--sidebarSubheadings);padding-left:15px}.sidebar .full-list li.nesting-context{font-weight:700;font-size:.9em;line-height:1.8em;color:var(--sidebarSubheadings);margin-top:10px;padding-left:15px}.sidebar .full-list a{margin-right:30px;padding:3px 0 3px 12px;border-left:var(--navTabBorderWidth) solid transparent;color:var(--sidebarItem)}.sidebar .full-list a[aria-selected]{color:var(--sidebarActiveItem)}.sidebar .full-list button{appearance:none;background-color:transparent;border:0;padding:0;cursor:pointer;color:inherit;width:20px;text-align:center;font-size:calc(1.2 * var(--sidebarFontSize));line-height:var(--sidebarLineHeight);position:absolute;display:block;right:10px;transform:translateY(-100%)}.sidebar .full-list a[aria-selected]+button{color:var(--sidebarActiveItem)}.sidebar .full-list button:after{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:var(--icon-arrow-down-s)}.sidebar .full-list button[aria-expanded=true]:after{content:var(--icon-arrow-up-s)}.sidebar .full-list ul{display:none;margin:10px 0 10px 10px;padding:0}.sidebar .full-list button[aria-expanded=true]+ul{display:block}.sidebar .full-list>li>a{height:27px;line-height:var(--sidebarLineHeight)}.sidebar .full-list>li>a:hover{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li>a[aria-selected]{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li:last-child{margin-bottom:30px}.sidebar .full-list>li.group:first-child{margin-top:0}.sidebar .full-list>li>ul>li:not(:has(li a[aria-selected=true]))>a[aria-selected=true]:before,.sidebar .full-list>li>ul>li>a:hover:before{content:"\2022";position:absolute;margin-left:-15px;color:var(--sidebarActiveItem)}.sidebar .full-list ul li{line-height:var(--sidebarFontSize);padding:0 8px}.sidebar .full-list ul a{padding-left:15px;height:24px}.sidebar .full-list ul button{font-size:var(--sidebarFontSize)}.sidebar .full-list ul button:after{content:var(--icon-add)}.sidebar .full-list ul button[aria-expanded=true]:after{content:var(--icon-subtract)}.sidebar .full-list ul ul{margin:9px 0 9px 10px}.sidebar .full-list ul ul li{height:20px;color:var(--sidebarAccentMain)}.sidebar .full-list ul ul a{border-left:1px solid var(--sidebarInactiveItemMarker);padding:0 10px;height:20px}.sidebar .full-list ul ul a:hover{border-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list ul ul a[aria-selected]{color:var(--sidebarActiveItem);border-color:var(--sidebarLanguageAccentBar)}.sidebar-button{cursor:pointer;background-color:transparent;border:none;font-size:var(--sidebarFontSize);color:var(--sidebarAccentMain)}.sidebar-button:hover{color:var(--sidebarHover)}body:not(.sidebar-open) .sidebar-button{color:var(--contrast)}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar .full-list{overflow:visible}}.top-search{background-color:var(--background);top:0;z-index:99;position:relative;width:100%;padding:10px 0}.search-settings{display:flex;column-gap:12px;align-items:center;width:100%;position:relative}.search-bar{border:1px solid var(--searchBarBorder);border-radius:var(--borderRadius-base);height:40px;position:relative;width:100%}.top-search .search-bar .search-input{background-color:var(--searchSearch);border:1px solid transparent;border-radius:var(--borderRadius-base);color:var(--searchAccentMain);position:relative;height:38px;padding:8px 40px 8px 12px;width:100%;transition:var(--transition-all)}.top-search .search-bar .search-input::placeholder{color:var(--searchAccentMain);opacity:.5}.top-search .search-bar .search-input:focus{border:1px solid var(--searchBarFocusColor);border-radius:calc(var(--borderRadius-base) - 1px);position:relative}.top-search .search-bar .search-label{position:relative}.top-search .search-bar .search-input-wrapper{position:relative;display:flex;align-items:center}.top-search .search-settings .engine-selector{position:relative}.top-search .search-settings .engine-selector[data-multiple=false]{display:none}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button{cursor:default}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button .ri-arrow-down-s-line{display:none}.top-search .search-settings .engine-button{display:flex;align-items:center;gap:6px;background-color:var(--autocompleteHover);border:none;color:var(--searchAccentMain);cursor:pointer;font-size:var(--text-sm);padding:8px 12px;border-radius:var(--borderRadius-base);transition:var(--transition-all);height:40px}.top-search .search-settings .engine-button:hover,.top-search .search-settings .engine-button[aria-expanded=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-name{font-size:var(--text-sm);white-space:nowrap}.top-search .search-settings .engine-dropdown{position:absolute;top:calc(100% + 10px);right:0;min-width:200px;max-width:50vh;width:max-content;background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;z-index:200}.top-search .search-settings .engine-dropdown[hidden]{display:none}.top-search .search-settings .engine-option{display:block;width:100%;text-align:left;background-color:transparent;border:0;color:var(--textHeaders);cursor:pointer;padding:10px 12px;transition:var(--transition-colors)}.top-search .search-settings .engine-option:hover{background-color:var(--autocompleteHover)}.top-search .search-settings .engine-option:focus{border-left:2px solid var(--main);padding-left:10px;background-color:var(--autocompleteHover);outline:none}.top-search .search-settings .engine-option[aria-checked=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-option .name{display:block;font-size:var(--text-sm);font-weight:500;margin-bottom:2px}.top-search .search-settings .engine-option .help{display:block;font-size:var(--text-xs);opacity:.7}.top-search .search-bar .search-close-button{font-size:var(--text-md);color:var(--searchAccentMain);background-color:transparent;border:none;cursor:pointer;margin:0;opacity:.5;padding:5px;transform:scaleY(0);transition:var(--transition-all);display:flex;align-items:center;position:absolute;right:0;top:50%;transform:translateY(-50%) scaleY(0)}.top-search .search-bar .search-close-button:hover{opacity:.7}.top-search .search-settings button.icon-settings{display:flex;align-items:center;justify-content:flex-end}.top-search .search-settings .icon-settings{font-size:var(--text-xl);float:right;color:var(--iconAction);text-decoration:none;border:none;transition:color .3s ease-in-out;background-color:transparent;cursor:pointer;padding:0}.top-search .search-settings .icon-settings:hover{color:var(--iconActionHover)}.top-search .search-settings .icon-settings:visited{color:var(--iconAction)}@media screen and (max-width:768px){.top-search{padding-left:calc(var(--content-gutter) + 36px);padding-right:var(--content-gutter);margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));width:calc(2 * var(--content-gutter) + 100%)}.search-settings{width:100%;box-sizing:border-box}.top-search .search-settings .engine-name{display:none}}body.search-focused .search-bar .search-close-button{transform:translateY(-50%) scaleY(1);transition:var(--transition-all)}@media screen and (hover:hover){body.search-focused .top-search{position:sticky!important}body.search-focused .sidebar-button{position:fixed!important}}@media screen and (hover:none){body.scroll-sticky .top-search{position:sticky!important}body.scroll-sticky .sidebar-button{position:fixed!important}}*:focus,button:focus,[type=button]:focus,[type=reset]:focus,[type=submit]:focus{outline:2px solid var(--main);outline-offset:-2px}*:focus:not(:focus-visible),button:focus:not(:focus-visible),[type=button]:focus:not(:focus-visible),[type=reset]:focus:not(:focus-visible),[type=submit]:focus:not(:focus-visible){outline:0}input[type=text],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=email],input[type=month],input[type=password],input[type=search],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{outline:0}.content-inner{font-size:1em;line-height:1.6875em;position:relative;background-color:var(--background);color:var(--textBody)}.content-inner .heading-with-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px}.content-inner .heading-with-actions>*:not(h1){flex-shrink:0}.content-inner .heading-with-actions h1{flex-grow:1;justify-self:flex-start;max-width:100%;margin:0;overflow-wrap:break-word}.content-inner .heading-with-actions .icon-action{width:20px;height:20px;display:flex;justify-content:center;align-items:center;font-weight:400;margin-left:3px}.content-inner .heading-with-actions.top-heading .icon-action{font-size:1.2rem}@container content (width > 600px){.content-inner .heading-with-actions.top-heading{flex-wrap:nowrap;align-items:flex-start;& h1{padding-right:32px}.icon-action{padding-top:1.7rem}}}.content-inner .top-heading{padding-top:1rem}.content-inner :is(h1,h2,h3,h4,h5,h6){font-family:var(--sansFontFamily);font-weight:700;line-height:1.5em;word-wrap:break-word;color:var(--textHeaders)}.content-inner div.group-description{margin:0 0 3em}.content-inner h1 small{font-weight:400}.content-inner{& h1{font-size:1.5rem;margin-top:1.75em}& h2{font-size:1.35rem;margin-top:1.5em;margin-bottom:.5em}& h3{font-size:1.25rem;margin-top:1.5em;margin-bottom:.5em}:is(h4,h5,h6){font-size:1.15rem;margin-top:1.25em;margin-bottom:.5em}:is(h5,h6){font-weight:400}& h6{font-size:1rem}}@container content (width > 600px){.content-inner{& h1{font-size:2rem}& h2{font-size:1.75rem}& h3{font-size:1.45rem}}}.content-inner li+li{margin-top:.25em}.content-inner :is(a,.a-main){color:var(--link-color);text-decoration:underline;text-decoration-skip-ink:auto}.content-inner :is(a:visited,.a-main:visited){color:var(--link-visited-color)}.content-inner .icon-action{color:var(--iconAction);text-decoration:none;border:none;transition:var(--transition-colors);background-color:transparent;cursor:pointer}.content-inner .icon-action:hover{color:var(--iconActionHover)}.content-inner .icon-action:visited{color:var(--iconAction)}.content-inner .livebook-badge-container{display:flex}.content-inner a.livebook-badge{display:inline-flex}.content-inner .note{color:var(--iconAction);font-size:var(--text-xs);font-weight:400}.content-inner{:is(blockquote,section.admonition){position:relative;margin:1.5625em 0;padding:0 1.2rem;overflow:auto;border-radius:var(--borderRadius-base);:is(h1,h2,h3,h4,h5,h6){margin:1.25rem 0}:is(h1,h2,h3){font-size:1.375rem}:is(h4,h5,h6){font-size:1rem}:is(code,pre code){background-color:var(--admCodeBackground);border:1px solid var(--admCodeBorder);color:var(--admCodeColor)}:is(a,a:visited){color:inherit}:is(h1,h2,h3,h4,h5,h6):first-child{padding-bottom:1rem;border-bottom:2px solid hsl(from currentColor h s l / 15%)}& p:last-child{padding-bottom:1em;margin-bottom:0}}& blockquote{border-left:3px solid var(--blockquoteBorder);background-color:var(--blockquoteBackground)}}@media print{.content-inner :is(blockquote,section.admonition){border:2px solid hsl(from var(--textBody) h s l / 15%);:is(code,pre code){border-color:var(--gray400)}& pre code.makeup{white-space:break-spaces;break-inside:avoid}}}.content-inner table{margin:2em 0;border-collapse:collapse;display:block;overflow:auto}.content-inner th{text-align:left;font-family:var(--sansFontFamily);font-weight:700;padding-bottom:.5em;white-space:nowrap}.content-inner thead tr{border-bottom:1px solid var(--tableHeadBorder)}.content-inner tbody tr{border-bottom:1px solid var(--tableBodyBorder)}.content-inner tbody tr:last-child{border-bottom:none}.content-inner tr{vertical-align:bottom;height:2.5em}.content-inner :is(td,th){padding:.25em .25em .25em 1em;line-height:2em;vertical-align:top}.content-inner .section-heading{--icon-size: 16px;--icon-spacing: 5px;display:grid;grid-template:1fr / 1fr}@media screen and (max-width:768px){.content-inner .section-heading{--icon-spacing: 2px}}.content-inner .section-heading>:is(.hover-link,.text){grid-row:1;grid-column:1}.content-inner .section-heading .hover-link{text-decoration:none}.content-inner .section-heading i{font-size:var(--icon-size);color:var(--mainLight);top:-2px;margin-left:calc(-1 * (var(--icon-size) + var(--icon-spacing)));padding-right:var(--icon-spacing);position:relative;opacity:0}.content-inner :is(blockquote,section.admonition) .section-heading i{display:none}.content-inner .section-heading:is(:hover,:focus,:target) i{opacity:1}.content-inner .app-vsn{display:none!important;font-size:.6em;line-height:1.5em}@media screen and (max-width:768px){.content-inner .app-vsn{display:block!important}}.content-inner img{max-width:100%}@media screen and (max-width:768px){.content-inner :is(ol,ul){padding-left:calc(1.5 * var(--content-gutter))}}.content-inner section.admonition{border-radius:var(--borderRadius-base)}.content-inner section.admonition>.admonition-title{color:var(--contrast);padding-left:2.2rem;font-weight:700;font-style:normal}.content-inner section.admonition>.admonition-title:before{color:var(--contrast);position:absolute;left:1rem;font-size:1.8rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.content-inner section.admonition{&.warning{background-color:var(--warningBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--warningHeading)}}}&.error{background-color:var(--errorBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--errorHeading)}}}&.info{background-color:var(--infoBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--infoHeading)}}}&.neutral{background-color:var(--neutralBackground);.admonition-title{&,&:before{content:var(--icon-double-quotes-l);color:var(--neutralHeading)}}}&.tip{background-color:var(--tipBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--tipHeading)}}}}@media print{.content-inner section.admonition{&.warning{border-color:hsl(from var(--warningHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--warningHue),90%,40%)}}}&.error{border-color:hsl(from var(--errorHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--errorHue),80%,40%)}}}&.info{border-color:hsl(from var(--infoHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--infoHue),80%,40%)}}}&.neutral{border-color:hsl(from var(--neutralHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--neutralHue),30%,40%)}}}&.tip{border-color:hsl(from var(--tipHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--tipHue),50%,40%)}}}}}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@media screen and (max-width:768px){.content-inner section.admonition{margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0}}.content-inner .summary h3 a{text-decoration:none;border:none;color:var(--textHeaders)!important}.content-inner .summary span.deprecated{color:var(--darkDeprecated);font-weight:400}.content-inner .summary .summary-row .summary-signature{font-family:var(--monoFontFamily);font-size:13px;font-weight:700}.content-inner .summary .summary-row .summary-signature a{text-decoration:none;border:none}.content-inner .summary .summary-row .summary-synopsis{padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.content-inner .footer{margin:4em auto 1em;text-align:center;font-size:var(--text-sm)}.content-inner .footer .line{display:inline-block}.content-inner .footer .line a,.content-inner .footer .line button{padding:0 4px}.content-inner .footer button{background-color:transparent;border:0;cursor:pointer}.content-inner .bottom-actions{display:flex;justify-content:space-between;margin-top:7em;gap:12px}.bottom-actions-item{flex:1 1 0%}.content-inner .bottom-actions .bottom-actions-button{display:flex;text-decoration:none;flex-direction:column;border-radius:var(--borderRadius-sm);border:1px solid var(--bottomActionsBtnBorder);padding:12px 16px;min-width:150px;transition:var(--transition-all)}.content-inner .bottom-actions .bottom-actions-button:hover{border-color:var(--mainLight)}.content-inner .bottom-actions .bottom-actions-button .subheader{font-size:.8em;color:var(--textHeaders);white-space:nowrap}.content-inner .bottom-actions .bottom-actions-button .title{color:var(--bottomActionsBtnTitle)}.content-inner .bottom-actions .bottom-actions-button[rel=prev]{text-align:start}.content-inner .bottom-actions .bottom-actions-button[rel=next]{text-align:end}@media screen and (max-width:768px){.content-inner .bottom-actions{flex-direction:column-reverse}}.page-cheatmd .content-inner{--horizontal-space: 1.5em;--vertical-space: 1em}@media(max-width:600px){.page-cheatmd .content-inner{--horizontal-space: 1em;--vertical-space: .75em}}.page-cheatmd .content-inner{max-width:1200px}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& h1{margin-bottom:var(--vertical-space)}& h2{font-size:1.6rem;margin:var(--vertical-space) 0;column-span:all;color:var(--gray700);font-weight:500}& h3{font-size:1.375rem;margin:0 0 1em;font-weight:400}& section.h3{min-width:300px;margin:0;padding:0 0 calc(var(--vertical-space) * 2) 0;break-inside:avoid}& h3 .text{overflow:hidden}& h3 .text:after{content:"";margin-left:calc(var(--horizontal-space) / 2);vertical-align:baseline;display:inline-block;width:100%;height:1px;margin-right:-100%;margin-bottom:5px;background-color:var(--codeBorder)}& h4{font-size:1rem;display:block;margin:0;padding:.25em var(--horizontal-space);font-weight:400;background:var(--gray100);color:#567;border:solid 1px 1px 0 1px var(--gray100)}.h2 p{margin:0;display:block;background:var(--gray50);padding:var(--vertical-space) var(--horizontal-space)}.h2 p>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}& pre code{padding:var(--vertical-space) var(--horizontal-space)}.h2 pre{margin:0}.h2 pre+pre{margin-top:-1px}& pre.wrap{white-space:break-spaces}@media screen and (max-width:768px){& pre code{border-left-width:1px!important;border-right-width:1px!important}}.h2 table{display:table;box-sizing:border-box;width:100%;border-collapse:collapse;margin:0}.h2 th{padding:var(--vertical-space) var(--horizontal-space);line-height:inherit;margin-bottom:-1px;vertical-align:middle;border-bottom:1px solid var(--codeBorder)}.h2 td{padding:var(--vertical-space) var(--horizontal-space);border:0;border-bottom:1px solid var(--codeBorder)}.h2 tr:first-child{border-top:1px solid var(--codeBorder)}.h2 td code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}.h2 thead{background-color:var(--gray50)}.h2 tbody{background-color:var(--codeBackground)}.h2 :is(ul,ol){margin:0;padding:0}.h2 li{list-style-position:inside;padding:.5em var(--horizontal-space);line-height:2em;vertical-align:middle;background-color:var(--codeBackground);border-bottom:1px solid var(--codeBorder);margin-top:0}.h2 :is(ul,ol)+pre code{border-top:0}.h2 li>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}}}@scope(.dark .page-cheatmd)to (.admonition){:scope .content-inner{& h2{color:var(--gray200)}& h4{background:#192f50;color:var(--textBody);border:1px solid #192f50;border-bottom:0}.h2 p{background:var(--gray700)}.h2 thead{background-color:var(--gray700)}}}.page-cheatmd .content-inner{& section.width-50{display:block;width:50%;margin:0;>section>table{width:100%}}& section:is(.col-2,.col-2-left,.col-3){column-gap:40px}& section.col-2{column-count:2;height:auto}& section.col-2-left{display:grid;grid-template-columns:calc(100% / 3) auto;>h2{grid-column-end:span 2}}& section.col-3{column-count:3;height:auto}& section.list-4>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 25%}}& section.list-6>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 calc(100% / 6)}}@media screen and (max-width:1400px){& section.col-3{column-count:2}& section.col-2-left{display:flex;flex-direction:column}}@media screen and (max-width:1200px){& section:is(.col-2,.col-3){display:flex;flex-direction:column}& section.list-6>ul>li{flex:0 0 25%}}@media screen and (max-width:1000px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 calc(100% / 3)}}@media screen and (max-width:600px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 50%}& section.width-50{width:100%}}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.page-search #search{min-height:200px;position:relative;.loading{height:64px;width:64px;position:absolute;top:50%;left:calc(50% - 32px)}.loading div{box-sizing:border-box;display:block;position:absolute;width:51px;height:51px;margin:6px;border:6px solid var(--coldGray);border-radius:50%;animation:loading 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:var(--coldGray) transparent transparent transparent}.loading div:nth-child(1){animation-delay:-.45s}.loading div:nth-child(2){animation-delay:-.3s}.loading div:nth-child(3){animation-delay:-.15s}.result{margin:2em 0}.result p{margin:0}.result-id{font-size:1.4em;margin:0}.result-id a{text-decoration:none;color:var(--textHeaders);transition:var(--transition-colors)}.result-id a:is(:visited,:active){color:var(--textHeaders)}.result-id a:is(:hover,:focus){color:var(--main)}:is(.result-id,.result-elem) em{font-style:normal;color:var(--main)}.result-id small{font-weight:400}}@keyframes keyboard-shortcuts-show{0%{opacity:0}to{opacity:1}}.modal{animation-duration:.15s;animation-name:keyboard-shortcuts-show;animation-iteration-count:1;animation-timing-function:ease-in-out;display:none;background-color:#000000bf;position:fixed;inset:0;z-index:300}.modal.shown{display:block}.modal .modal-contents{margin:75px auto 0;max-width:500px;background-color:var(--modalBackground);border-radius:var(--borderRadius-sm);box-shadow:2px 2px 8px #0003;padding:25px 35px 35px}@media screen and (max-width:768px){.modal .modal-contents{padding:20px}}.modal .modal-header{display:flex;align-items:start}.modal .modal-title{display:inline-block;flex-grow:1;font-size:1.2rem;font-weight:700;margin-bottom:20px}.modal .modal-title button{border:none;background-color:transparent;color:var(--textHeaders);font-weight:700;margin-right:30px;padding-left:0;text-align:left;transition:var(--transition-colors)}.modal .modal-title button:hover{color:var(--main);cursor:pointer}.modal .modal-title button.active{color:var(--main)}.modal .modal-close{cursor:pointer;display:block;font-size:1.5rem;margin:-8px -8px 0 0;padding:8px;opacity:.7;background-color:transparent;color:var(--textHeaders);border:none;transition:var(--transition-opacity)}.modal .modal-close:hover{opacity:1}#keyboard-shortcuts-content dl.shortcut-row{display:flex;align-items:center;justify-content:space-between;margin:0;padding:6px 0 8px;border-bottom:1px solid var(--settingsSectionBorder)}#keyboard-shortcuts-content dl.shortcut-row:last-of-type{border-bottom-style:none}#keyboard-shortcuts-content dl.shortcut-row:first-child{padding-top:0}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){display:inline-block}#keyboard-shortcuts-content kbd>kbd{background-color:var(--settingsInputBorder);color:var(--contrast);border-radius:var(--borderRadius-sm);font-family:inherit;font-weight:700;display:inline-block;line-height:1;padding:4px 7px 6px;min-width:26px;text-align:center;font-size:var(--text-sm)}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){margin:0}#quick-switch-modal-body{width:100%;position:relative}#quick-switch-modal-body .ri-search-2-line{position:absolute;left:0;top:0;padding:4px 10px;color:var(--quickSwitchContour);font-weight:700}#quick-switch-modal-body #quick-switch-input{width:100%;padding:8px 6px 8px 38px;border:none;color:var(--quickSwitchInput);background-color:transparent;border-bottom:1px solid var(--quickSwitchContour);box-sizing:border-box;transition:all .12s ease-out}#quick-switch-modal-body #quick-switch-results{margin:0}#quick-switch-modal-body .quick-switch-result{padding:2px 5px;border-bottom:1px dotted var(--quickSwitchContour);transition:all .12s ease-out}#quick-switch-modal-body .quick-switch-result:last-child{border-bottom:none}#quick-switch-modal-body .quick-switch-result:hover{cursor:pointer}#quick-switch-modal-body .quick-switch-result:is(:hover,.selected){border-left:4px solid var(--main);background-color:var(--codeBackground)}.autocomplete{display:none;position:absolute;width:calc(100% - 32px);top:55px}.autocomplete .triangle{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-bottom:12px solid var(--autocompleteBackground);position:absolute;top:8px;left:26px;transform:translate(-50%);z-index:100;background-color:transparent}.autocomplete-results{margin:0;padding:15px 20px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;color:var(--autocompleteResults);font-weight:300;font-size:.9rem;font-style:italic}.autocomplete-results .query{margin-right:auto}.autocomplete-results .bold{color:var(--autocompleteResultsBold);font-weight:400}.autocomplete.shown{display:block}.autocomplete-container{position:absolute;top:5px;width:100%;z-index:200}.autocomplete-suggestions{background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;overflow-y:auto;max-height:450px;white-space:normal;overflow-x:hidden;overscroll-behavior-y:contain;scrollbar-width:thin}.autocomplete-suggestion{color:var(--textHeaders)!important;display:block;padding:12px 20px;text-decoration:none!important;transition:var(--transition-colors);border-top:1px solid var(--suggestionBorder);font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.autocomplete-suggestion.selected{background-color:var(--autocompleteSelected)}.autocomplete-suggestion:hover{background-color:var(--autocompleteHover)}.autocomplete-suggestion em{font-style:normal;font-weight:700}.autocomplete-suggestion .separator{opacity:.4;margin:0 2px}.autocomplete-suggestion .description{opacity:.6}.autocomplete-suggestion .label{background-color:var(--autocompleteLabelBack);opacity:.6;color:var(--autocompleteLabelFont);padding:4px 8px;border-radius:4px;margin-left:5px;text-transform:uppercase;font-family:var(--sansFontFamily);font-size:.7rem}@media screen and (hover:none){.autocomplete-results .press-return{display:none!important}}.tooltip{box-shadow:0 0 10px var(--black-opacity-10);max-height:300px;max-width:500px;padding:0;position:absolute;pointer-events:none;margin:0;z-index:99;top:0;left:0;visibility:hidden;transform:translateY(20px);opacity:0;transition:.2s visibility ease-out,.2s transform ease-out,.2s opacity ease-out}.tooltip.tooltip-shown{visibility:visible;transform:translateY(0);opacity:1}.tooltip .tooltip-body{border:1px solid var(--codeBorder);border-radius:var(--borderRadius-sm);overflow:auto}.tooltip .tooltip-body .signature{min-width:320px;width:100%;line-height:1em!important;margin:.75em 0!important}.tooltip .tooltip-body .detail-header{border-left:0;margin-bottom:0;margin-top:0}.tooltip .tooltip-body .docstring{background-color:var(--background);padding:1.2em;margin:0;width:498px}.tooltip .tooltip-body .docstring-plain{max-width:498px;width:auto}.tooltip .tooltip-body .version-info{float:right;font-family:var(--monoFontFamily);font-weight:400;opacity:.3;padding-left:.3em}pre{position:relative}pre:hover .copy-button,pre .copy-button:focus{opacity:1}.copy-button{display:flex;opacity:0;position:absolute;top:7px;right:8px;padding:8px;background-color:transparent;backdrop-filter:blur(8px);border-radius:var(--borderRadius-sm);border:1px solid var(--codeBorder);cursor:pointer;transition:var(--transition-all);font-size:var(--text-sm);line-height:24px;color:currentColor;& svg[aria-live=polite]{display:none}}.copy-button svg{opacity:.5;transition:var(--transition-all)}pre .copy-button:hover svg,pre .copy-button:focus-visible svg{opacity:1}.copy-button svg{width:20px}.copy-button.clicked{opacity:1;color:var(--success);& svg[aria-live=polite]{display:block}}.copy-button.clicked svg{display:none;color:currentColor}#settings-modal-content{margin-top:10px}#settings-modal-content .hidden{display:none}#settings-modal-content .input{box-sizing:border-box;width:80%;padding:8px;font-size:var(--text-sm);background-color:var(--settingsInputBackground);color:var(--settingsInput);border:1px solid var(--settingsInputBorder);border-radius:var(--borderRadius-base);transition:var(--transition-all)}#settings-modal-content .input:focus{border-color:var(--main)}#settings-modal-content .input::placeholder{color:var(--gray400)}#settings-modal-content .switch-button-container{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--settingsSectionBorder);padding:10px 0}#settings-modal-content .switch-button-container:first-of-type{border-top-style:none;padding-top:0}#settings-modal-content .switch-button-container>div>span{font-size:var(--text-md)}#settings-modal-content .switch-button-container>div>p{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--text-sm);line-height:1.4;margin:0;padding-bottom:6px;padding-right:10px}#settings-modal-content .switch-button{position:relative;display:inline-block;flex-shrink:0;width:40px;height:20px;user-select:none;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox{appearance:none;position:absolute;display:block;width:20px;height:20px;border-radius:1000px;background-color:#91a4b7;border:3px solid #e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__bg{display:block;width:100%;height:100%;border-radius:1000px;background-color:#e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox:checked{background-color:#fff;border-color:var(--main);transform:translate(100%)}#settings-modal-content .switch-button__checkbox:checked+.switch-button__bg{background-color:var(--main)}#settings-modal-content .switch-button__checkbox:focus{outline:0}#settings-modal-content .switch-button__checkbox:focus+.switch-button__bg{outline:2px solid var(--main);outline-offset:2px}#settings-modal-content .switch-button__checkbox:focus:not(:focus-visible)+.switch-button__bg{outline:0}#settings-modal-content .settings-select{cursor:pointer;position:relative;border:none;background-color:transparent;color:var(--textBody)}#settings-modal-content .settings-select option{color:initial}#toast{visibility:hidden;opacity:0;position:fixed;z-index:1000;right:10px;top:0;font-size:.9rem;min-width:3rem;padding:.7rem 1.2rem .7rem 2.8rem;text-align:left;font-weight:400;border-radius:var(--borderRadius-base);border:1px solid hsl(from var(--tipHeading) h s l / 15%);background-color:var(--tipBackground);color:var(--tipHeading);transition:opacity .4s ease-in-out,transform .3s ease-out;cursor:default}#toast:before{content:var(--icon-information);position:absolute;left:1rem;font-size:1.2rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#toast.show{visibility:visible;opacity:1;transform:translateY(.75rem)}@media(prefers-reduced-motion:reduce){#toast{transition:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}@media print{.body-wrapper{display:block}.sidebar,.sidebar-button,.top-search{display:none}.content{padding-left:0;overflow:visible;left:0;width:100%}.summary-row{break-inside:avoid}#toast{display:none}.content-inner{padding:0}.content-inner .section-heading a.hover-link,.content-inner button.icon-action,.content-inner a.icon-action,.content-inner .bottom-actions{display:none}.footer p:first-of-type{display:none}.content-inner pre code.makeup{white-space:break-spaces;break-inside:avoid}}@media print{.page-cheatmd .content-inner{max-width:100%;width:100%;padding:0;font-size:.7em;& section:is(.col-2,.col-2-left,.col-3){column-gap:30px}& section.col-2{column-count:2}& section.col-2-left{display:grid}& section.col-3{column-count:3}}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& *{background-color:transparent!important;border-color:var(--gray400)!important}& h1{margin-top:0;margin-bottom:.5em}& h2.section-heading{font-weight:700;margin-top:1em;column-span:all}& section.h2{break-inside:avoid}& h3{font-weight:700;color:var(--mainDark)}& h3:after{height:2px;background-color:var(--gray400)}& section.h3{min-width:300px;break-inside:avoid}& h4{padding:.5em 0;border:none;font-weight:700;color:#000}.h2 p{padding-left:0;padding-right:0;border:none!important}& code{line-height:1.5em}.h2 table{font-variant-numeric:tabular-nums;break-inside:avoid}.h2 :is(th,td){vertical-align:top;padding-left:0;padding-right:0}.h2 thead{border-style:solid none;border-width:1px}.h2 tr{border-bottom:none}.h2 th{font-weight:700}.h2 li{padding-left:0;padding-right:0;vertical-align:middle;border-bottom:none}& pre:hover button.copy-button{display:none}& div.tooltip{display:none}& footer p:not(.built-using){display:none}}}}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}.tabset{--borderWidth: 1px;--tabsetPadding: var(--baseLineHeight);margin:var(--baseLineHeight) 0;border:var(--borderWidth) solid var(--tabBorder);padding:0 var(--tabsetPadding);border-radius:var(--borderRadius-lg)}.tabset-tablist{display:flex;overflow:auto;scrollbar-width:thin;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--tabBorderTop)}.tabset-tab{padding:1.1rem var(--tabsetPadding);font-family:var(--sansFontFamily);color:var(--textColor);margin-right:calc(-1 * var(--borderWidth));background-color:transparent;border:0;box-shadow:none;cursor:pointer;border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:transparent;transition:var(--transition-all)}:hover.tabset-tab{border-bottom-color:var(--tabBorderTop);color:var(--textHeaders)}.tabset-tab[aria-selected=true]{border-bottom-color:var(--mainLight);color:var(--textHeaders)}.tabset-tab[aria-selected=true]:focus-visible{background-color:var(--mainLight);border-color:var(--mainLight);color:var(--white)}@media screen and (max-width:768px){.tabset{--tabsetPadding: calc(var(--baseLineHeight) / 2)}.tabset-panel{padding-top:calc(var(--tabsetPadding) / 2);padding-bottom:calc(var(--tabsetPadding) / 2)}.tabset-panel pre,.tabset-panel blockquote,.tabset-panel section.admonition{margin-left:calc(-1 * var(--tabsetPadding))!important;margin-right:calc(-1 * var(--tabsetPadding))!important}.tabset-panel>pre code{border-left-width:0;border-right-width:0}}@media screen and (max-width:768px){.tabset-panel>:is(:first-child){&:is(table){margin:.5em 0}}}@media screen and (min-width:769px){.tabset-panel>:is(:first-child){&:is(blockquote,.admonition){margin-top:1.5em}&:is(p:has(img)){margin-top:1.25em}&:is(table){margin-top:.75em}}.tabset-panel>:is(:last-child){&:is(blockquote,.admonition){margin-bottom:1.5em}&:is(p:not(:has(img)),ul,ol){margin-bottom:1.25em}&:is(table){margin-bottom:.75em}}}body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"],body.dark .content-inner img[src*="#gh-light-mode-only"]{display:none} -/*! Bundled license information: - -modern-normalize/modern-normalize.css: - (*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize *) -*/ diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-erlang-KCHZLXSC.css b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-erlang-KCHZLXSC.css deleted file mode 100644 index 4dd7edb..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/html-erlang-KCHZLXSC.css +++ /dev/null @@ -1,6 +0,0 @@ -:root{--main: hsl(0, 100%, 44%);--mainDark: hsl(0, 100%, 34%);--mainDarkest: hsl(0, 100%, 24%);--mainLight: hsl(0, 100%, 64%);--mainLightest: hsl(0, 100%, 74%);--searchBarFocusColor: hsl(0, 100%, 50%);--searchBarBorderColor: rgb(255, 71, 71, .1);--link-color: hsl(212, 96%, 45%);--link-visited-color: hsl(212, 96%, 40%)}body.dark{--link-color: hsl(212, 56%, 72%);--link-visited-color: hsl(212, 56%, 67%)}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-ext-400-normal-N27NCBWW.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-400-normal-W7754I4D.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-ext-700-normal-Q2L5DVMW.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-700-normal-2XVSBPG4.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}body.dark{--background: var(--gray900);--contrast: var(--white);--textBody: var(--gray200);--textHeaders: var(--gray100);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--gray700);--iconAction: var(--coldGray-lightened-10);--iconActionHover: var(--white);--blockquoteBackground: var(--coldGray-opacity-10);--blockquoteBorder: var(--coldGrayDim);--tableHeadBorder: var(--gray600);--tableBodyBorder: var(--gray700);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray900);--admMixAmt: 40%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 40%, 14%) 100%);--warningHeading: hsl(var(--warningHue), 100%, 50%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 40%, 14%) 100%);--errorHeading: hsl(var(--errorHue), 100%, 52%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 60%, 15%) 100%);--infoHeading: hsl(var(--infoHue), 100%, 52%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 14%) 100%);--neutralHeading: hsl(var(--neutralHue), 50%, 80%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 14%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 48%);--fnSpecAttr: var(--gray400);--fnDeprecated: var(--yellowDark);--blink: var(--gray600);--codeBackground: var(--gray800);--codeBorder: var(--gray650);--codeScrollThumb: var(--gray500);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--black-opacity-30);--admCodeBorder: var(--white-opacity-10);--admCodeColor: var(--gray100);--tabBorder: var(--gray700);--tabBorderTop: var(--gray700);--tabShadow: var(--black);--bottomActionsBtnBorder: var(--white-opacity-10);--bottomActionsBtnTitle: var(--mainLightest);--modalBackground: var(--gray800);--settingsInput: var(--white);--settingsInputBackground: var(--gray700);--settingsInputBorder: var(--gray700);--settingsSectionBorder: var(--gray700);--quickSwitchInput: var(--gray300);--quickSwitchContour: var(--gray500);--success: var(--green-lightened-10);--progressBarColor: var(--gray300);--sidebarAccentMain: var(--gray50);--sidebarBackground: var(--gray800);--sidebarHeader: var(--gray700);--sidebarMuted: var(--gray300);--sidebarHover: var(--white);--sidebarStaleVersion: var(--orangeLight);--sidebarSubheadings: var(--gray400);--sidebarItem: var(--gray200);--sidebarInactiveItemBorder: var(--gray400);--sidebarInactiveItemMarker: var(--gray600);--sidebarLanguageAccentBar: var(--mainLight);--sidebarActiveItem: var(--mainLightest);--searchBarBorder: var(--gray500);--searchAccentMain: var(--gray300);--searchSearch: var(--gray900);--autocompleteBorder: rgba(28,42,60,.75);--autocompletePreview: var(--gray750);--autocompleteSelected: var(--gray750);--autocompleteHover: var(--gray700);--autocompleteBackground: var(--gray800);--suggestionBorder: var(--gray600);--autocompleteResults: var(--gray200);--autocompleteResultsBold: var(--gray100);--autocompleteLabelBack: var(--gray600);--autocompleteLabelFont: rgba(255, 255, 255, .8)}:root:has(body.dark){color-scheme:dark}*,:before,:after{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}@font-face{font-family:remixicon;src:url("./remixicon-ZWU5GQ4X.woff2") format("woff2");font-display:swap}[class^=ri-],[class*=" ri-"],.remix-icon{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--icon-arrow-up-s: "\ea78";--icon-arrow-down-s: "\ea4e";--icon-arrow-right-s: "\ea6e";--icon-add: "\ea13";--icon-subtract: "\f1af";--icon-error-warning: "\eca1";--icon-external-link-line: "\ecaf";--icon-information: "\ee59";--icon-alert: "\ea21";--icon-double-quotes-l: "\ec51";--icon-link-m: "\eeaf";--icon-close-line: "\eb99";--icon-code-s-slash-line: "\ebad";--icon-menu-line: "\ef3e";--icon-search-2-line: "\f0cd";--icon-settings-3-line: "\f0e6";--icon-printer-line: "\f029";--icon-markdown-line: "\ef1e"}.ri-lg{font-size:1.3333em;line-height:.75em;vertical-align:-.0667em}.ri-settings-3-line:before{content:var(--icon-settings-3-line)}.ri-add-line:before{content:var(--icon-add)}.ri-subtract-line:before{content:var(--icon-subtract)}.ri-arrow-up-s-line:before{content:var(--icon-arrow-up-s)}.ri-arrow-down-s-line:before{content:var(--icon-arrow-down-s)}.ri-arrow-right-s-line:before{content:var(--icon-arrow-right-s)}.ri-external-link-line:before{content:var(--icon-external-link-line)}.ri-search-2-line:before{content:var(--icon-search-2-line)}.ri-menu-line:before{content:var(--icon-menu-line)}.ri-close-line:before{content:var(--icon-close-line)}.ri-link-m:before{content:var(--icon-link-m)}.ri-code-s-slash-line:before{content:var(--icon-code-s-slash-line)}.ri-error-warning-line:before{content:var(--icon-error-warning)}.ri-information-line:before{content:var(--icon-information)}.ri-alert-line:before{content:var(--icon-alert)}.ri-double-quotes-l:before{content:var(--icon-double-quotes-l)}.ri-printer-line:before{content:var(--icon-printer-line)}.ri-markdown-line:before{content:var(--icon-markdown-line)}html,body{box-sizing:border-box;height:100%;width:100%}body{--sidebarWidth: 300px;--sidebarMinWidth: 300px;--sidebarTransitionDuration: .3s;background-color:var(--background);color:var(--textBody);font-size:var(--text-md);line-height:1.6875em;outline:none!important}*,*:before,*:after{box-sizing:inherit}.body-wrapper{display:flex;height:100%}.sidebar{display:none;flex-direction:column;width:var(--sidebarWidth);min-width:var(--sidebarMinWidth);max-width:50vw;height:100%;position:fixed;top:0;left:calc(-1 * var(--sidebarWidth));z-index:100;resize:horizontal}.sidebar-button{padding:20px 12px 18px 19px;position:fixed;z-index:200;top:0;left:0;will-change:transform;transform:translate(0)}.content{left:0;width:100%;height:100%;position:absolute}.content .content-inner{container:content / inline-size;max-width:var(--content-width);min-height:100%;margin:0 auto;padding:0 var(--content-gutter) 10px}.content-inner:focus{outline:none}.sidebar-transition .sidebar,.sidebar-transition .sidebar-button,.sidebar-transition .content{transition:all var(--sidebarTransitionDuration) ease-in-out allow-discrete}.sidebar-open .sidebar,.sidebar-transition .sidebar{display:flex}.sidebar-open .sidebar{left:0}.sidebar-open .sidebar-button{transform:translate(calc(var(--sidebarWidth) - 100%))}.sidebar-open .content{width:calc(100% - var(--sidebarWidth));left:var(--sidebarWidth)}@media screen and (max-width:768px){.sidebar-open .content{left:0;width:100%}.sidebar{max-width:90vw}body:not(.sidebar-open) .sidebar-button{position:absolute}}.swup-progress-bar{height:2px;background-color:var(--progressBarColor)}.sidebar{--sidebarFontSize: 16px;--sidebarLineHeight: 20px;font-family:var(--sansFontFamily);font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);background-color:var(--sidebarBackground);color:var(--sidebarAccentMain);overflow:hidden;.sidebar-tabpanel{scrollbar-width:thin}}.apple-os .sidebar{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar ul{list-style:none}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:var(--sidebarAccentMain);text-decoration:none;transition:var(--transition-colors)}.sidebar a:hover{color:var(--sidebarHover)}.sidebar .external-link{margin-left:2.5px}.sidebar .sidebar-header{background-color:var(--sidebarHeader);width:100%}.sidebar .sidebar-projectInfo{display:flex;justify-content:start;align-items:center;gap:8px;margin:8px 34px 12px 14px}.sidebar .sidebar-projectInfo>div{flex:1}.sidebar .sidebar-projectImage{align-self:flex-end}.sidebar .sidebar-projectImage img{display:block;max-width:48px;max-height:48px}.sidebar .sidebar-projectName{font-weight:700;font-size:var(--text-xl);line-height:24px;color:var(--sidebarAccentMain);margin:0;padding:0;word-wrap:break-word;display:block;width:calc(100% - 12px)}.sidebar .sidebar-projectVersion{display:block;position:relative;margin:0;padding:0;font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);color:var(--sidebarMuted);width:calc(100% - 12px)}.sidebar .sidebar-projectVersion form{display:flex}.sidebar .sidebar-projectVersion select{cursor:pointer;position:relative;margin:0;padding:0 0 0 10px;border:none;-webkit-appearance:none;appearance:none;background-color:transparent;color:var(--sidebarMuted);z-index:2}.sidebar .sidebar-projectVersion option{color:initial}.sidebar .sidebar-projectVersionsCaret{position:absolute;left:0;top:2px;z-index:1;font-size:8px;color:var(--sidebarMuted)}.sidebar .sidebar-projectVersion select::-ms-expand{display:none}.sidebar .sidebar-staleVersion{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--sidebarStaleVersion)}.sidebar .sidebar-staleVersion>a{color:var(--sidebarStaleVersion);font-weight:400}.sidebar .sidebar-staleIcon{font-size:var(--text-lg);line-height:0}.sidebar .sidebar-list-nav{display:flex;margin:0;padding:0;overflow:auto;scrollbar-width:thin}.sidebar .sidebar-list-nav :is(li,li button){text-transform:uppercase;letter-spacing:.02em;font-size:var(--text-sm);color:var(--sidebarSubheadings);white-space:nowrap}.sidebar .sidebar-list-nav li{display:inline-block;padding:0}.sidebar .sidebar-list-nav button{background:none;border:0;border-radius:0;-webkit-appearance:none;text-align:inherit;color:inherit;font-weight:inherit;cursor:pointer;display:inline-block;line-height:27px;padding:4px 14px;transition:var(--transition-all)}.sidebar .sidebar-list-nav button{border-bottom:var(--navTabBorderWidth) solid transparent}.sidebar .sidebar-list-nav button:not([aria-selected]):hover{border-bottom:var(--navTabBorderWidth) solid var(--sidebarInactiveItemBorder);color:var(--sidebarAccentMain);transition:var(--transition-all)}.sidebar .sidebar-list-nav button[aria-selected]{border-bottom:var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar);color:var(--sidebarAccentMain)}.sidebar .sidebar-tabpanel{flex:1 1 .01%;overflow-y:auto;overscroll-behavior:contain;position:relative;-webkit-overflow-scrolling:touch;padding-top:12px;scroll-padding-top:40px}.sidebar .full-list{margin:0;padding:0 0 20px;position:relative}.sidebar .full-list :is(li,a){display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar .full-list li{padding:0;line-height:27px}.sidebar .full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:1.5em 0 0;line-height:1.8em;color:var(--sidebarSubheadings);padding-left:15px}.sidebar .full-list li.nesting-context{font-weight:700;font-size:.9em;line-height:1.8em;color:var(--sidebarSubheadings);margin-top:10px;padding-left:15px}.sidebar .full-list a{margin-right:30px;padding:3px 0 3px 12px;border-left:var(--navTabBorderWidth) solid transparent;color:var(--sidebarItem)}.sidebar .full-list a[aria-selected]{color:var(--sidebarActiveItem)}.sidebar .full-list button{appearance:none;background-color:transparent;border:0;padding:0;cursor:pointer;color:inherit;width:20px;text-align:center;font-size:calc(1.2 * var(--sidebarFontSize));line-height:var(--sidebarLineHeight);position:absolute;display:block;right:10px;transform:translateY(-100%)}.sidebar .full-list a[aria-selected]+button{color:var(--sidebarActiveItem)}.sidebar .full-list button:after{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:var(--icon-arrow-down-s)}.sidebar .full-list button[aria-expanded=true]:after{content:var(--icon-arrow-up-s)}.sidebar .full-list ul{display:none;margin:10px 0 10px 10px;padding:0}.sidebar .full-list button[aria-expanded=true]+ul{display:block}.sidebar .full-list>li>a{height:27px;line-height:var(--sidebarLineHeight)}.sidebar .full-list>li>a:hover{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li>a[aria-selected]{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li:last-child{margin-bottom:30px}.sidebar .full-list>li.group:first-child{margin-top:0}.sidebar .full-list>li>ul>li:not(:has(li a[aria-selected=true]))>a[aria-selected=true]:before,.sidebar .full-list>li>ul>li>a:hover:before{content:"\2022";position:absolute;margin-left:-15px;color:var(--sidebarActiveItem)}.sidebar .full-list ul li{line-height:var(--sidebarFontSize);padding:0 8px}.sidebar .full-list ul a{padding-left:15px;height:24px}.sidebar .full-list ul button{font-size:var(--sidebarFontSize)}.sidebar .full-list ul button:after{content:var(--icon-add)}.sidebar .full-list ul button[aria-expanded=true]:after{content:var(--icon-subtract)}.sidebar .full-list ul ul{margin:9px 0 9px 10px}.sidebar .full-list ul ul li{height:20px;color:var(--sidebarAccentMain)}.sidebar .full-list ul ul a{border-left:1px solid var(--sidebarInactiveItemMarker);padding:0 10px;height:20px}.sidebar .full-list ul ul a:hover{border-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list ul ul a[aria-selected]{color:var(--sidebarActiveItem);border-color:var(--sidebarLanguageAccentBar)}.sidebar-button{cursor:pointer;background-color:transparent;border:none;font-size:var(--sidebarFontSize);color:var(--sidebarAccentMain)}.sidebar-button:hover{color:var(--sidebarHover)}body:not(.sidebar-open) .sidebar-button{color:var(--contrast)}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar .full-list{overflow:visible}}.top-search{background-color:var(--background);top:0;z-index:99;position:relative;width:100%;padding:10px 0}.search-settings{display:flex;column-gap:12px;align-items:center;width:100%;position:relative}.search-bar{border:1px solid var(--searchBarBorder);border-radius:var(--borderRadius-base);height:40px;position:relative;width:100%}.top-search .search-bar .search-input{background-color:var(--searchSearch);border:1px solid transparent;border-radius:var(--borderRadius-base);color:var(--searchAccentMain);position:relative;height:38px;padding:8px 40px 8px 12px;width:100%;transition:var(--transition-all)}.top-search .search-bar .search-input::placeholder{color:var(--searchAccentMain);opacity:.5}.top-search .search-bar .search-input:focus{border:1px solid var(--searchBarFocusColor);border-radius:calc(var(--borderRadius-base) - 1px);position:relative}.top-search .search-bar .search-label{position:relative}.top-search .search-bar .search-input-wrapper{position:relative;display:flex;align-items:center}.top-search .search-settings .engine-selector{position:relative}.top-search .search-settings .engine-selector[data-multiple=false]{display:none}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button{cursor:default}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button .ri-arrow-down-s-line{display:none}.top-search .search-settings .engine-button{display:flex;align-items:center;gap:6px;background-color:var(--autocompleteHover);border:none;color:var(--searchAccentMain);cursor:pointer;font-size:var(--text-sm);padding:8px 12px;border-radius:var(--borderRadius-base);transition:var(--transition-all);height:40px}.top-search .search-settings .engine-button:hover,.top-search .search-settings .engine-button[aria-expanded=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-name{font-size:var(--text-sm);white-space:nowrap}.top-search .search-settings .engine-dropdown{position:absolute;top:calc(100% + 10px);right:0;min-width:200px;max-width:50vh;width:max-content;background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;z-index:200}.top-search .search-settings .engine-dropdown[hidden]{display:none}.top-search .search-settings .engine-option{display:block;width:100%;text-align:left;background-color:transparent;border:0;color:var(--textHeaders);cursor:pointer;padding:10px 12px;transition:var(--transition-colors)}.top-search .search-settings .engine-option:hover{background-color:var(--autocompleteHover)}.top-search .search-settings .engine-option:focus{border-left:2px solid var(--main);padding-left:10px;background-color:var(--autocompleteHover);outline:none}.top-search .search-settings .engine-option[aria-checked=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-option .name{display:block;font-size:var(--text-sm);font-weight:500;margin-bottom:2px}.top-search .search-settings .engine-option .help{display:block;font-size:var(--text-xs);opacity:.7}.top-search .search-bar .search-close-button{font-size:var(--text-md);color:var(--searchAccentMain);background-color:transparent;border:none;cursor:pointer;margin:0;opacity:.5;padding:5px;transform:scaleY(0);transition:var(--transition-all);display:flex;align-items:center;position:absolute;right:0;top:50%;transform:translateY(-50%) scaleY(0)}.top-search .search-bar .search-close-button:hover{opacity:.7}.top-search .search-settings button.icon-settings{display:flex;align-items:center;justify-content:flex-end}.top-search .search-settings .icon-settings{font-size:var(--text-xl);float:right;color:var(--iconAction);text-decoration:none;border:none;transition:color .3s ease-in-out;background-color:transparent;cursor:pointer;padding:0}.top-search .search-settings .icon-settings:hover{color:var(--iconActionHover)}.top-search .search-settings .icon-settings:visited{color:var(--iconAction)}@media screen and (max-width:768px){.top-search{padding-left:calc(var(--content-gutter) + 36px);padding-right:var(--content-gutter);margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));width:calc(2 * var(--content-gutter) + 100%)}.search-settings{width:100%;box-sizing:border-box}.top-search .search-settings .engine-name{display:none}}body.search-focused .search-bar .search-close-button{transform:translateY(-50%) scaleY(1);transition:var(--transition-all)}@media screen and (hover:hover){body.search-focused .top-search{position:sticky!important}body.search-focused .sidebar-button{position:fixed!important}}@media screen and (hover:none){body.scroll-sticky .top-search{position:sticky!important}body.scroll-sticky .sidebar-button{position:fixed!important}}*:focus,button:focus,[type=button]:focus,[type=reset]:focus,[type=submit]:focus{outline:2px solid var(--main);outline-offset:-2px}*:focus:not(:focus-visible),button:focus:not(:focus-visible),[type=button]:focus:not(:focus-visible),[type=reset]:focus:not(:focus-visible),[type=submit]:focus:not(:focus-visible){outline:0}input[type=text],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=email],input[type=month],input[type=password],input[type=search],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{outline:0}.content-inner{font-size:1em;line-height:1.6875em;position:relative;background-color:var(--background);color:var(--textBody)}.content-inner .heading-with-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px}.content-inner .heading-with-actions>*:not(h1){flex-shrink:0}.content-inner .heading-with-actions h1{flex-grow:1;justify-self:flex-start;max-width:100%;margin:0;overflow-wrap:break-word}.content-inner .heading-with-actions .icon-action{width:20px;height:20px;display:flex;justify-content:center;align-items:center;font-weight:400;margin-left:3px}.content-inner .heading-with-actions.top-heading .icon-action{font-size:1.2rem}@container content (width > 600px){.content-inner .heading-with-actions.top-heading{flex-wrap:nowrap;align-items:flex-start;& h1{padding-right:32px}.icon-action{padding-top:1.7rem}}}.content-inner .top-heading{padding-top:1rem}.content-inner :is(h1,h2,h3,h4,h5,h6){font-family:var(--sansFontFamily);font-weight:700;line-height:1.5em;word-wrap:break-word;color:var(--textHeaders)}.content-inner div.group-description{margin:0 0 3em}.content-inner h1 small{font-weight:400}.content-inner{& h1{font-size:1.5rem;margin-top:1.75em}& h2{font-size:1.35rem;margin-top:1.5em;margin-bottom:.5em}& h3{font-size:1.25rem;margin-top:1.5em;margin-bottom:.5em}:is(h4,h5,h6){font-size:1.15rem;margin-top:1.25em;margin-bottom:.5em}:is(h5,h6){font-weight:400}& h6{font-size:1rem}}@container content (width > 600px){.content-inner{& h1{font-size:2rem}& h2{font-size:1.75rem}& h3{font-size:1.45rem}}}.content-inner li+li{margin-top:.25em}.content-inner :is(a,.a-main){color:var(--link-color);text-decoration:underline;text-decoration-skip-ink:auto}.content-inner :is(a:visited,.a-main:visited){color:var(--link-visited-color)}.content-inner .icon-action{color:var(--iconAction);text-decoration:none;border:none;transition:var(--transition-colors);background-color:transparent;cursor:pointer}.content-inner .icon-action:hover{color:var(--iconActionHover)}.content-inner .icon-action:visited{color:var(--iconAction)}.content-inner .livebook-badge-container{display:flex}.content-inner a.livebook-badge{display:inline-flex}.content-inner .note{color:var(--iconAction);font-size:var(--text-xs);font-weight:400}.content-inner{:is(blockquote,section.admonition){position:relative;margin:1.5625em 0;padding:0 1.2rem;overflow:auto;border-radius:var(--borderRadius-base);:is(h1,h2,h3,h4,h5,h6){margin:1.25rem 0}:is(h1,h2,h3){font-size:1.375rem}:is(h4,h5,h6){font-size:1rem}:is(code,pre code){background-color:var(--admCodeBackground);border:1px solid var(--admCodeBorder);color:var(--admCodeColor)}:is(a,a:visited){color:inherit}:is(h1,h2,h3,h4,h5,h6):first-child{padding-bottom:1rem;border-bottom:2px solid hsl(from currentColor h s l / 15%)}& p:last-child{padding-bottom:1em;margin-bottom:0}}& blockquote{border-left:3px solid var(--blockquoteBorder);background-color:var(--blockquoteBackground)}}@media print{.content-inner :is(blockquote,section.admonition){border:2px solid hsl(from var(--textBody) h s l / 15%);:is(code,pre code){border-color:var(--gray400)}& pre code.makeup{white-space:break-spaces;break-inside:avoid}}}.content-inner table{margin:2em 0;border-collapse:collapse;display:block;overflow:auto}.content-inner th{text-align:left;font-family:var(--sansFontFamily);font-weight:700;padding-bottom:.5em;white-space:nowrap}.content-inner thead tr{border-bottom:1px solid var(--tableHeadBorder)}.content-inner tbody tr{border-bottom:1px solid var(--tableBodyBorder)}.content-inner tbody tr:last-child{border-bottom:none}.content-inner tr{vertical-align:bottom;height:2.5em}.content-inner :is(td,th){padding:.25em .25em .25em 1em;line-height:2em;vertical-align:top}.content-inner .section-heading{--icon-size: 16px;--icon-spacing: 5px;display:grid;grid-template:1fr / 1fr}@media screen and (max-width:768px){.content-inner .section-heading{--icon-spacing: 2px}}.content-inner .section-heading>:is(.hover-link,.text){grid-row:1;grid-column:1}.content-inner .section-heading .hover-link{text-decoration:none}.content-inner .section-heading i{font-size:var(--icon-size);color:var(--mainLight);top:-2px;margin-left:calc(-1 * (var(--icon-size) + var(--icon-spacing)));padding-right:var(--icon-spacing);position:relative;opacity:0}.content-inner :is(blockquote,section.admonition) .section-heading i{display:none}.content-inner .section-heading:is(:hover,:focus,:target) i{opacity:1}.content-inner .app-vsn{display:none!important;font-size:.6em;line-height:1.5em}@media screen and (max-width:768px){.content-inner .app-vsn{display:block!important}}.content-inner img{max-width:100%}@media screen and (max-width:768px){.content-inner :is(ol,ul){padding-left:calc(1.5 * var(--content-gutter))}}.content-inner section.admonition{border-radius:var(--borderRadius-base)}.content-inner section.admonition>.admonition-title{color:var(--contrast);padding-left:2.2rem;font-weight:700;font-style:normal}.content-inner section.admonition>.admonition-title:before{color:var(--contrast);position:absolute;left:1rem;font-size:1.8rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.content-inner section.admonition{&.warning{background-color:var(--warningBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--warningHeading)}}}&.error{background-color:var(--errorBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--errorHeading)}}}&.info{background-color:var(--infoBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--infoHeading)}}}&.neutral{background-color:var(--neutralBackground);.admonition-title{&,&:before{content:var(--icon-double-quotes-l);color:var(--neutralHeading)}}}&.tip{background-color:var(--tipBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--tipHeading)}}}}@media print{.content-inner section.admonition{&.warning{border-color:hsl(from var(--warningHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--warningHue),90%,40%)}}}&.error{border-color:hsl(from var(--errorHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--errorHue),80%,40%)}}}&.info{border-color:hsl(from var(--infoHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--infoHue),80%,40%)}}}&.neutral{border-color:hsl(from var(--neutralHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--neutralHue),30%,40%)}}}&.tip{border-color:hsl(from var(--tipHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--tipHue),50%,40%)}}}}}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@media screen and (max-width:768px){.content-inner section.admonition{margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0}}.content-inner .summary h3 a{text-decoration:none;border:none;color:var(--textHeaders)!important}.content-inner .summary span.deprecated{color:var(--darkDeprecated);font-weight:400}.content-inner .summary .summary-row .summary-signature{font-family:var(--monoFontFamily);font-size:13px;font-weight:700}.content-inner .summary .summary-row .summary-signature a{text-decoration:none;border:none}.content-inner .summary .summary-row .summary-synopsis{padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.content-inner .footer{margin:4em auto 1em;text-align:center;font-size:var(--text-sm)}.content-inner .footer .line{display:inline-block}.content-inner .footer .line a,.content-inner .footer .line button{padding:0 4px}.content-inner .footer button{background-color:transparent;border:0;cursor:pointer}.content-inner .bottom-actions{display:flex;justify-content:space-between;margin-top:7em;gap:12px}.bottom-actions-item{flex:1 1 0%}.content-inner .bottom-actions .bottom-actions-button{display:flex;text-decoration:none;flex-direction:column;border-radius:var(--borderRadius-sm);border:1px solid var(--bottomActionsBtnBorder);padding:12px 16px;min-width:150px;transition:var(--transition-all)}.content-inner .bottom-actions .bottom-actions-button:hover{border-color:var(--mainLight)}.content-inner .bottom-actions .bottom-actions-button .subheader{font-size:.8em;color:var(--textHeaders);white-space:nowrap}.content-inner .bottom-actions .bottom-actions-button .title{color:var(--bottomActionsBtnTitle)}.content-inner .bottom-actions .bottom-actions-button[rel=prev]{text-align:start}.content-inner .bottom-actions .bottom-actions-button[rel=next]{text-align:end}@media screen and (max-width:768px){.content-inner .bottom-actions{flex-direction:column-reverse}}.page-cheatmd .content-inner{--horizontal-space: 1.5em;--vertical-space: 1em}@media(max-width:600px){.page-cheatmd .content-inner{--horizontal-space: 1em;--vertical-space: .75em}}.page-cheatmd .content-inner{max-width:1200px}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& h1{margin-bottom:var(--vertical-space)}& h2{font-size:1.6rem;margin:var(--vertical-space) 0;column-span:all;color:var(--gray700);font-weight:500}& h3{font-size:1.375rem;margin:0 0 1em;font-weight:400}& section.h3{min-width:300px;margin:0;padding:0 0 calc(var(--vertical-space) * 2) 0;break-inside:avoid}& h3 .text{overflow:hidden}& h3 .text:after{content:"";margin-left:calc(var(--horizontal-space) / 2);vertical-align:baseline;display:inline-block;width:100%;height:1px;margin-right:-100%;margin-bottom:5px;background-color:var(--codeBorder)}& h4{font-size:1rem;display:block;margin:0;padding:.25em var(--horizontal-space);font-weight:400;background:var(--gray100);color:#567;border:solid 1px 1px 0 1px var(--gray100)}.h2 p{margin:0;display:block;background:var(--gray50);padding:var(--vertical-space) var(--horizontal-space)}.h2 p>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}& pre code{padding:var(--vertical-space) var(--horizontal-space)}.h2 pre{margin:0}.h2 pre+pre{margin-top:-1px}& pre.wrap{white-space:break-spaces}@media screen and (max-width:768px){& pre code{border-left-width:1px!important;border-right-width:1px!important}}.h2 table{display:table;box-sizing:border-box;width:100%;border-collapse:collapse;margin:0}.h2 th{padding:var(--vertical-space) var(--horizontal-space);line-height:inherit;margin-bottom:-1px;vertical-align:middle;border-bottom:1px solid var(--codeBorder)}.h2 td{padding:var(--vertical-space) var(--horizontal-space);border:0;border-bottom:1px solid var(--codeBorder)}.h2 tr:first-child{border-top:1px solid var(--codeBorder)}.h2 td code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}.h2 thead{background-color:var(--gray50)}.h2 tbody{background-color:var(--codeBackground)}.h2 :is(ul,ol){margin:0;padding:0}.h2 li{list-style-position:inside;padding:.5em var(--horizontal-space);line-height:2em;vertical-align:middle;background-color:var(--codeBackground);border-bottom:1px solid var(--codeBorder);margin-top:0}.h2 :is(ul,ol)+pre code{border-top:0}.h2 li>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}}}@scope(.dark .page-cheatmd)to (.admonition){:scope .content-inner{& h2{color:var(--gray200)}& h4{background:#192f50;color:var(--textBody);border:1px solid #192f50;border-bottom:0}.h2 p{background:var(--gray700)}.h2 thead{background-color:var(--gray700)}}}.page-cheatmd .content-inner{& section.width-50{display:block;width:50%;margin:0;>section>table{width:100%}}& section:is(.col-2,.col-2-left,.col-3){column-gap:40px}& section.col-2{column-count:2;height:auto}& section.col-2-left{display:grid;grid-template-columns:calc(100% / 3) auto;>h2{grid-column-end:span 2}}& section.col-3{column-count:3;height:auto}& section.list-4>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 25%}}& section.list-6>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 calc(100% / 6)}}@media screen and (max-width:1400px){& section.col-3{column-count:2}& section.col-2-left{display:flex;flex-direction:column}}@media screen and (max-width:1200px){& section:is(.col-2,.col-3){display:flex;flex-direction:column}& section.list-6>ul>li{flex:0 0 25%}}@media screen and (max-width:1000px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 calc(100% / 3)}}@media screen and (max-width:600px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 50%}& section.width-50{width:100%}}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.page-search #search{min-height:200px;position:relative;.loading{height:64px;width:64px;position:absolute;top:50%;left:calc(50% - 32px)}.loading div{box-sizing:border-box;display:block;position:absolute;width:51px;height:51px;margin:6px;border:6px solid var(--coldGray);border-radius:50%;animation:loading 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:var(--coldGray) transparent transparent transparent}.loading div:nth-child(1){animation-delay:-.45s}.loading div:nth-child(2){animation-delay:-.3s}.loading div:nth-child(3){animation-delay:-.15s}.result{margin:2em 0}.result p{margin:0}.result-id{font-size:1.4em;margin:0}.result-id a{text-decoration:none;color:var(--textHeaders);transition:var(--transition-colors)}.result-id a:is(:visited,:active){color:var(--textHeaders)}.result-id a:is(:hover,:focus){color:var(--main)}:is(.result-id,.result-elem) em{font-style:normal;color:var(--main)}.result-id small{font-weight:400}}@keyframes keyboard-shortcuts-show{0%{opacity:0}to{opacity:1}}.modal{animation-duration:.15s;animation-name:keyboard-shortcuts-show;animation-iteration-count:1;animation-timing-function:ease-in-out;display:none;background-color:#000000bf;position:fixed;inset:0;z-index:300}.modal.shown{display:block}.modal .modal-contents{margin:75px auto 0;max-width:500px;background-color:var(--modalBackground);border-radius:var(--borderRadius-sm);box-shadow:2px 2px 8px #0003;padding:25px 35px 35px}@media screen and (max-width:768px){.modal .modal-contents{padding:20px}}.modal .modal-header{display:flex;align-items:start}.modal .modal-title{display:inline-block;flex-grow:1;font-size:1.2rem;font-weight:700;margin-bottom:20px}.modal .modal-title button{border:none;background-color:transparent;color:var(--textHeaders);font-weight:700;margin-right:30px;padding-left:0;text-align:left;transition:var(--transition-colors)}.modal .modal-title button:hover{color:var(--main);cursor:pointer}.modal .modal-title button.active{color:var(--main)}.modal .modal-close{cursor:pointer;display:block;font-size:1.5rem;margin:-8px -8px 0 0;padding:8px;opacity:.7;background-color:transparent;color:var(--textHeaders);border:none;transition:var(--transition-opacity)}.modal .modal-close:hover{opacity:1}#keyboard-shortcuts-content dl.shortcut-row{display:flex;align-items:center;justify-content:space-between;margin:0;padding:6px 0 8px;border-bottom:1px solid var(--settingsSectionBorder)}#keyboard-shortcuts-content dl.shortcut-row:last-of-type{border-bottom-style:none}#keyboard-shortcuts-content dl.shortcut-row:first-child{padding-top:0}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){display:inline-block}#keyboard-shortcuts-content kbd>kbd{background-color:var(--settingsInputBorder);color:var(--contrast);border-radius:var(--borderRadius-sm);font-family:inherit;font-weight:700;display:inline-block;line-height:1;padding:4px 7px 6px;min-width:26px;text-align:center;font-size:var(--text-sm)}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){margin:0}#quick-switch-modal-body{width:100%;position:relative}#quick-switch-modal-body .ri-search-2-line{position:absolute;left:0;top:0;padding:4px 10px;color:var(--quickSwitchContour);font-weight:700}#quick-switch-modal-body #quick-switch-input{width:100%;padding:8px 6px 8px 38px;border:none;color:var(--quickSwitchInput);background-color:transparent;border-bottom:1px solid var(--quickSwitchContour);box-sizing:border-box;transition:all .12s ease-out}#quick-switch-modal-body #quick-switch-results{margin:0}#quick-switch-modal-body .quick-switch-result{padding:2px 5px;border-bottom:1px dotted var(--quickSwitchContour);transition:all .12s ease-out}#quick-switch-modal-body .quick-switch-result:last-child{border-bottom:none}#quick-switch-modal-body .quick-switch-result:hover{cursor:pointer}#quick-switch-modal-body .quick-switch-result:is(:hover,.selected){border-left:4px solid var(--main);background-color:var(--codeBackground)}.autocomplete{display:none;position:absolute;width:calc(100% - 32px);top:55px}.autocomplete .triangle{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-bottom:12px solid var(--autocompleteBackground);position:absolute;top:8px;left:26px;transform:translate(-50%);z-index:100;background-color:transparent}.autocomplete-results{margin:0;padding:15px 20px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;color:var(--autocompleteResults);font-weight:300;font-size:.9rem;font-style:italic}.autocomplete-results .query{margin-right:auto}.autocomplete-results .bold{color:var(--autocompleteResultsBold);font-weight:400}.autocomplete.shown{display:block}.autocomplete-container{position:absolute;top:5px;width:100%;z-index:200}.autocomplete-suggestions{background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;overflow-y:auto;max-height:450px;white-space:normal;overflow-x:hidden;overscroll-behavior-y:contain;scrollbar-width:thin}.autocomplete-suggestion{color:var(--textHeaders)!important;display:block;padding:12px 20px;text-decoration:none!important;transition:var(--transition-colors);border-top:1px solid var(--suggestionBorder);font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.autocomplete-suggestion.selected{background-color:var(--autocompleteSelected)}.autocomplete-suggestion:hover{background-color:var(--autocompleteHover)}.autocomplete-suggestion em{font-style:normal;font-weight:700}.autocomplete-suggestion .separator{opacity:.4;margin:0 2px}.autocomplete-suggestion .description{opacity:.6}.autocomplete-suggestion .label{background-color:var(--autocompleteLabelBack);opacity:.6;color:var(--autocompleteLabelFont);padding:4px 8px;border-radius:4px;margin-left:5px;text-transform:uppercase;font-family:var(--sansFontFamily);font-size:.7rem}@media screen and (hover:none){.autocomplete-results .press-return{display:none!important}}.tooltip{box-shadow:0 0 10px var(--black-opacity-10);max-height:300px;max-width:500px;padding:0;position:absolute;pointer-events:none;margin:0;z-index:99;top:0;left:0;visibility:hidden;transform:translateY(20px);opacity:0;transition:.2s visibility ease-out,.2s transform ease-out,.2s opacity ease-out}.tooltip.tooltip-shown{visibility:visible;transform:translateY(0);opacity:1}.tooltip .tooltip-body{border:1px solid var(--codeBorder);border-radius:var(--borderRadius-sm);overflow:auto}.tooltip .tooltip-body .signature{min-width:320px;width:100%;line-height:1em!important;margin:.75em 0!important}.tooltip .tooltip-body .detail-header{border-left:0;margin-bottom:0;margin-top:0}.tooltip .tooltip-body .docstring{background-color:var(--background);padding:1.2em;margin:0;width:498px}.tooltip .tooltip-body .docstring-plain{max-width:498px;width:auto}.tooltip .tooltip-body .version-info{float:right;font-family:var(--monoFontFamily);font-weight:400;opacity:.3;padding-left:.3em}pre{position:relative}pre:hover .copy-button,pre .copy-button:focus{opacity:1}.copy-button{display:flex;opacity:0;position:absolute;top:7px;right:8px;padding:8px;background-color:transparent;backdrop-filter:blur(8px);border-radius:var(--borderRadius-sm);border:1px solid var(--codeBorder);cursor:pointer;transition:var(--transition-all);font-size:var(--text-sm);line-height:24px;color:currentColor;& svg[aria-live=polite]{display:none}}.copy-button svg{opacity:.5;transition:var(--transition-all)}pre .copy-button:hover svg,pre .copy-button:focus-visible svg{opacity:1}.copy-button svg{width:20px}.copy-button.clicked{opacity:1;color:var(--success);& svg[aria-live=polite]{display:block}}.copy-button.clicked svg{display:none;color:currentColor}#settings-modal-content{margin-top:10px}#settings-modal-content .hidden{display:none}#settings-modal-content .input{box-sizing:border-box;width:80%;padding:8px;font-size:var(--text-sm);background-color:var(--settingsInputBackground);color:var(--settingsInput);border:1px solid var(--settingsInputBorder);border-radius:var(--borderRadius-base);transition:var(--transition-all)}#settings-modal-content .input:focus{border-color:var(--main)}#settings-modal-content .input::placeholder{color:var(--gray400)}#settings-modal-content .switch-button-container{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--settingsSectionBorder);padding:10px 0}#settings-modal-content .switch-button-container:first-of-type{border-top-style:none;padding-top:0}#settings-modal-content .switch-button-container>div>span{font-size:var(--text-md)}#settings-modal-content .switch-button-container>div>p{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--text-sm);line-height:1.4;margin:0;padding-bottom:6px;padding-right:10px}#settings-modal-content .switch-button{position:relative;display:inline-block;flex-shrink:0;width:40px;height:20px;user-select:none;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox{appearance:none;position:absolute;display:block;width:20px;height:20px;border-radius:1000px;background-color:#91a4b7;border:3px solid #e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__bg{display:block;width:100%;height:100%;border-radius:1000px;background-color:#e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox:checked{background-color:#fff;border-color:var(--main);transform:translate(100%)}#settings-modal-content .switch-button__checkbox:checked+.switch-button__bg{background-color:var(--main)}#settings-modal-content .switch-button__checkbox:focus{outline:0}#settings-modal-content .switch-button__checkbox:focus+.switch-button__bg{outline:2px solid var(--main);outline-offset:2px}#settings-modal-content .switch-button__checkbox:focus:not(:focus-visible)+.switch-button__bg{outline:0}#settings-modal-content .settings-select{cursor:pointer;position:relative;border:none;background-color:transparent;color:var(--textBody)}#settings-modal-content .settings-select option{color:initial}#toast{visibility:hidden;opacity:0;position:fixed;z-index:1000;right:10px;top:0;font-size:.9rem;min-width:3rem;padding:.7rem 1.2rem .7rem 2.8rem;text-align:left;font-weight:400;border-radius:var(--borderRadius-base);border:1px solid hsl(from var(--tipHeading) h s l / 15%);background-color:var(--tipBackground);color:var(--tipHeading);transition:opacity .4s ease-in-out,transform .3s ease-out;cursor:default}#toast:before{content:var(--icon-information);position:absolute;left:1rem;font-size:1.2rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#toast.show{visibility:visible;opacity:1;transform:translateY(.75rem)}@media(prefers-reduced-motion:reduce){#toast{transition:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}@media print{.body-wrapper{display:block}.sidebar,.sidebar-button,.top-search{display:none}.content{padding-left:0;overflow:visible;left:0;width:100%}.summary-row{break-inside:avoid}#toast{display:none}.content-inner{padding:0}.content-inner .section-heading a.hover-link,.content-inner button.icon-action,.content-inner a.icon-action,.content-inner .bottom-actions{display:none}.footer p:first-of-type{display:none}.content-inner pre code.makeup{white-space:break-spaces;break-inside:avoid}}@media print{.page-cheatmd .content-inner{max-width:100%;width:100%;padding:0;font-size:.7em;& section:is(.col-2,.col-2-left,.col-3){column-gap:30px}& section.col-2{column-count:2}& section.col-2-left{display:grid}& section.col-3{column-count:3}}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& *{background-color:transparent!important;border-color:var(--gray400)!important}& h1{margin-top:0;margin-bottom:.5em}& h2.section-heading{font-weight:700;margin-top:1em;column-span:all}& section.h2{break-inside:avoid}& h3{font-weight:700;color:var(--mainDark)}& h3:after{height:2px;background-color:var(--gray400)}& section.h3{min-width:300px;break-inside:avoid}& h4{padding:.5em 0;border:none;font-weight:700;color:#000}.h2 p{padding-left:0;padding-right:0;border:none!important}& code{line-height:1.5em}.h2 table{font-variant-numeric:tabular-nums;break-inside:avoid}.h2 :is(th,td){vertical-align:top;padding-left:0;padding-right:0}.h2 thead{border-style:solid none;border-width:1px}.h2 tr{border-bottom:none}.h2 th{font-weight:700}.h2 li{padding-left:0;padding-right:0;vertical-align:middle;border-bottom:none}& pre:hover button.copy-button{display:none}& div.tooltip{display:none}& footer p:not(.built-using){display:none}}}}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}.tabset{--borderWidth: 1px;--tabsetPadding: var(--baseLineHeight);margin:var(--baseLineHeight) 0;border:var(--borderWidth) solid var(--tabBorder);padding:0 var(--tabsetPadding);border-radius:var(--borderRadius-lg)}.tabset-tablist{display:flex;overflow:auto;scrollbar-width:thin;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--tabBorderTop)}.tabset-tab{padding:1.1rem var(--tabsetPadding);font-family:var(--sansFontFamily);color:var(--textColor);margin-right:calc(-1 * var(--borderWidth));background-color:transparent;border:0;box-shadow:none;cursor:pointer;border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:transparent;transition:var(--transition-all)}:hover.tabset-tab{border-bottom-color:var(--tabBorderTop);color:var(--textHeaders)}.tabset-tab[aria-selected=true]{border-bottom-color:var(--mainLight);color:var(--textHeaders)}.tabset-tab[aria-selected=true]:focus-visible{background-color:var(--mainLight);border-color:var(--mainLight);color:var(--white)}@media screen and (max-width:768px){.tabset{--tabsetPadding: calc(var(--baseLineHeight) / 2)}.tabset-panel{padding-top:calc(var(--tabsetPadding) / 2);padding-bottom:calc(var(--tabsetPadding) / 2)}.tabset-panel pre,.tabset-panel blockquote,.tabset-panel section.admonition{margin-left:calc(-1 * var(--tabsetPadding))!important;margin-right:calc(-1 * var(--tabsetPadding))!important}.tabset-panel>pre code{border-left-width:0;border-right-width:0}}@media screen and (max-width:768px){.tabset-panel>:is(:first-child){&:is(table){margin:.5em 0}}}@media screen and (min-width:769px){.tabset-panel>:is(:first-child){&:is(blockquote,.admonition){margin-top:1.5em}&:is(p:has(img)){margin-top:1.25em}&:is(table){margin-top:.75em}}.tabset-panel>:is(:last-child){&:is(blockquote,.admonition){margin-bottom:1.5em}&:is(p:not(:has(img)),ul,ol){margin-bottom:1.25em}&:is(table){margin-bottom:.75em}}}body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"],body.dark .content-inner img[src*="#gh-light-mode-only"]{display:none} -/*! Bundled license information: - -modern-normalize/modern-normalize.css: - (*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize *) -*/ diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/inline_html-4XT25SPW.js b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/inline_html-4XT25SPW.js deleted file mode 100644 index 048b201..0000000 --- a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/inline_html-4XT25SPW.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{var t="ex_doc:settings",e="dark";var o="dark",s="light";var E="sidebar_state",n="closed";var r="sidebar_width";var a="sidebar-open";var i=new URLSearchParams(window.location.search),S=i.get("theme")||JSON.parse(localStorage.getItem(t)||"{}").theme;(S===o||S!==s&&window.matchMedia("(prefers-color-scheme: dark)").matches)&&document.body.classList.add(e);var d=sessionStorage.getItem(E),A=d!==n&&!window.matchMedia(`screen and (max-width: ${768}px)`).matches;document.body.classList.toggle(a,A);var c=sessionStorage.getItem(r);c&&document.body.style.setProperty("--sidebarWidth",`${c}px`);var p=/(Macintosh|iPhone|iPad|iPod)/.test(window.navigator.userAgent);document.documentElement.classList.toggle("apple-os",p);})(); diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-400-normal-MNITWADU.woff b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-400-normal-MNITWADU.woff deleted file mode 100644 index eac7dd1ca48064ec6ee9cbd72ab121c70d765d7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34020 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 diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-700-normal-XMT5XFBS.woff b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-all-700-normal-XMT5XFBS.woff deleted file mode 100644 index ac9382a811495707732baba8feb48e87b9768169..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33296 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 QOFh<%JiLFgmWQ+r07g396aWAK diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-700-normal-2XVSBPG4.woff2 b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-700-normal-2XVSBPG4.woff2 deleted file mode 100644 index 11de83feaf0fb872c91fce89534fd4e2d8bbda28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23040 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 diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 deleted file mode 100644 index edb9fa6f492362a9ab1f1d674b8f6e6f4e2085d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5472 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 WhF)K4IGNT+hd8;|K8&%*Sz!P*WW1>W diff --git a/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 b/plugins/mob_iap/deps/ex_doc/formatters/html/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 deleted file mode 100644 index 2c8aaa866eee8bee09ee05a9809c2fda7cd87bd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5368 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 diff --git a/plugins/mob_iap/deps/ex_doc/hex_metadata.config b/plugins/mob_iap/deps/ex_doc/hex_metadata.config deleted file mode 100644 index 9dd57e3..0000000 --- a/plugins/mob_iap/deps/ex_doc/hex_metadata.config +++ /dev/null @@ -1,109 +0,0 @@ -{<<"links">>, - [{<<"Changelog">>,<<"https://hexdocs.pm/ex_doc/changelog.html">>}, - {<<"GitHub">>,<<"https://github.com/elixir-lang/ex_doc">>}, - {<<"Writing documentation">>, - <<"https://hexdocs.pm/elixir/writing-documentation.html">>}]}. -{<<"name">>,<<"ex_doc">>}. -{<<"version">>,<<"0.40.3">>}. -{<<"description">>,<<"ExDoc is a documentation generation tool for Elixir">>}. -{<<"elixir">>,<<"~> 1.15">>}. -{<<"files">>, - [<<"CHANGELOG.md">>,<<"Cheatsheet.cheatmd">>,<<"formatters">>, - <<"formatters/html">>,<<"formatters/html/dist">>, - <<"formatters/html/dist/html-Y2MUTVIN.js">>, - <<"formatters/html/dist/lato-latin-700-normal-2XVSBPG4.woff2">>, - <<"formatters/html/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2">>, - <<"formatters/html/dist/lato-all-700-normal-XMT5XFBS.woff">>, - <<"formatters/html/dist/lato-all-400-normal-MNITWADU.woff">>, - <<"formatters/html/dist/remixicon-ZWU5GQ4X.woff2">>, - <<"formatters/html/dist/lato-latin-ext-400-normal-N27NCBWW.woff2">>, - <<"formatters/html/dist/lato-latin-400-normal-W7754I4D.woff2">>, - <<"formatters/html/dist/inline_html-4XT25SPW.js">>, - <<"formatters/html/dist/html-erlang-KCHZLXSC.css">>, - <<"formatters/html/dist/html-elixir-NHQLCD6Y.css">>,<<"formatters/epub">>, - <<"formatters/epub/dist">>, - <<"formatters/epub/dist/epub-erlang-IMUVAO3R.css">>, - <<"formatters/epub/dist/epub-elixir-A3GHWQ7R.css">>, - <<"formatters/epub/dist/epub-4WIP524F.js">>,<<"formatters/epub/metainfo">>, - <<"formatters/epub/metainfo/container.xml">>, - <<"formatters/epub/metainfo/com.apple.ibooks.display-options.xml">>, - <<"lib">>,<<"lib/ex_doc.ex">>,<<"lib/ex_doc">>,<<"lib/ex_doc/autolink.ex">>, - <<"lib/ex_doc/retriever.ex">>,<<"lib/ex_doc/extras.ex">>, - <<"lib/ex_doc/markdown">>,<<"lib/ex_doc/markdown/earmark.ex">>, - <<"lib/ex_doc/formatter.ex">>,<<"lib/ex_doc/config.ex">>, - <<"lib/ex_doc/language">>,<<"lib/ex_doc/language/erlang.ex">>, - <<"lib/ex_doc/language/source.ex">>,<<"lib/ex_doc/language/elixir.ex">>, - <<"lib/ex_doc/formatter">>,<<"lib/ex_doc/formatter/html.ex">>, - <<"lib/ex_doc/formatter/markdown">>, - <<"lib/ex_doc/formatter/markdown/templates">>, - <<"lib/ex_doc/formatter/markdown/templates/module_template.eex">>, - <<"lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex">>, - <<"lib/ex_doc/formatter/markdown/templates/api_reference_template.eex">>, - <<"lib/ex_doc/formatter/markdown/templates/detail_template.eex">>, - <<"lib/ex_doc/formatter/markdown/templates/nav_group_template.eex">>, - <<"lib/ex_doc/formatter/markdown/templates.ex">>, - <<"lib/ex_doc/formatter/config.ex">>,<<"lib/ex_doc/formatter/html">>, - <<"lib/ex_doc/formatter/html/search_data.ex">>, - <<"lib/ex_doc/formatter/html/templates">>, - <<"lib/ex_doc/formatter/html/templates/summary_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/sidebar_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/module_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/extra_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/search_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/redirect_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/head_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/api_reference_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/not_found_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/detail_template.eex">>, - <<"lib/ex_doc/formatter/html/templates/footer_template.eex">>, - <<"lib/ex_doc/formatter/html/templates.ex">>, - <<"lib/ex_doc/formatter/html/assets.ex">>, - <<"lib/ex_doc/formatter/epub.ex">>,<<"lib/ex_doc/formatter/markdown.ex">>, - <<"lib/ex_doc/formatter/epub">>,<<"lib/ex_doc/formatter/epub/templates">>, - <<"lib/ex_doc/formatter/epub/templates/module_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/extra_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/media-types.txt">>, - <<"lib/ex_doc/formatter/epub/templates/head_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/content_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/toc_item_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/nav_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates/title_template.eex">>, - <<"lib/ex_doc/formatter/epub/templates.ex">>, - <<"lib/ex_doc/formatter/epub/assets.ex">>,<<"lib/ex_doc/nodes.ex">>, - <<"lib/ex_doc/shell_lexer.ex">>,<<"lib/ex_doc/application.ex">>, - <<"lib/ex_doc/cli.ex">>,<<"lib/ex_doc/utils.ex">>, - <<"lib/ex_doc/language.ex">>,<<"lib/ex_doc/markdown.ex">>, - <<"lib/ex_doc/refs.ex">>,<<"lib/ex_doc/doc_ast.ex">>,<<"lib/mix">>, - <<"lib/mix/tasks">>,<<"lib/mix/tasks/docs.ex">>,<<"LICENSE">>,<<"mix.exs">>, - <<"README.md">>]}. -{<<"app">>,<<"ex_doc">>}. -{<<"licenses">>,[<<"Apache-2.0">>]}. -{<<"requirements">>, - [[{<<"name">>,<<"earmark_parser">>}, - {<<"app">>,<<"earmark_parser">>}, - {<<"optional">>,false}, - {<<"requirement">>,<<"~> 1.4.44">>}, - {<<"repository">>,<<"hexpm">>}], - [{<<"name">>,<<"makeup_elixir">>}, - {<<"app">>,<<"makeup_elixir">>}, - {<<"optional">>,false}, - {<<"requirement">>,<<"~> 0.14 or ~> 1.0">>}, - {<<"repository">>,<<"hexpm">>}], - [{<<"name">>,<<"makeup_erlang">>}, - {<<"app">>,<<"makeup_erlang">>}, - {<<"optional">>,false}, - {<<"requirement">>,<<"~> 0.1 or ~> 1.0">>}, - {<<"repository">>,<<"hexpm">>}], - [{<<"name">>,<<"makeup_c">>}, - {<<"app">>,<<"makeup_c">>}, - {<<"optional">>,true}, - {<<"requirement">>,<<">= 0.1.0">>}, - {<<"repository">>,<<"hexpm">>}], - [{<<"name">>,<<"makeup_html">>}, - {<<"app">>,<<"makeup_html">>}, - {<<"optional">>,true}, - {<<"requirement">>,<<">= 0.1.0">>}, - {<<"repository">>,<<"hexpm">>}]]}. -{<<"build_tools">>,[<<"mix">>]}. diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc.ex deleted file mode 100644 index f9a98a6..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc.ex +++ /dev/null @@ -1,549 +0,0 @@ -defmodule ExDoc do - @moduledoc """ - Main entry point for generating docs. - """ - @ex_doc_version Mix.Project.config()[:version] - - @doc """ - Returns the ExDoc version (used in templates). - """ - @spec version :: String.t() - def version, do: @ex_doc_version - - @doc """ - Emits a warning. - """ - def warn(message, stacktrace_info) do - :persistent_term.put({__MODULE__, :warned?}, true) - IO.warn(message, stacktrace_info) - end - - defp unset_warned() do - warned? = :persistent_term.get({__MODULE__, :warned?}, false) - :persistent_term.erase({__MODULE__, :warned?}) - warned? - end - - @doc ~S""" - Generates documentation for the given `project`, `vsn` (version), - `source_beams` directories, and `options`. - - By default it generates HTML, Markdown, and EPUB documents. - - ## Options - - * `:annotations_for_docs` - a function that receives metadata and returns a list - of annotations to be added to the signature. The metadata received will also - contain `:module`, `:name`, `:arity` and `:kind` to help identify which entity is - currently being processed. - - * `:api_reference` - Whether to generate `api-reference.html`; default: `true`. - If this is set to false, `:main` must also be set. - - * `:assets` - A map of source => target directories that will be copied as is to - the output path. It defaults to an empty map. - - * `:authors` - List of authors for the generated docs or epub. - - * `:before_closing_body_tag` - a function that takes as argument an atom specifying - the formatter being used (`:html` or `:epub`) and returns a literal HTML string - to be included just before the closing body tag (``). - The atom given as argument can be used to include different content in both formats. - Useful to inject custom assets, such as Javascript. - - * `:before_closing_head_tag` - a function that takes as argument an atom specifying - the formatter being used (`:html` or `:epub`) and returns a literal HTML string - to be included just before the closing head tag (``). - The atom given as argument can be used to include different content in both formats. - Useful to inject custom assets, such as CSS stylesheets. - - * `:before_closing_footer_tag` - a function that takes as argument an atom specifying - the formatter being used (`:html`) and returns a literal HTML string - to be included just before the closing footer tag (``). - This option only has effect on the html formatter. - Useful if you want to inject an extra footer into the documentation. - - * `:canonical` - String that defines the preferred URL with the rel="canonical" - element; defaults to no canonical path. - - * `:cover` - Path to the epub cover image (only PNG or JPEG accepted) - The image size should be around 1600x2400. When specified, the cover will be placed under - the "assets" directory in the output path under the name "cover" and the - appropriate extension. This option has no effect when using the "html" formatter. - - * `:deps` - A keyword list application names and their documentation URL. - ExDoc will by default include all dependencies and assume they are hosted on - HexDocs. This can be overridden by your own values. Example: `[plug: "https://myserver/plug/"]` - - * `:description` - A brief description of the project, currently included in Hex.pm and the generated - `llms.txt` file - - * `:extra_section` - String that defines the section title of the additional - Markdown and plain text pages; default: "Pages". Example: "Guides" - - * `:extras` - List of paths to additional Markdown (`.md` extension), Live Markdown - (`.livemd` extension), Cheatsheets (`.cheatmd` extension), external urls (`:url` option), - and plain text pages to add to the documentation. You can also specify keyword pairs to - customize the generated filename, title and source file, and search content of each extra page; - default: `[]`. Example: `["README.md", "LICENSE", "CONTRIBUTING.md": [filename: "contributing", - title: "Contributing", source: "CONTRIBUTING.mdx"]]` See the Customizing Extras section for - more. - - * `:favicon` - Path to a favicon image file for the project. Must be PNG, JPEG or SVG. When - specified, the image file will be placed in the output "assets" directory, named - "favicon.EXTENSION". If using SVG, ensure appropriate width, height and viewBox attributes - are present in order to ensure predictable sizing and cropping. - - * `:filter_modules` - Include only modules that match the given value. The - value can be a regex, a string (representing a regex), or a two-arity - function that receives the module and its metadata and returns true if the - module must be included. If a string or a regex is given, it will be matched - against the complete module name (which includes the "Elixir." prefix for - Elixir modules). If a module has `@moduledoc false`, then it is always excluded. - - * `:formatters` - Formatter to use; default: ["html", "markdown", "epub"], options: "html", "markdown", "epub". - - * `:footer` - When false, does not render the footer on all pages, except for - the required "Built with ExDoc" note. - - * `:groups_for_extras`, `:groups_for_modules`, `:groups_for_docs`, and `:default_group_for_doc` - - See the "Groups" section. - - * `:ignore_apps` - Apps to be ignored when generating documentation in an umbrella project. - Receives a list of atoms. Example: `[:first_app, :second_app]`. - - * `:language` - Identify the primary language of the documents, its value must be - a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag. Default: "en". - - * `:logo` - Path to a logo image file for the project. Must be PNG, JPEG or SVG. When - specified, the image file will be placed in the output "assets" directory, named - "logo.EXTENSION". The image will be shown within a 48x48px area. If using SVG, ensure - appropriate width, height and viewBox attributes are present in order to ensure - predictable sizing and cropping. - - * `:main` - Main page of the documentation. It may be a module or a - generated page, like "Plug" or "api-reference". Default: "api-reference". - - * `:markdown_processor` - The markdown processor to use, - either `module()` or `{module(), keyword()}` to provide configuration options. - - * `:nest_modules_by_prefix` - See the "Nesting" section. - - * `:output` - Output directory for the generated docs. Default: "doc". - May be overridden by command line argument. - - * `:redirects` - A map or list of tuples, where the key is the path to redirect from and the - value is the path to redirect to. The extension is omitted in both cases, i.e `%{"old-readme" => "readme"}`. - The destination may include an anchor, i.e `%{"old-readme" => "readme#section"}`. - See the "Changing documentation over time" section below for more. - - * `:search` - A list of search engine configurations. See the "Search engines" section. - - * `:skip_undefined_reference_warnings_on` - ExDoc warns when it can't create a `Mod.fun/arity` - reference in the current project docs (for example, because of a typo). This option controls when to - skip such warnings. This option can be a list of strings that will be checked for exact matches, - or a function that takes a *reference* and must return a boolean (`true` means "skip this"). - *References* that are checked against this option (either whether they're in the given - list or whether they match the given function) are the relative filename, the "ID" of - the node (like `User.exists?/1`), or the module name. Examples for this option: - * `["Foo", "Bar.baz/0"]` - skip warnings for `Foo` and `Bar.baz/0` - * `&String.match?(&1, ~r/Foo/)` - skip warnings for any reference that matches the regex - * `["pages/deprecations.md"]` - skip warnings for any reference in the - `pages/deprecations.md` file - - * `:skip_code_autolink_to` - Similar to `:skip_undefined_reference_warnings_on`, this option - controls which terms will be skipped by ExDoc when building documentation. - Useful for example if you want to highlight private modules or functions without warnings. - This option can be a function from a term to a boolean (e.g.: `&String.match?(&1, ~r/PrivateModule/)`) - or a list of terms (e.g.:`["PrivateModule", "PrivateModule.func/1"]`); - default is nothing to be skipped. - - * `:source_beam` - Path to the beam directory; default: mix's compile path. - - * `:source_url` - The source URL fallback if `:source_url` is not given at the project - configuration. See the following sections. - - * `:source_ref` - The branch/commit/tag used for source link inference. - Default: "main". - - * `:source_url_pattern` - Public URL of the project for source links. This is derived - automatically from the project's `:source_url` and `:source_ref` when using one of - the supported public hosting services (currently GitHub, GitLab, or Bitbucket). If - you are using one of those services with their default public hostname, you do not - need to set this configuration. - - However, if using a different solution, or self-hosting, you will need to set this - configuration variable to a pattern for source code links. The value must be a string or - a function. - - If a string, then it should be the full URI to use for links with the following - variables available for interpolation: - - * `%{path}`: the path of a file in the repo - * `%{line}`: the line number in the file - - For self-hosted GitLab/GitHub: - - ```text - https://mydomain.org/user_or_team/repo_name/blob/main/%{path}#L%{line} - ``` - - For self-hosted Bitbucket: - - ```text - https://mydomain.org/user_or_team/repo_name/src/main/%{path}#cl-%{line} - ``` - - If a function, then it must be a function that takes two arguments, path and line, - where path is either an relative path from the cwd, or an absolute path. The function - must return the full URI as it should be placed in the documentation. - - ### Using `:source_url` and `:source_ref` together - - A common setup for a project or library is to set both `:source_url` and `:source_ref`. Setting - both of them will allow ExDoc to link to a specific version of the code for a function or module - that matches the version of the docs. If the docs have been generated for version 1.0.5, - clicking on the source link in the docs will take the browser to the source code for the 1.0.5 - version of the code instead of the primary ref (for example, `main`). - - An example setup: - - @version "0.30.10" - def project do - [ - ... - version: @version, - source_url: "https://github.com/USER/PROJECT", - docs: &docs/0 - ] - end - - def docs do - [ - ... - source_ref: "v#{@version}" - ] - end - - If you use `source_ref: "v#{@version}"`, you should run `git tag vVERSION` and push the tag when - publishing a new version of your package; ExDoc will generate links to the specific version for - which the docs were generated. - - ## Additional pages (extras) - - It is possible to attach additional pages to the documentation. Markdown extensions (and variations) - such as `.md`, `.livemd`, and `.cheatmd` rendered by extension. Other extensions are added as is. - - When specifying extras, the allowed configuration is: - - * `:title` - The title of the extra page. If not provided, the title will be inferred from the filename. - * `:filename` - The name of the generated file. If not provided, the filename will be inferred from - the source file. - * `:source` - The source file of the extra page. This is useful if you want to customize the filename or - title but keep the source file unchanged. - * `:search_data` - A list of terms to be indexed for autocomplete and search. If not provided, the content - of the extra page will be indexed for search. See the section below for more. - - It is also possible to specify URLs, which are added as links to the sidebar: - - * `:title` - The title of the extra page. If not provided, the title will be inferred from the extra name. - * `:url` - The external url to link to from the sidebar. - - ### Customizing search data - - It is possible to fully customize the way a given extra is indexed, both in autocomplete and in search. - In most cases, this makes sense for _generated_ documentation. If `search_data` is provided, it completely - overrides the built in logic for indexing your document based on the headers and content of the document. - The following fields can be provided in a list of maps for `search_data`. - - * `:anchor` - The anchor link for the search result. Use `""` to point to the top of the page. - * `:title` - The title of the result. - * `:type` - The type of the search result, such as "module", "function" or "section". - * `:body` - The main content or body of the search result, _as markdown_. Used in search, not autocomplete. - - ## Groups - - ExDoc content can be organized in groups. This is done via the `:groups_for_extras` - and `:groups_for_modules`. For example, imagine you are storing extra guides in - your documentation which are organized per directory. In the extras section you - have: - - extras: [ - "guides/introduction/foo.md", - "guides/introduction/bar.md", - - ... - - "guides/advanced/baz.md", - "guides/advanced/bat.md" - ] - - You can have those grouped as follows: - - groups_for_extras: [ - "Introduction": Path.wildcard("guides/introduction/*.md"), - "Advanced": Path.wildcard("guides/advanced/*.md") - ] - - Or via a regex: - - groups_for_extras: [ - "Introduction": ~r"/introduction/", - "Advanced": ~r"/advanced/" - ] - - External extras from a URL can also be grouped: - - groups_for_extras: [ - "Elixir": ~r"https://elixir-lang.org/", - "Erlang": ~r"https://www.erlang.org/" - ] - - Similar can be done for modules: - - groups_for_modules: [ - "Data types": [Atom, Regex, URI], - "Collections": [Enum, MapSet, Stream] - ] - - A regex or the string name of the module is also supported. - - ### Grouping functions, types, and callbacks - - Types, functions, and callbacks inside a module can also be organized in groups. - - #### Group metadata - - By default, ExDoc respects the `:group` metadata field to determine in which - group an element belongs: - - @doc group: "Queries" - def get_by(schema, fields) - - The function above will be automatically listed under the "Queries" section in - the sidebar. The benefit of using `:group` is that it can also be used by tools - such as IEx during autocompletion. These groups are then displayed in the sidebar. - - It is also possible to tell ExDoc to either enrich the group metadata or lookup a - different field via the `:default_group_for_doc` configuration. The default is: - - default_group_for_doc: fn metadata -> metadata[:group] end - - The `metadata` received contains all of the documentation metadata, such as `:group`, - but also `:module`, `:name`, `:arity` and `:kind` to help identify which entity is - currently being processed. For example, projects like Nx have a custom function that - converts "Queries" into "Function: Queries": - - default_group_for_doc: fn metadata -> - if group = metadata[:group] do - "Functions: #{group}" - end - end - - Finally, you can also use the `:groups_for_docs` which works similarly as the - one for modules/extra pages. - - `:groups_for_docs` is a keyword list of group titles and filtering functions - that receive the documentation metadata and must return a boolean. - For example, imagine that you have an API client library with a large surface - area for all the API endpoints you need to support. It would be helpful to - group the functions with similar responsibilities together. In this case in - your module you might have: - - defmodule APIClient do - @doc section: :auth - def refresh_token(params \\ []) - - @doc subject: :object - def update_status(id, new_status) - - @doc permission: :grant - def grant_privilege(resource, privilege) - end - - And then in the configuration you can group these with: - - groups_for_docs: [ - Authentication: & &1[:section] == :auth, - Resource: & &1[:subject] == :object, - Admin: & &1[:permission] in [:grant, :write] - ] - - A function can belong to a single group only. The first group that matches - will be the one used. In case no group is found in `:groups_for_docs`, - the `:default_group_for_doc` callback is invoked. If it returns nil, it - then falls back to the appropriate "Functions", "Types" or "Callbacks" - section respectively. - - #### Group descriptions - - It is possible to display a description for each group under its respective section - in a module's page. This helps to better explain what is the intended usage of each - group elements instead of describing everything in the displayed `@moduledoc`. - - Descriptions can be provided as `@moduledoc` metadata. Groups without descriptions are - also supported to define group ordering. - - @moduledoc groups: [ - "Main API", - %{title: "Helpers", description: "Functions shared with other modules."} - ] - - Descriptions can also be given in the `:default_group_for_doc` configuration: - - default_group_for_doc: fn metadata -> - case metadata[:group] do - :main_api -> "Main API" - :helpers -> [title: "Helpers", description: "Functions shared with other modules."] - _ -> nil - end - end - - Keyword lists or maps are supported in either case. - - When using `:groups_for_docs`, if all the elements for a given group are matched then the - `:default_group_for_doc` is never invoked and ExDoc will not know about the description. - In that case, the description should be provided in the `@moduledoc` `:groups` metadata. - - Whenever using the `:group` key, the groups will be ordered alphabetically. - If you also want control over the group order, you can also use the `:groups_for_docs` - which works similarly as the one for modules/extra pages. - - #### Group ordering - - Groups in the sidebar and main page body are ordered according to the following - rules: - - * First, groups defined as `@moduledoc groups: [...]` in the given order. - * Then groups defined as keys in the `:groups_for_docs` configuration. - * Then default groups: Types, Callbacks and Functions. - * Finally, other groups returned by `:default_group_for_doc` by alphabetical order. - - ## Nesting - - ExDoc also allows module names in the sidebar to appear nested under a given - prefix. The `:nest_modules_by_prefix` expects a list of module names, such as - `[Foo.Bar, Bar.Baz]`. In this case, a module named `Foo.Bar.Baz` will appear - nested within `Foo.Bar` and only the name `Baz` will be shown in the sidebar. - Note the `Foo.Bar` module itself is not affected. - - This option is mainly intended to improve the display of long module names in - the sidebar, particularly when they are too long for the sidebar or when many - modules share a long prefix. If you mean to group modules logically or call - attention to them in the docs, you should probably use `:groups_for_modules` - (which can be used in conjunction with `:nest_modules_by_prefix`). - - ## HTML-specific configuration - - ### Search engines - - ExDoc allows custom search engines via the `:search` key. Each search engine - is a map with the following keys: - - * `:name` - The display name of the search engine (required) - * `:help` - A help text describing what the search engine does (required) - * `:url` - The optional search URL template, usually ending with `q=` - * `:packages` - An optional list of packages (or package-versions) to search on - https://hexdocs.pm. For example: `[:plug, :phoenix, ecto: "3.0.0", ecto_sql: "3.0.0"]`. - If no version is specified, it uses the package latest - - If none of `:url` or `:packages` are given, ExDoc will use its default search engine - powered by Lunr. - - When multiple search engines are configured, a dropdown selector will appear next to - the search bar allowing users to choose which engine to use. For example: - - search: [ - %{name: "FooBar", help: "Search on FooBar", url: "https://example.com/?q="}, - %{name: "Local", help: "In-browser search"} - ] - - If only one search engine is configured, the dropdown selector will be hidden. - If no search engine is configured, only the built-in Lunr's is shown. - - ### Redirects and changing documentation over time - - As your project grows, your documentation may change, perhaps even structurally. - There are a few important things to consider in this regard: - - * Links to your *extras* will break if you change or move file names. - * Links to your *modules, and mix tasks* will change if you change their name. - * Links to *functions* are actually links to modules with anchor links. - If you change the function name, the link does not break but will leave users - at the top of the module's documentation. - - Because these docs are static files, the behaviour of a missing page will depend on where - they are hosted. In particular, [hexdocs.pm](https://hexdocs.pm) will show a 404 page. - - You can improve the developer experience on everything but function names changing - by using the `redirects` configuration. For example, if you changed the module `MyApp.MyModule` - to `MyApp.My.Module` and the extra `get-started.md` to `quickstart.md`, you can - setup the following redirects: - - redirects: %{ - "MyApp.MyModule" => "MyApp.My.Module", - "get-started" => "quickstart" - } - - The destination may also include an anchor to redirect to a specific section: - - redirects: %{ - "old-page" => "new-page#relevant-section" - } - - ### Meta tags configuration - - It is also possible to configure some of ExDoc's behaviour using meta tags. - - * `exdoc:autocomplete` - when set to "off", disables autocompletion. - - * `exdoc:autocomplete-limit` - Set to an integer to configure how many results - appear in the autocomplete dropdown. Defaults to 10. - - You can insert meta tags using the `before_closing_head_tag` option. - - ### Version dropdown menu - - ExDoc will automatically render a version dropdown on HTML pages if a - `docs_config.js` file is placed within the documentation. - This file may define the following global variables in JavaScript: - - * `versionNodes` - an array of `{"version":"vNUMBER", "url":url}` listing - all documented versions and their URLs. ExDoc will automatically match - the version of the package with the one in the array to mark as current. - """ - @spec generate(String.t(), String.t(), [Path.t()], Keyword.t()) :: - [%{entrypoint: String.t(), warned?: boolean(), formatter: module()}] - def generate(project, version, source_beams, options) - when is_binary(project) and is_binary(version) and is_list(source_beams) and - is_list(options) do - # Clear it up for tests - _ = unset_warned() - - retriever = Keyword.get(options, :retriever, ExDoc.Retriever) - extras_input = Keyword.get(options, :extras, []) - - # Build configs independently (build both upfront for validation) - retriever_config = ExDoc.Config.build(options) - formatter_config = ExDoc.Formatter.Config.build(project, version, options) - - # Retriever phase (run once for all formatters) - {modules, filtered} = retriever.docs_from_dir(source_beams, retriever_config) - extras = ExDoc.Extras.build(extras_input, retriever_config) - - for formatter <- formatter_config.formatters do - formatter = find_formatter(formatter) - entrypoint = ExDoc.Formatter.run(formatter, formatter_config, modules, filtered, extras) - %{entrypoint: entrypoint, warned?: unset_warned(), formatter: formatter} - end - end - - defp find_formatter(modname) when is_atom(modname), - do: modname - - defp find_formatter("ExDoc.Formatter." <> _ = name), - do: Module.concat([name]) - - defp find_formatter(name), - do: Module.concat([ExDoc.Formatter, String.upcase(name)]) -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/application.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/application.ex deleted file mode 100644 index 2b74f53..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/application.ex +++ /dev/null @@ -1,27 +0,0 @@ -defmodule ExDoc.Application do - @moduledoc false - use Application - - def start(_type, _args) do - Makeup.Registry.register_lexer(ExDoc.ShellLexer, - options: [], - names: ["shell", "console", "sh", "bash", "zsh"], - extensions: [] - ) - - # Load applications so we can find their modules in docs - Enum.each([:eex, :ex_unit, :iex, :logger, :mix], &Application.load/1) - - # Start all applications with the makeup prefix - for {app, _, _} <- Application.loaded_applications(), - match?("makeup_" <> _, Atom.to_string(app)) do - Application.ensure_all_started(app) - end - - children = [ - ExDoc.Refs - ] - - Supervisor.start_link(children, strategy: :one_for_one) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/autolink.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/autolink.ex deleted file mode 100644 index 9a1c4c0..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/autolink.ex +++ /dev/null @@ -1,660 +0,0 @@ -defmodule ExDoc.Autolink do - # Encapsulates all functionality related to autolinking, - # decoupled from language and ExDoc structs. - @moduledoc false - - # * `:apps` - the apps that the docs are being generated for. When linking modules they are - # checked if they are part of the app and based on that the links are relative or absolute. - # - # * `:current_module` - the module that the docs are being generated for. Used to link local - # calls and see if remote calls are in the same app. - # - # * `:current_kfa` - the kind, function, arity that the docs are being generated for. Is nil - # if there is no such thing. Used to generate more accurate warnings. - # - # * `:module_id` - id of the module being documented (e.g.: `"String"`) - # - # * `:file` - source file location - # - # * `:line` - line number of the beginning of the documentation - # - # * `:language` - the language call-back module to use - # - # * `:id` - a module/function/etc being documented (e.g.: `"String.upcase/2"`) - # - # * `:ext` - the extension (`".html"`, "`.xhtml"`, etc) - # - # * `:extras` - map of extras - # - # * `:skip_undefined_reference_warnings_on` - function that will be called with - # a module/function/file/etc and return a boolean whether to skip warning on it. - # - # * `:skip_code_autolink_to` - function that will be called with a term and return a boolean - # whether to skip autolinking to it. - # - # * `:filtered_modules` - A list of module nodes that were filtered by the retriever - # - # * `:warnings` - one of: - # - # * `:emit` (default) - # - # * `:raise` (useful for tests) - # - # * `:send` - send back to caller (useful for tests) - - alias ExDoc.Refs - - defstruct [ - :current_module, - :module_id, - :id, - :line, - :language, - file: "nofile", - apps: [], - extras: [], - deps: [], - ext: ".html", - current_kfa: nil, - siblings: [], - skip_undefined_reference_warnings_on: - &ExDoc.Formatter.Config.skip_undefined_reference_warnings_on/1, - skip_code_autolink_to: &ExDoc.Formatter.Config.skip_code_autolink_to/1, - force_module_prefix: nil, - filtered_modules: [], - assets: %{}, - warnings: :emit - ] - - @hexdocs "https://hexdocs.pm/" - @otpappdocs "https://www.erlang.org/doc/apps/" - - def app_module_url(tool, module, anchor \\ "#content", config) - - def app_module_url(:no_tool, _, _, _), do: nil - - def app_module_url(tool, module, anchor, config) do - base_url = - case tool do - :ex_doc -> @hexdocs - :otp -> @otpappdocs - end - - path = module |> inspect() |> String.trim_leading(":") - app_url(base_url, module, config, path, config.ext, "#{anchor}") - end - - defp string_app_module_url(tool, module, anchor, config) do - if Enum.any?(config.filtered_modules, &(&1.module == module)) do - warn(config, "reference to a filtered module") - nil - else - app_module_url(tool, module, anchor, config) - end - end - - @doc false - def ex_doc_app_url(module, config, path, ext, suffix) do - app_url(@hexdocs, module, config, path, ext, suffix) - end - - defp app_url(base_url, module, config, path, ext, suffix) do - if app = app(module) do - if app in config.apps do - path <> ext <> suffix - else - config.deps - |> Keyword.get_lazy(app, fn -> base_url <> "#{app}" end) - |> String.trim_trailing("/") - |> Kernel.<>("/" <> path <> ".html" <> suffix) - end - else - path <> ext <> suffix - end - end - - defp app(module) do - case :code.which(module) do - :preloaded -> - :erts - - maybe_path -> - case :application.get_application(module) do - {:ok, app} -> - app - - _ -> - with true <- is_list(maybe_path), - [_, "ebin", app, "lib" | _] <- maybe_path |> Path.split() |> Enum.reverse() do - String.split(app, "-") |> hd() |> String.to_atom() - else - _ -> nil - end - end - end - end - - @doc false - def tool(module, config) do - if match?("Elixir." <> _, Atom.to_string(module)) do - :ex_doc - else - app = app(module) - - if is_app_otp(app) and app not in config.apps and not Keyword.has_key?(config.deps, app) do - :otp - else - :ex_doc - end - end - end - - defp is_app_otp(app) do - maybe_lib_dir_path = :code.lib_dir(app) - is_list(maybe_lib_dir_path) and List.starts_with?(maybe_lib_dir_path, :code.root_dir()) - end - - def maybe_warn(config, ref, visibility, metadata) do - file = Path.relative_to_cwd(config.file) - - unless Enum.any?( - [config.id, config.module_id, file], - config.skip_undefined_reference_warnings_on - ) do - warn(config, ref, visibility, metadata) - end - end - - defp ref_regex, do: ~r/^`(.+)`$/ - - def custom_link(attrs, config) do - case Keyword.fetch(attrs, :href) do - {:ok, href} -> - case Regex.scan(ref_regex(), href) do - [[_, custom_link]] -> - custom_link - |> url(:custom_link, config) - |> remove_and_warn_if_invalid(custom_link, config) - - [] -> - build_extra_link(href, config) - end - - _ -> - nil - end - end - - def url(string = "mix help " <> name, mode, config) do - name |> mix_task(string, mode, config) |> maybe_remove_link(mode) - end - - def url(string = "mix " <> name, mode, config) do - name |> mix_task(string, mode, config) |> maybe_remove_link(mode) - end - - def url(string, mode, config) do - if config.skip_code_autolink_to.(string) do - nil - else - parse_url(string, mode, config) - end - end - - defp remove_and_warn_if_invalid(nil, reference, config) do - warn( - config, - ~s[documentation references "#{reference}" but it is invalid] - ) - - :remove_link - end - - defp remove_and_warn_if_invalid(result, _, _), do: result - - @builtin_ext [".livemd", ".cheatmd", ".md", ".txt", ""] - - defp build_extra_link(link, config) do - with %{scheme: nil, host: nil, path: path} = uri <- URI.parse(link), - true <- is_binary(path) and path != "" and not (path =~ ref_regex()), - true <- Path.extname(path) in @builtin_ext do - cond do - file = config.extras[Path.basename(path)] -> - append_fragment(file <> config.ext, uri.fragment) - - asset_file?(path, config.assets) -> - nil - - true -> - maybe_warn(config, nil, nil, %{file_path: path, original_text: link}) - nil - end - else - _ -> nil - end - end - - defp asset_file?(path, assets) do - Enum.any?(assets, fn {source_dir, target_dir} -> - prefix = String.trim_trailing(target_dir, "/") <> "/" - - if String.starts_with?(path, prefix) do - path - |> String.trim_leading(prefix) - |> Path.expand(source_dir) - |> File.exists?() - end - end) - end - - defp maybe_remove_link(nil, :custom_link) do - :remove_link - end - - defp maybe_remove_link(result, _mode) do - result - end - - defp mix_task(name, string, mode, config) do - {module, url, visibility} = - if name =~ ~r/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/ do - parts = name |> String.split(".") |> Enum.map(&Macro.camelize/1) - module = Module.concat([Mix, Tasks | parts]) - - {module, module_url(module, :regular_link, config, string), - Refs.get_visibility({:module, module})} - else - {nil, nil, :undefined} - end - - if url in [nil, :remove_link] and mode == :custom_link do - maybe_warn(config, {:module, module}, visibility, %{ - mix_task: true, - original_text: string - }) - end - - url - end - - defp module_url(module, anchor \\ nil, mode, config, string) do - ref = {:module, module} - - case {mode, Refs.get_visibility(ref)} do - {_link_type, visibility} when visibility in [:public, :limited] -> - string_app_module_url(tool(module, config), module, anchor, config) - - {:regular_link, :undefined} -> - nil - - {:custom_link, visibility} when visibility in [:hidden, :undefined] -> - maybe_warn(config, ref, visibility, %{original_text: string}) - :remove_link - - {_link_type, visibility} -> - maybe_warn(config, ref, visibility, %{original_text: string}) - nil - end - end - - defp extra_url(string, config) do - case String.split(string, ":", parts: 2) do - [app, extra] -> - {extra, anchor} = - case String.split(extra, "#", parts: 2) do - [extra] -> - {extra, ""} - - [extra, anchor] -> - {extra, "#" <> anchor} - end - - app = String.to_atom(app) - - config.deps - |> Keyword.get_lazy(app, fn -> - if Application.ensure_loaded(app) != :ok do - maybe_warn( - config, - "documentation references \"e:#{string}\" but #{app} cannot be found.", - nil, - %{} - ) - end - - prefix = - cond do - app in config.apps -> "" - is_app_otp(app) -> @otpappdocs - true -> @hexdocs - end - - prefix <> "#{app}" - end) - |> String.trim_trailing("/") - |> Kernel.<>("/" <> convert_extra_extension(extra, config) <> anchor) - - _ -> - nil - end - end - - defp convert_extra_extension(extra, config) do - if Path.extname(extra) in @builtin_ext do - Path.rootname(extra) <> config.ext - else - extra - end - end - - defp parse_url(string, mode, config) do - case Regex.run(~r{^(.+)/(\d+)(#.*)?$}, string) do - [_, left, right | maybe_fragment] -> - with {:ok, arity} <- parse_arity(right) do - {kind, rest} = kind(left) - - case config.language.parse_module_function(rest) do - {:local, function} -> - kind - |> local_url(function, arity, config, string, mode: mode) - |> maybe_append_nested_fragment(maybe_fragment) - |> maybe_remove_link(mode) - - {:remote, module, function} -> - {kind, module, function, arity} - |> remote_url(config, string, mode: mode) - |> maybe_append_nested_fragment(maybe_fragment) - |> maybe_remove_link(mode) - - :error -> - nil - end - else - _ -> - nil - end - - nil -> - case string do - "m:" <> rest -> - parse_module_with_anchor(rest, config) - - "e:" <> rest -> - extra_url(rest, config) - - string when mode == :custom_link -> - parse_module_with_anchor(string, config) - - string when not config.force_module_prefix -> - case config.language.parse_module(string, mode) do - {:module, module} -> - module_url(module, mode, config, string) - - :error -> - nil - end - - _ -> - nil - end - - _ -> - nil - end - end - - defp parse_module_with_anchor(string, config) do - destructure [rest, fragment], String.split(string, "#", parts: 2) - # TODO: rename :custom_link to :strict i.e. we expect ref to be valid - # force custom_link mode because of m: prefix. - case config.language.parse_module(rest, :custom_link) do - {:module, module} -> - module_url(module, fragment && "#" <> fragment, :custom_link, config, rest) - - :error -> - nil - end - end - - defp parse_arity(string) do - case Integer.parse(string) do - {arity, ""} -> {:ok, arity} - _ -> :error - end - end - - def kind("c:" <> rest), do: {:callback, rest} - def kind("t:" <> rest), do: {:type, rest} - ## \\ does not work for :custom_url as Earmark strips the \... - def kind("\\" <> rest), do: {:function, rest} - def kind(rest), do: {:function, rest} - - def local_url(kind, name, arity, config, original_text, options \\ []) - - def local_url(kind, name, arity, config, original_text, options) do - module = config.current_module - ref = {kind, module, name, arity} - mode = Keyword.get(options, :mode, :regular_link) - visibility = Refs.get_visibility(ref) - - case {kind, visibility} do - {_kind, :public} -> - fragment(kind, name, arity) - - {:function, _visibility} -> - case config.language.try_autoimported_function(name, arity, mode, config, original_text) do - nil -> - if mode == :custom_link do - maybe_warn(config, ref, visibility, %{original_text: original_text}) - end - - nil - - url -> - url - end - - {:type, _visibility} -> - case config.language.try_builtin_type(name, arity, mode, config, original_text) do - nil -> - if mode == :custom_link or config.language == ExDoc.Language.Erlang do - maybe_warn(config, ref, visibility, %{original_text: original_text}) - end - - nil - - url -> - url - end - - _ -> - maybe_warn(config, ref, visibility, %{original_text: original_text}) - nil - end - end - - def fragment(kind, nil, arity) do - fragment(kind, "nil", arity) - end - - def fragment(kind, name, arity) do - "#" <> prefix(kind) <> "#{encode_fragment_name(name)}/#{arity}" - end - - defp encode_fragment_name(name) when is_atom(name) do - encode_fragment_name(Atom.to_string(name)) - end - - defp encode_fragment_name(name) when is_binary(name) do - URI.encode(name) - end - - defp prefix(kind) - defp prefix(:function), do: "" - defp prefix(:callback), do: "c:" - defp prefix(:type), do: "t:" - - def remote_url({kind, module, name, arity} = ref, config, original_text, opts \\ []) do - warn? = Keyword.get(opts, :warn?, true) - mode = Keyword.get(opts, :mode, :regular_link) - same_module? = module == config.current_module - - case {mode, Refs.get_visibility({:module, module}), Refs.get_visibility(ref)} do - {_mode, _module_visibility, :public} -> - tool = tool(module, config) - - if same_module? do - fragment(kind, name, arity) - else - url = string_app_module_url(tool, module, nil, config) - url && url <> fragment(kind, name, arity) - end - - {:regular_link, module_visibility, :undefined} - when module_visibility == :public - when module_visibility == :limited and kind != :type -> - if warn? do - maybe_warn(config, ref, :undefined, %{original_text: original_text}) - end - - nil - - {:regular_link, _module_visibility, :undefined} - when not same_module? and - (config.language != ExDoc.Language.Erlang or kind == :function) -> - nil - - {:regular_link, :hidden, :hidden} - when not same_module? -> - if warn? do - maybe_warn(config, ref, :hidden, %{ - original_text: original_text, - module_visibility: :hidden, - same_module?: false - }) - end - - nil - - {_mode, _module_visibility, visibility} -> - if warn? do - maybe_warn(config, ref, visibility, %{original_text: original_text}) - end - - nil - end - end - - @doc false - def warn(config, message) do - f = - case config.current_kfa do - {:function, f, a} -> - [function: {f, a}] - - _ -> - [] - end - - stacktrace_info = [file: config.file, line: config.line, module: config.current_module] ++ f - - case config.warnings do - :emit -> - ExDoc.warn(message, stacktrace_info) - - :raise -> - ExDoc.warn(message, stacktrace_info) - raise "fail due to warnings" - - :send -> - send(self(), {:warn, message, file: config.file, line: config.line}) - end - end - - defp warn(config, ref, visibility, metadata) - - defp warn( - config, - {:module, _module}, - visibility, - %{mix_task: true, original_text: original_text} - ) do - message = - "documentation references \"#{original_text}\" but it is " <> - format_visibility(visibility, :module) - - warn(config, message) - end - - defp warn( - config, - {:module, _module}, - visibility, - %{original_text: original_text} - ) do - message = - "documentation references module \"#{original_text}\" but it is " <> - format_visibility(visibility, :module) - - warn(config, message) - end - - defp warn( - config, - nil, - _visibility, - %{file_path: _file_path, original_text: original_text} - ) do - message = "documentation references file \"#{original_text}\" but it does not exist" - - warn(config, message) - end - - defp warn( - config, - {:type, module, _name, _arity}, - :hidden, - %{original_text: original_text, module_visibility: :hidden, same_module?: false} - ) do - message = - "documentation references type \"#{original_text}\" but the module " <> - "#{inspect(module)} is #{format_visibility(:hidden, :module)}" - - warn(config, message) - end - - defp warn( - config, - {kind, _module, _name, _arity}, - visibility, - %{original_text: original_text} - ) do - message = - "documentation references #{kind} \"#{original_text}\" but it is " <> - format_visibility(visibility, kind) - - warn(config, message) - end - - defp warn(config, message, _, _) when is_binary(message) do - warn(config, message) - end - - # there is not such a thing as private callback or private module - def format_visibility(visibility, kind) when kind in [:module, :callback], do: "#{visibility}" - - # typep is defined as :hidden, since there is no :private visibility value - # but type defined with @doc false also is the stored the same way. - def format_visibility(:hidden, :type), do: "hidden or private" - - # for the rest, it can either be undefined or private - def format_visibility(:undefined, _kind), do: "undefined or private" - def format_visibility(visibility, _kind), do: "#{visibility}" - - defp maybe_append_nested_fragment(nil, _), do: nil - defp maybe_append_nested_fragment(url, []), do: url - defp maybe_append_nested_fragment(url, ["#" <> fragment]), do: url <> "-" <> fragment - - defp append_fragment(url, nil), do: url - defp append_fragment(url, fragment), do: url <> "#" <> fragment -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/cli.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/cli.ex deleted file mode 100644 index 66fa698..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/cli.ex +++ /dev/null @@ -1,275 +0,0 @@ -defmodule ExDoc.CLI do - @moduledoc false - - @doc """ - Handles the command line parsing for the escript. - """ - def main(args, generator \\ &ExDoc.generate/4) do - {:ok, _} = Application.ensure_all_started(:ex_doc) - - {opts, args} = - OptionParser.parse!(args, - aliases: [ - c: :config, - f: :formatter, - l: :logo, - m: :main, - o: :output, - q: :quiet, - u: :source_url, - v: :version - ], - switches: [ - canonical: :string, - config: :string, - favicon: :string, - footer: :boolean, - formatter: [:keep, :string], - homepage_url: :string, - language: :string, - logo: :string, - main: :string, - output: :string, - package: :string, - package: :string, - paths: [:keep, :string], - proglang: :string, - quiet: :boolean, - source_ref: :string, - source_url: :string, - version: :boolean, - warnings_as_errors: :boolean - ] - ) - - if List.keymember?(opts, :version, 0) do - IO.puts("ExDoc v#{ExDoc.version()}") - else - generate(args, opts, generator) - end - end - - defp generate(args, opts, generator) do - [project, version | source_beams] = parse_args(args) - Code.prepend_paths(source_beams) - - for path <- Keyword.get_values(opts, :paths), - path <- Path.wildcard(path) do - Code.prepend_path(path) - app(path) |> Application.load() - end - - # Start all applications with the makeup prefix - for {app, _, _} <- Application.loaded_applications(), - match?("makeup_" <> _, Atom.to_string(app)) do - Application.ensure_all_started(app) - end - - opts = - opts - |> Keyword.put(:apps, Enum.map(source_beams, &app/1)) - |> merge_config() - |> normalize_formatters() - - quiet? = Keyword.get(opts, :quiet, false) - generated_docs = generator.(project, version, source_beams, opts) - - unless quiet? do - Enum.each(generated_docs, fn %{entrypoint: entrypoint, formatter: formatter} -> - extension = formatter_module_to_extension(formatter) - IO.puts(IO.ANSI.format([:green, "View #{extension} docs at #{inspect(entrypoint)}"])) - end) - end - - warned = Enum.filter(generated_docs, & &1.warned?) - - if opts[:warnings_as_errors] == true and warned != [] do - formatters = Enum.map(warned, &formatter_module_to_extension(&1.formatter)) - - format_message = - case formatters do - [formatter] -> "#{formatter} format" - _ -> "#{Enum.join(formatters, ", ")} formats" - end - - message = - "Documents have been generated, but generation for #{format_message} failed " <> - "due to warnings while using the --warnings-as-errors option" - - message_formatted = IO.ANSI.format([:red, message, :reset]) - IO.puts(:stderr, message_formatted) - exit({:shutdown, 1}) - else - Enum.map(generated_docs, & &1.entrypoint) - end - end - - defp formatter_module_to_extension(module) do - module |> Module.split() |> List.last() |> String.downcase() - end - - defp normalize_formatters(opts) do - formatters = - case Keyword.get_values(opts, :formatter) do - [] -> opts[:formatters] || ["html", "markdown", "epub"] - values -> values - end - - opts - |> Keyword.delete(:formatter) - |> Keyword.put(:formatters, formatters) - end - - defp app(source_beam) do - case Path.wildcard(Path.join([source_beam, "*.app"])) do - [path] -> - path |> Path.basename(".app") |> String.to_atom() - - _ -> - raise "cannot find .app file in #{inspect(source_beam)}" - end - end - - defp merge_config(opts) do - case Keyword.fetch(opts, :config) do - {:ok, config} -> - opts_without_config = Keyword.delete(opts, :config) - Keyword.merge(read_config(config), opts_without_config) - - _ -> - opts - end - end - - defp read_config(path) do - case Path.extname(path) do - ".exs" -> - read_config_exs(path) - - ".config" -> - read_config_erl(path) - - other -> - raise "expected config to have .exs or .config extension, got: #{inspect(other)}" - end - end - - defp read_config_exs(path) do - config = File.read!(path) - {result, _} = Code.eval_string(config) - - unless is_list(result) do - raise "expected a keyword list from config file: #{inspect(path)}" - end - - result - end - - defp read_config_erl(path) do - case :file.consult(path) do - {:ok, config} -> - config - - {:error, reason} -> - raise "error parsing #{path}: #{inspect(reason)}" - end - end - - defp parse_args([_project, _version | _source_beams] = args), do: args - - defp parse_args(_) do - IO.puts("Too few arguments.\n") - print_usage() - exit({:shutdown, 1}) - end - - defp print_usage do - IO.puts(~S""" - Usage: - ex_doc PROJECT VERSION BEAMS [OPTIONS] - - Examples: - ex_doc "Ecto" "0.8.0" "_build/dev/lib/ecto/ebin" -u "https://github.com/elixir-ecto/ecto" - ex_doc "Project" "1.0.0" "_build/dev/lib/project/ebin" -c "docs.exs" - - Options: - PROJECT Project name - VERSION Version number - BEAMS Path to compiled beam files - --canonical Indicate the preferred URL with rel="canonical" link element - -c, --config Give configuration through a file instead of a command line. - See "Custom config" section below for more information. - --favicon Path to a favicon image for the project. Must be PNG, JPEG or SVG. The image - will be placed in the output "assets" directory. - -f, --formatter Docs formatter to use (html or epub), default: html and epub - --no-footer Do not render the footer (except for the required "Built with ExDoc" note) - --homepage-url URL to link to for the site name - --language Identify the primary language of the documents, its value must be - a valid [BCP 47](https://tools.ietf.org/html/bcp47) language tag, default: "en" - -l, --logo Path to a logo image for the project. Must be PNG, JPEG or SVG. The image will - be placed in the output "assets" directory. - -m, --main The entry-point page in docs, default: "api-reference" - -o, --output Path to output docs, default: "doc" - --package Hex package name - --paths Prepends the given path to Erlang code path. The path might contain a glob - pattern but in that case, remember to quote it: --paths "_build/dev/lib/*/ebin". - This option can be given multiple times. - --proglang The project's programming language, default: "elixir". - -q, --quiet Only output warnings and errors. - --source-ref Branch/commit/tag used for source link inference, default: "master". - -u, --source-url URL to the source code. - -v, --version Print ExDoc version. - --warnings-as-errors Exit with non-zero status if doc generation produces warnings. - - ## Custom config - - A custom config can be given with the --config option. - - The file must either have ".exs" or ".config" extension. - - The file with the ".exs" extension must be an Elixir script that returns - a keyword list with the same options declares in `ExDoc.generate/4`. - Here is an example: - - [ - extras: Path.wildcard("lib/elixir/pages/*.md"), - groups_for_docs: [ - Guards: & &1[:guard] == true - ], - skip_undefined_reference_warnings_on: ["compatibility-and-deprecations"], - groups_for_modules: [ - ... - ] - ] - - The file with the ".config" extension must contain Erlang terms separated by ".". - See `:file.consult/1` for more information. Here is an example: - - {extras, [<<"README.md">>, <<"CHANGELOG.md">>]}. - {main, <<"readme">>}. - {proglang, erlang}. - - ## Source linking - - ExDoc by default provides links to the source code implementation as - long as --source-url or --source-url-pattern is provided. If you - provide --source-url, ExDoc will inflect the url pattern automatically - for GitHub, GitLab, and Bitbucket URLs. For example: - - --source-url "https://github.com/elixir-ecto/ecto" - - Will be inflected as: - - https://github.com/elixir-ecto/ecto/blob/master/%{path}#L%{line} - - To specify a particular branch or commit, use the `--source-ref` option: - - --source-url "https://github.com/elixir-ecto/ecto" --source-ref "v1.0" - - will result in the following URL pattern: - - https://github.com/elixir-ecto/ecto/blob/v1.0/%{path}#L%{line} - - """) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/config.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/config.ex deleted file mode 100644 index b968b44..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/config.ex +++ /dev/null @@ -1,269 +0,0 @@ -defmodule ExDoc.Config do - # Config used by retriever and extras - @moduledoc false - - # Defaults - @default_source_ref "main" - def default_group_for_doc(metadata), do: metadata[:group] - def filter_modules(_module, _metadata), do: true - def annotations_for_docs(_), do: [] - def source_url_pattern(_, _), do: nil - - defstruct filter_modules: &__MODULE__.filter_modules/2, - groups_for_modules: [], - group_for_doc: &__MODULE__.default_group_for_doc/1, - annotations_for_docs: &__MODULE__.annotations_for_docs/1, - docs_groups: [], - source_ref: @default_source_ref, - groups_for_extras: [], - source_url_pattern: &__MODULE__.source_url_pattern/2, - nest_modules_by_prefix: [], - proglang: :elixir, - markdown_processor: {ExDoc.Markdown.Earmark, []} - - @type t :: %__MODULE__{ - filter_modules: (module, map -> boolean), - groups_for_modules: [{binary(), term()}], - group_for_doc: (keyword() -> String.t() | nil), - annotations_for_docs: (map() -> list()), - docs_groups: [String.t()], - source_ref: nil | String.t(), - groups_for_extras: [{binary(), term()}], - source_url_pattern: (String.t(), integer() -> String.t() | nil), - nest_modules_by_prefix: [String.t()], - proglang: :elixir | :erlang, - markdown_processor: {module(), keyword()} | module() - } - - def build(options) do - proglang = Keyword.get(options, :proglang, :elixir) - filter_modules = Keyword.get(options, :filter_modules, &filter_modules/2) - nest_modules_by_prefix = Keyword.get(options, :nest_modules_by_prefix, []) - - options = - if groups_for_functions = options[:groups_for_functions] do - IO.warn(":groups_for_functions is deprecated, please use :groups_for_docs instead") - Keyword.put_new(options, :groups_for_docs, groups_for_functions) - else - options - end - - apps = Keyword.get(options, :apps, []) - - groups_for_docs = Keyword.get(options, :groups_for_docs, []) - groups_for_extras = Keyword.get(options, :groups_for_extras, []) - - groups_for_modules = - Keyword.get(options, :groups_for_modules, default_groups_for_modules(apps)) - - default_group_for_doc = - Keyword.get(options, :default_group_for_doc, &default_group_for_doc/1) - - source_url_pattern = - Keyword.get_lazy(options, :source_url_pattern, fn -> - guess_url(options[:source_url], options[:source_ref] || @default_source_ref) - end) - - preconfig = %__MODULE__{ - filter_modules: normalize_filter_modules(filter_modules), - docs_groups: for({group, _} <- groups_for_docs, do: to_string(group)), - group_for_doc: normalize_groups_for_docs(groups_for_docs, default_group_for_doc), - groups_for_extras: normalize_groups(groups_for_extras), - groups_for_modules: - normalize_groups( - # TODO: The default module groups must be returned by the language - groups_for_modules ++ - [Deprecated: &deprecated?/1, Exceptions: &exception?/1] - ), - nest_modules_by_prefix: normalize_nest_modules_by_prefix(nest_modules_by_prefix), - proglang: normalize_proglang(proglang), - source_url_pattern: normalize_source_url_pattern(source_url_pattern) - } - - retriever_options = - Keyword.take(options, [ - :annotations_for_docs, - :markdown_processor, - :source_ref - ]) - - struct!(preconfig, retriever_options) - end - - # Helper functions - - defp normalize_proglang(binary) when is_binary(binary) do - binary |> String.to_atom() |> normalize_proglang() - end - - defp normalize_proglang(proglang) when proglang in [:elixir, :erlang] do - proglang - end - - defp normalize_proglang(proglang) do - raise ArgumentError, "#{inspect(proglang)} is not supported" - end - - defp normalize_filter_modules(string) when is_binary(string), - do: normalize_filter_modules(Regex.compile!(string)) - - defp normalize_filter_modules(%Regex{} = regex), - do: fn module, _ -> Atom.to_string(module) =~ regex end - - defp normalize_filter_modules(fun) when is_function(fun, 2), - do: fun - - defp normalize_groups_for_docs(groups, default) do - groups = normalize_groups(groups) - - fn metadata -> - Enum.find_value(groups, fn {group, function} -> - function.(metadata) && group - end) || default.(metadata) - end - end - - defp normalize_groups(groups) do - for {k, v} <- groups, do: {to_string(k), v} - end - - defp normalize_nest_modules_by_prefix(nest_modules_by_prefix) do - nest_modules_by_prefix - |> Enum.map(&inspect_atoms/1) - |> Enum.sort() - |> Enum.reverse() - end - - defp inspect_atoms(atom) when is_atom(atom), do: inspect(atom) - defp inspect_atoms(binary) when is_binary(binary), do: binary - - defp deprecated?(metadata), do: metadata[:deprecated] != nil - defp exception?(metadata), do: metadata[:kind] == :exception - - defp normalize_source_url_pattern(function) when is_function(function, 2), do: function - defp normalize_source_url_pattern(nil), do: &source_url_pattern/2 - - defp normalize_source_url_pattern(binary) when is_binary(binary) do - case :binary.split(binary, "%{path}") do - [left, right] -> - case :binary.split(left, "%{line}") do - [line_left, line_right] -> - fn path, line -> - line_left <> Integer.to_string(line) <> line_right <> path <> right - end - - [_] -> - case :binary.split(right, "%{line}") do - [line_left, line_right] -> - fn path, line -> - left <> path <> line_left <> Integer.to_string(line) <> line_right - end - - [_] -> - fn path, _ -> left <> path <> right end - end - end - - [_] -> - case :binary.split(binary, "%{line}") do - [left, right] -> - fn _, line -> left <> Integer.to_string(line) <> right end - - [_] -> - fn _, _ -> binary end - end - end - end - - defp normalize_source_url_pattern(other) do - raise ArgumentError, - ":source_url_pattern must be a string, a two-arity function or nil, got: #{inspect(other)}" - end - - defp guess_url(url, ref) do - with {:ok, host_with_path} <- http_or_https(url), - {:ok, pattern} <- known_pattern(host_with_path, ref) do - "https://" <> append_slash(host_with_path) <> pattern - else - _ -> url - end - end - - defp http_or_https("http://" <> rest), - do: {:ok, rest} - - defp http_or_https("https://" <> rest), - do: {:ok, rest} - - defp http_or_https(_), - do: :error - - defp known_pattern("github.com/" <> _, ref), - do: {:ok, "blob/#{ref}/%{path}#L%{line}"} - - defp known_pattern("gitlab.com/" <> _, ref), - do: {:ok, "blob/#{ref}/%{path}#L%{line}"} - - defp known_pattern("bitbucket.org/" <> _, ref), - do: {:ok, "src/#{ref}/%{path}#cl-%{line}"} - - defp known_pattern(_host_with_path, _ref), - do: :error - - defp append_slash(url) do - if :binary.last(url) == ?/, do: url, else: url <> "/" - end - - defp default_groups_for_modules([_app]) do - [] - end - - defp default_groups_for_modules(apps) do - Enum.map(apps, fn app -> - Application.load(app) - {app, Application.spec(app, :modules)} - end) - end - - # Group matching helpers - - @doc """ - Finds the index of a given group. - """ - def index(groups, group) do - Enum.find_index(groups, fn {k, _v} -> k == group end) || -1 - end - - @doc """ - Finds a matching group for the given module name, id, and metadata. - """ - def match_module(group_patterns, module, id, metadata) do - match_group_patterns(group_patterns, fn pattern -> - case pattern do - %Regex{} = regex -> Regex.match?(regex, id) - string when is_binary(string) -> id == string - atom when is_atom(atom) -> atom == module - function when is_function(function) -> function.(metadata) - end - end) - end - - @doc """ - Finds a matching group for the given filename or url. - """ - def match_extra(group_patterns, path) do - match_group_patterns(group_patterns, fn pattern -> - case pattern do - %Regex{} = regex -> Regex.match?(regex, path) - string when is_binary(string) -> path == string - end - end) - end - - defp match_group_patterns(group_patterns, matcher) do - Enum.find_value(group_patterns, fn {group, patterns} -> - patterns = List.wrap(patterns) - Enum.any?(patterns, matcher) && group - end) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/doc_ast.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/doc_ast.ex deleted file mode 100644 index c7f7ece..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/doc_ast.ex +++ /dev/null @@ -1,336 +0,0 @@ -defmodule ExDoc.DocAST do - # General helpers for dealing with the documentation AST - # (which is the Markdown -> HTML AST). - @moduledoc false - - @type t :: term() - - alias ExDoc.Markdown - - @doc """ - Parses given `doc_content` according to `doc_format`. - """ - def parse!(doc_content, doc_format, options \\ []) - - def parse!(markdown, "text/markdown", opts) do - parse_markdown(markdown, opts) - end - - def parse!(ast, "application/erlang+html", _options) do - parse_erl_ast(ast) - end - - def parse!(_ast, other, _opts) do - raise "content type #{inspect(other)} is not supported" - end - - # https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-element - @void_elements ~W(area base br col command embed hr img input keygen link - meta param source track wbr)a - - @doc """ - Transform AST into HTML string. - """ - def to_html(binary) do - IO.iodata_to_binary(to_iodata(binary)) - end - - defp to_iodata(binary) when is_binary(binary) do - ExDoc.Utils.h(binary) - end - - defp to_iodata(list) when is_list(list) do - Enum.map(list, &to_iodata/1) - end - - defp to_iodata({:comment, _attrs, inner, _meta}) do - [""] - end - - defp to_iodata({tag, attrs, _inner, _meta}) when tag in @void_elements do - "<#{tag}#{ast_attributes_to_string(attrs)}/>" - end - - defp to_iodata({tag, attrs, inner, %{verbatim: true}}) do - ["<#{tag}#{ast_attributes_to_string(attrs)}>", inner, ""] - end - - defp to_iodata({tag, attrs, inner, _meta}) do - ["<#{tag}#{ast_attributes_to_string(attrs)}>", to_iodata(inner), ""] - end - - defp ast_attributes_to_string(attrs) do - Enum.map(attrs, fn {key, val} -> " #{key}=\"#{ExDoc.Utils.h(val)}\"" end) - end - - ## parse markdown - - defp parse_markdown(markdown, opts) do - Markdown.to_ast(markdown, opts) - end - - ## parse erlang+html - - defp parse_erl_ast(binary) when is_binary(binary) do - binary - end - - defp parse_erl_ast(list) when is_list(list) do - Enum.map(list, &parse_erl_ast/1) - end - - defp parse_erl_ast({:pre, attrs, content}) do - case content do - # if we already have
    ...
    , carry on - [{:code, _, _}] -> - {:pre, attrs, parse_erl_ast(content), %{}} - - # otherwise, turn
    ...
    into
    ...
    - _ -> - content = [{:code, [], parse_erl_ast(content), %{}}] - {:pre, attrs, content, %{}} - end - end - - defp parse_erl_ast({tag, attrs, content}) when is_atom(tag) do - {tag, attrs, parse_erl_ast(content), %{}} - end - - @doc """ - Extracts leading title element from the given AST. - - If found, the title element is stripped from the resulting AST. - """ - def extract_title(ast) - - def extract_title([{:comment, _, _, _} | ast]), do: extract_title(ast) - def extract_title([{:h1, _attrs, inner, _meta} | ast]), do: {:ok, inner, ast} - def extract_title(_ast), do: :error - - @doc """ - Extracts the headers which have anchors (aka ids) in them. - """ - def extract_headers_with_ids(ast, headers) do - ast - |> reduce_tags([], fn {tag, attrs, inner, _}, acc -> - with true <- tag in headers, - id = Keyword.get(attrs, :id, ""), - text = ExDoc.DocAST.text(inner), - true <- id != "" and text != "" do - [{tag, text, id} | acc] - else - _ -> acc - end - end) - |> Enum.reverse() - end - - @doc """ - Adds an id attribute to the given headers. - - A prefix for the id attribute can be given, - which is automatically URL encoded to avoid - issues. - """ - def add_ids_to_headers(doc_ast, headers, prefix \\ "") do - prefix = URI.encode(prefix) - - doc_ast - |> map_reduce_tags(%{}, fn {tag, attrs, inner, meta} = ast, seen -> - if tag in headers and not Keyword.has_key?(attrs, :id) do - possible_id = inner |> text() |> ExDoc.Utils.text_to_id() - id_count = Map.get(seen, possible_id, 0) - partial_id = if id_count >= 1, do: "#{possible_id}-#{id_count}", else: possible_id - seen = Map.put(seen, possible_id, id_count + 1) - {{tag, [id: prefix <> partial_id] ++ attrs, inner, meta}, seen} - else - {ast, seen} - end - end) - |> elem(0) - end - - @doc """ - Compute a synopsis from a document by looking at its first paragraph. - """ - def synopsis({:p, _attrs, [_ | _] = inner, meta}) do - inner = - case Enum.split(inner, -1) do - {pre, [post]} when is_binary(post) -> - pre ++ [String.trim_trailing(post, ":")] - - _ -> - inner - end - - to_html({:p, [], remove_ids(inner), meta}) - end - - def synopsis([{:comment, _, _, _} | rest]), do: synopsis(rest) - def synopsis([head | _]), do: synopsis(head) - def synopsis(_other), do: "" - - defp remove_ids(ast) do - map_tags(ast, fn {tag, attrs, inner, meta} -> - {tag, Keyword.delete(attrs, :id), inner, meta} - end) - end - - @doc """ - Returns text content from the given AST. - """ - def text(ast, joiner \\ "") do - ast - |> do_text(joiner) - |> IO.iodata_to_binary() - |> String.trim() - end - - defp do_text(ast, joiner) when is_list(ast), - do: Enum.map_intersperse(ast, joiner, &do_text(&1, joiner)) - - defp do_text(ast, _joiner) when is_binary(ast), - do: ast - - defp do_text({_tag, _attr, ast, _meta}, joiner), - do: do_text(ast, joiner) - - @doc """ - Wraps a list of HTML nodes into `
    ` tags whenever `headers` returns true. - """ - def sectionize(list, headers), do: sectionize(list, headers, []) - - defp sectionize(list, headers, acc) do - case pivot(list, acc, headers) do - {acc, {header_tag, header_attrs, _, _} = header, rest} -> - {inner, rest} = Enum.split_while(rest, ¬_tag?(&1, header_tag)) - class = String.trim_trailing("#{header_tag} #{header_attrs[:class]}") - section = {:section, [class: class], [header | sectionize(inner, headers, [])], %{}} - sectionize(rest, headers, [section | acc]) - - acc -> - acc - end - end - - defp not_tag?({tag, _, _, _}, tag), do: false - defp not_tag?(_, _tag), do: true - - defp pivot([{tag, _, _, _} = head | tail], acc, headers) do - if tag in headers do - {acc, head, tail} - else - pivot(tail, [head | acc], headers) - end - end - - defp pivot([head | tail], acc, headers), do: pivot(tail, [head | acc], headers) - defp pivot([], acc, _headers), do: Enum.reverse(acc) - - @doc """ - Highlights the code blocks in the AST. - """ - def highlight(ast, language, opts \\ []) do - highlight_info = language.highlight_info() - - map_tags(ast, fn - {:pre, pre_attrs, [{:code, code_attrs, [code], code_meta}], pre_meta} = ast - when is_binary(code) -> - {lang, code_attrs} = Keyword.pop(code_attrs, :class, "") - - case pick_language_and_lexer(lang, highlight_info, code) do - {_lang, nil, _lexer_opts} -> - ast - - {lang, lexer, lexer_opts} -> - try do - Makeup.highlight_inner_html(code, - lexer: lexer, - lexer_options: lexer_opts, - formatter_options: opts - ) - rescue - exception -> - ExDoc.warn( - [ - "crashed while highlighting #{lang} snippet:\n\n", - ExDoc.DocAST.to_html(ast), - "\n\n", - Exception.format_banner(:error, exception, __STACKTRACE__) - ], - __STACKTRACE__ - ) - - ast - else - highlighted -> - code_attrs = [class: "makeup #{lang}", translate: "no"] ++ code_attrs - code_meta = Map.put(code_meta, :verbatim, true) - {:pre, pre_attrs, [{:code, code_attrs, [highlighted], code_meta}], pre_meta} - end - end - - ast -> - ast - end) - end - - defp pick_language_and_lexer("", _highlight_info, "$ " <> _) do - {"shell", ExDoc.ShellLexer, []} - end - - defp pick_language_and_lexer("output", highlight_info, _code) do - {"output", highlight_info.lexer, highlight_info.opts} - end - - defp pick_language_and_lexer("", highlight_info, _code) do - {highlight_info.language_name, highlight_info.lexer, highlight_info.opts} - end - - defp pick_language_and_lexer(lang, _highlight_info, _code) do - case Makeup.Registry.fetch_lexer_by_name(lang) do - {:ok, {lexer, opts}} -> {lang, lexer, opts} - :error -> {lang, nil, []} - end - end - - ## Traversal helpers - - @doc """ - Maps the tags in the AST, first mapping children tags, then the tag itself. - """ - def map_tags({tag, attrs, inner, meta}, fun), - do: fun.({tag, attrs, Enum.map(inner, &map_tags(&1, fun)), meta}) - - def map_tags(list, fun) when is_list(list), - do: Enum.map(list, &map_tags(&1, fun)) - - def map_tags(other, _fun), - do: other - - @doc """ - Reduces the tags in the AST, first reducing children tags, then the tag itself. - """ - def reduce_tags({tag, attrs, inner, meta}, acc, fun), - do: fun.({tag, attrs, inner, meta}, Enum.reduce(inner, acc, &reduce_tags(&1, &2, fun))) - - def reduce_tags(list, acc, fun) when is_list(list), - do: Enum.reduce(list, acc, &reduce_tags(&1, &2, fun)) - - def reduce_tags(_other, acc, _fun), - do: acc - - @doc """ - Map-reduces the tags in the AST, first mapping children tags, then the tag itself. - """ - def map_reduce_tags({tag, attrs, inner, meta}, acc, fun) do - {inner, acc} = Enum.map_reduce(inner, acc, &map_reduce_tags(&1, &2, fun)) - fun.({tag, attrs, inner, meta}, acc) - end - - def map_reduce_tags(list, acc, fun) when is_list(list), - do: Enum.map_reduce(list, acc, &map_reduce_tags(&1, &2, fun)) - - def map_reduce_tags(other, acc, _fun), - do: {other, acc} -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/extras.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/extras.ex deleted file mode 100644 index fdf1228..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/extras.ex +++ /dev/null @@ -1,210 +0,0 @@ -defmodule ExDoc.Extras do - @moduledoc false - - alias ExDoc.{Config, Markdown, Utils} - - @reserved_filenames ~w(404.html search.html) - - @doc """ - Build a list of `ExDoc.ExtraNode` and `ExDoc.URLNode`. - """ - def build(extras_input, config) do - groups = config.groups_for_extras - - extras = - extras_input - |> Enum.map(&build_extra(&1, groups, config)) - - validate_no_reserved_filenames!(extras) - validate_no_duplicate_extras!(extras) - - ids_count = Enum.reduce(extras, %{}, &Map.update(&2, &1.id, 1, fn c -> c + 1 end)) - - extras - |> Enum.map_reduce(1, fn extra, idx -> - if ids_count[extra.id] > 1, do: {disambiguate_id(extra, idx), idx + 1}, else: {extra, idx} - end) - |> elem(0) - |> Enum.sort_by(fn extra -> Config.index(groups, extra.group) end) - end - - defp validate_no_reserved_filenames!(extras) do - conflicts = - extras - |> Enum.filter(&match?(%ExDoc.ExtraNode{}, &1)) - |> Enum.filter(fn extra -> "#{extra.id}.html" in @reserved_filenames end) - - if conflicts != [] do - entries = - Enum.map_join(conflicts, ", ", fn extra -> - "#{inspect(extra.source_path)} would generate #{extra.id}.html" - end) - - raise ArgumentError, - "extra(s) would conflict with built-in ExDoc page(s): #{entries}; " <> - "rename your extra file(s) or use the :filename option to set different output filename(s)" - end - end - - # Detects duplicate ExtraNode entries by checking for collisions on the - # {source_path, id} pair. - # - URLNodes are excluded since they don't produce output files. - # - Two nodes are duplicates when they share both source_path and ID — meaning - # the same file would generate the same output page. - # - Nodes with different source paths or different IDs (e.g. via the - # :filename option) are not duplicates. - # - Nodes from different source files that happen to share an ID are handled - # by disambiguate_id/2 instead. - defp validate_no_duplicate_extras!(extras) do - duplicates = - extras - |> Enum.filter(&match?(%ExDoc.ExtraNode{}, &1)) - |> Enum.frequencies_by(fn extra -> {extra.source_path, extra.id} end) - |> Enum.filter(fn {_, count} -> count > 1 end) - - if duplicates != [] do - entries = Enum.map_join(duplicates, ", ", fn {{source, _}, _} -> inspect(source) end) - - raise ArgumentError, - "duplicate extras: #{entries}" - end - end - - defp disambiguate_id(extra, discriminator) do - Map.put(extra, :id, "#{extra.id}-#{discriminator}") - end - - defp build_extra(input, groups, config) when is_binary(input) do - build_extra({input, %{}}, groups, config) - end - - defp build_extra({input, opts}, groups, config) when is_list(opts) do - build_extra({input, Map.new(opts)}, groups, config) - end - - defp build_extra({input, %{url: _} = input_options}, groups, _config) do - input = to_string(input) - title = validate_extra_string!(input_options, :title) || input - url = validate_extra_string!(input_options, :url) - group = Config.match_extra(groups, url) - - %ExDoc.URLNode{ - group: group, - id: Utils.text_to_id(title), - title: title, - url: url - } - end - - defp build_extra({input, input_options}, groups, config) do - input = to_string(input) - - id = - validate_extra_string!(input_options, :filename) || - input |> filename_to_title() |> Utils.text_to_id() - - source_file = validate_extra_string!(input_options, :source) || input - opts = [file: source_file, line: 1, markdown_processor: config.markdown_processor] - - {extension, source, ast} = - case extension_name(input) do - extension when extension in ["", ".txt"] -> - source = File.read!(input) - ast = [{:pre, [], ["\n" <> source], %{}}] - {extension, source, ast} - - extension when extension in [".md", ".livemd", ".cheatmd"] -> - source = File.read!(input) - - ast = - source - |> Markdown.to_ast(opts) - |> ExDoc.DocAST.add_ids_to_headers([:h2, :h3]) - - {extension, source, ast} - - _ -> - raise ArgumentError, - "file extension not recognized, allowed extension is either .cheatmd, .livemd, .md, .txt or no extension" - end - - {title_doc, title_text, ast} = - case ExDoc.DocAST.extract_title(ast) do - {:ok, title_doc, ast} -> {title_doc, ExDoc.DocAST.text(title_doc), ast} - :error -> {nil, nil, ast} - end - - title = - validate_extra_string!(input_options, :title) || title_text || filename_to_title(input) - - group = Config.match_extra(groups, input) - source_path = source_file |> Path.relative_to(File.cwd!()) |> String.replace_leading("./", "") - source_url = config.source_url_pattern.(source_path, 1) - search_data = validate_search_data!(input_options[:search_data]) - - %ExDoc.ExtraNode{ - type: extra_type(extension), - source_doc: source, - group: group, - id: id, - doc: ast, - source_path: source_path, - source_url: source_url, - search_data: search_data, - title: title, - title_doc: title_doc || title - } - end - - defp validate_extra_string!(input_options, key) do - case input_options[key] do - nil -> - nil - - binary when is_binary(binary) -> - binary - - other -> - raise ArgumentError, - "extra field #{inspect(key)} must be a string, got: #{inspect(other)}" - end - end - - @search_data_keys [:anchor, :body, :title, :type] - - defp validate_search_data!(nil), do: nil - - defp validate_search_data!(search_data) when is_list(search_data) do - Enum.each(search_data, fn search_data -> - has_keys = Map.keys(search_data) - - if Enum.sort(has_keys) != @search_data_keys do - raise ArgumentError, - "expected search data to be a list of maps with the keys: #{inspect(@search_data_keys)}, " <> - "found keys: #{inspect(has_keys)}" - end - end) - - search_data - end - - defp validate_search_data!(search_data) do - raise ArgumentError, - "expected search data to be a list of maps with the keys: #{inspect(@search_data_keys)}, " <> - "found: #{inspect(search_data)}" - end - - defp extension_name(input) do - input - |> Path.extname() - |> String.downcase() - end - - defp filename_to_title(input) do - input |> Path.basename() |> Path.rootname() - end - - defp extra_type(".cheatmd"), do: :cheatmd - defp extra_type(".livemd"), do: :livemd - defp extra_type(_), do: :extra -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter.ex deleted file mode 100644 index f8e2dca..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter.ex +++ /dev/null @@ -1,324 +0,0 @@ -defmodule ExDoc.Formatter do - @moduledoc """ - Specifies the custom formatter API. - """ - - @doc """ - The callback that must be implemented by formatters. - - It receives the configuration, a list of nodes, - and it must return the documentation entrypoint - plus a list of files built inside the output folder. - """ - @callback run( - ExDoc.Formatter.Config.t(), - [ExDoc.ModuleNode.t()], - [ExDoc.ExtraNode.t() | ExDoc.URLNode.t()] - ) :: %{entrypoint: String.t(), build: [String.t()]} - - @doc """ - A list of options to configure autolinking behaviour. - """ - @callback autolink_options() :: [highlight_tag: String.t(), extension: String.t()] - - @optional_callbacks autolink_options: 0 - - @doc false - def run(formatter, formatter_config, module_nodes, filtered_nodes, extras) do - if not Code.ensure_loaded?(formatter) do - raise "formatter module #{inspect(formatter)} not found" - end - - build_file = build_file_path(formatter, formatter_config) - cleanup_build_file(build_file, formatter_config) - - {module_nodes, extras} = - if function_exported?(formatter, :autolink_options, 0) do - autolink_opts = formatter.autolink_options() - autolink(formatter_config, module_nodes, filtered_nodes, extras, autolink_opts) - else - {module_nodes, extras} - end - - %{entrypoint: entrypoint, build: build} = - formatter.run(formatter_config, module_nodes, extras) - - write_build_file(build_file, build) - entrypoint - end - - defp build_file_path(formatter, config) do - extension = - case formatter do - # For backwards compatibility - ExDoc.Formatter.HTML -> - "" - - _ -> - formatter - |> Module.split() - |> List.last() - |> String.downcase() - |> then(&".#{&1}") - end - - Path.join(config.output, ".build#{extension}") - end - - defp cleanup_build_file(build_file, config) do - if File.exists?(build_file) do - build_file - |> File.read!() - |> String.split("\n", trim: true) - |> Enum.map(&Path.join(config.output, &1)) - |> Enum.each(&File.rm/1) - end - end - - defp write_build_file(_build_file, []) do - :ok - end - - defp write_build_file(build_file, files) do - entries = - files - |> Enum.uniq() - |> Enum.sort() - |> Enum.map(&[&1, "\n"]) - - File.mkdir_p!(Path.dirname(build_file)) - File.write!(build_file, entries) - end - - ## Assets/image helpers - - @doc """ - Copy `assets` to the given `output` folder. - """ - def copy_assets(assets, output) do - Enum.flat_map(assets, fn {dir_or_files, relative_target_dir} -> - target_dir = Path.join(output, relative_target_dir) - File.mkdir_p!(target_dir) - - cond do - is_list(dir_or_files) -> - Enum.map(dir_or_files, fn {name, content} -> - target = Path.join(target_dir, name) - File.write(target, content) - Path.relative_to(target, output) - end) - - is_binary(dir_or_files) and File.dir?(dir_or_files) -> - dir_or_files - |> File.cp_r!(target_dir, dereference_symlinks: true) - |> Enum.reduce([], fn path, acc -> - # Omit directories in .build file - if File.dir?(path) do - acc - else - [Path.relative_to(path, output) | acc] - end - end) - |> Enum.reverse() - - is_binary(dir_or_files) -> - [] - - true -> - raise ":assets must be a map of source directories to target directories" - end - end) - end - - @doc """ - Copies the logo to the given location in the output directory. - """ - def copy_logo(%{logo: nil}, _target) do - [] - end - - def copy_logo(%{output: output, logo: logo}, target) do - copy_image(output, logo, target) - end - - @doc """ - Copies the favicon to the given location in the output directory. - """ - def copy_favicon(%{favicon: nil}, _target) do - [] - end - - def copy_favicon(%{output: output, favicon: favicon}, target) do - copy_image(output, favicon, target) - end - - @doc """ - Copies the cover to the given location in the output directory. - """ - def copy_cover(%{cover: nil}, _target) do - [] - end - - def copy_cover(%{output: output, cover: cover}, target) do - copy_image(output, cover, target) - end - - defp copy_image(output, source, target) do - extname = source |> Path.extname() |> String.downcase() - - if extname in ~w(.png .jpg .jpeg .svg) do - filename = "#{target}#{extname}" - target = Path.join(output, filename) - File.mkdir_p!(Path.dirname(target)) - File.copy!(source, target) - [filename] - else - raise ArgumentError, "image format not recognized, allowed formats are: .png, .jpg, .svg" - end - end - - ## Autolinking - - @doc false - def autolink(config, nodes, filtered_nodes, extras, opts) do - {ext, highlight_opts} = Keyword.pop!(opts, :extension) - - language = - case config.proglang do - :erlang -> ExDoc.Language.Erlang - _ -> ExDoc.Language.Elixir - end - - base_config = %ExDoc.Autolink{ - apps: config.apps, - deps: config.deps, - ext: ext, - extras: extra_paths(extras), - skip_undefined_reference_warnings_on: config.skip_undefined_reference_warnings_on, - skip_code_autolink_to: config.skip_code_autolink_to, - filtered_modules: filtered_nodes, - assets: config.assets - } - - extras = - extras - |> Task.async_stream( - &autolink_extra(&1, language, base_config, highlight_opts), - timeout: :infinity - ) - |> Enum.map(fn {:ok, res} -> res end) - - # Render project nodes with autolinked extras - nodes = - nodes - |> Task.async_stream( - &autolink_node(&1, base_config, highlight_opts), - timeout: :infinity - ) - |> Enum.map(&elem(&1, 1)) - - {nodes, extras} - end - - defp autolink_node(node, base_config, highlight_opts) do - language = node.language - - autolink_config = %{ - base_config - | current_module: node.module, - module_id: node.id, - language: language - } - - docs_groups = - for group <- node.docs_groups do - docs = - for child_node <- group.docs do - child_config = %{ - autolink_config - | id: id(node, child_node), - line: child_node.doc_line, - file: child_node.doc_file, - current_kfa: {child_node.type, child_node.name, child_node.arity} - } - - specs = - Enum.map(child_node.source_specs, &language.autolink_spec(&1, child_config)) - - child_node = %{child_node | specs: specs} - autolink_doc(child_node, language, child_config, highlight_opts) - end - - %{autolink_doc(group, language, autolink_config, highlight_opts) | docs: docs} - end - - module_config = %{ - autolink_config - | id: node.id, - file: node.moduledoc_file, - line: node.moduledoc_line - } - - %{ - autolink_doc(node, language, module_config, highlight_opts) - | docs_groups: docs_groups - } - end - - defp autolink_doc(%{doc: nil} = node, _language, _autolink_opts, _opts), - do: node - - defp autolink_doc(%{doc: doc} = node, language, autolink_opts, opts) do - doc = autolink_and_highlight(doc, language, autolink_opts, opts) - %{node | doc: doc} - end - - defp autolink_extra(%ExDoc.URLNode{} = extra, _language, _autolink_opts, _opts), - do: extra - - defp autolink_extra(%ExDoc.ExtraNode{doc: nil} = extra, _language, _autolink_opts, _opts), - do: extra - - defp autolink_extra( - %ExDoc.ExtraNode{doc: doc, source_path: source_path, id: id} = extra, - language, - base_config, - opts - ) do - extra_config = %{base_config | file: source_path, id: id, language: language} - doc = autolink_and_highlight(doc, language, extra_config, opts) - %{extra | doc: doc} - end - - defp id(%{id: mod_id}, %{id: "c:" <> id}) do - "c:" <> mod_id <> "." <> id - end - - defp id(%{id: mod_id}, %{id: "t:" <> id}) do - "t:" <> mod_id <> "." <> id - end - - defp id(%{id: mod_id}, %{id: id}) do - mod_id <> "." <> id - end - - defp autolink_and_highlight(doc, language, autolink_opts, opts) do - doc - |> language.autolink_doc(autolink_opts) - |> ExDoc.DocAST.highlight(language, opts) - end - - defp extra_paths(extras) do - Enum.reduce(extras, %{}, fn - %ExDoc.URLNode{}, acc -> - acc - - %ExDoc.ExtraNode{source_path: source_path, id: id}, acc when is_binary(source_path) -> - base = Path.basename(source_path) - Map.put(acc, base, id) - - _extra, acc -> - acc - end) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/config.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/config.ex deleted file mode 100644 index 284ad1c..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/config.ex +++ /dev/null @@ -1,276 +0,0 @@ -defmodule ExDoc.Formatter.Config do - @moduledoc """ - Configuration used by formatters. - - Contains project metadata, output settings, and various - customization options for generating documentation. - """ - - @doc false - def before_closing_head_tag(_), do: "" - @doc false - def before_closing_footer_tag(_), do: "" - @doc false - def before_closing_body_tag(_), do: "" - @doc false - def skip_undefined_reference_warnings_on(_string), do: false - @doc false - def skip_code_autolink_to(_string), do: false - - defstruct output: "./doc", - project: nil, - version: nil, - main: "api-reference", - api_reference: true, - canonical: nil, - redirects: %{}, - assets: %{}, - logo: nil, - cover: nil, - favicon: nil, - extra_section: "Pages", - footer: true, - formatters: [], - search: [%{name: "Default", help: "In-browser search", url: "search.html?q="}], - homepage_url: nil, - source_url: nil, - language: "en", - proglang: :elixir, - authors: nil, - package: nil, - title: nil, - description: nil, - nest_modules_by_prefix: [], - before_closing_head_tag: &__MODULE__.before_closing_head_tag/1, - before_closing_body_tag: &__MODULE__.before_closing_body_tag/1, - before_closing_footer_tag: &__MODULE__.before_closing_footer_tag/1, - # Autolink fields - apps: [], - deps: [], - skip_undefined_reference_warnings_on: - &__MODULE__.skip_undefined_reference_warnings_on/1, - skip_code_autolink_to: &__MODULE__.skip_code_autolink_to/1 - - @type t :: %__MODULE__{ - output: Path.t(), - project: nil | String.t(), - version: nil | String.t(), - main: nil | String.t(), - api_reference: boolean(), - canonical: nil | String.t(), - redirects: %{optional(String.t()) => String.t()} | [{String.t(), String.t()}], - assets: %{binary() => binary()}, - logo: nil | Path.t(), - cover: nil | Path.t(), - favicon: nil | Path.t(), - extra_section: String.t(), - footer: boolean(), - formatters: [String.t()], - search: [%{name: String.t(), help: String.t(), url: String.t()}], - homepage_url: nil | String.t(), - source_url: nil | String.t(), - language: String.t(), - authors: nil | [String.t()], - package: :atom | nil, - title: nil | String.t(), - description: nil | String.t(), - nest_modules_by_prefix: [String.t()], - before_closing_head_tag: (atom() -> String.t()) | mfa() | map(), - before_closing_body_tag: (atom() -> String.t()) | mfa() | map(), - before_closing_footer_tag: (atom() -> String.t()) | mfa() | map(), - apps: [atom()], - deps: [{ebin_path :: String.t(), doc_url :: String.t()}], - skip_undefined_reference_warnings_on: (String.t() -> boolean), - skip_code_autolink_to: (String.t() -> boolean), - proglang: :elixir | :erlang - } - - @doc false - def build(project, version, options) do - output = Keyword.get(options, :output, "./doc") - nest_modules_by_prefix = Keyword.get(options, :nest_modules_by_prefix, []) - proglang = Keyword.get(options, :proglang, :elixir) - - skip_undefined_reference_warnings_on = - Keyword.get( - options, - :skip_undefined_reference_warnings_on, - &skip_undefined_reference_warnings_on/1 - ) - - skip_code_autolink_to = - Keyword.get(options, :skip_code_autolink_to, &skip_code_autolink_to/1) - - search = Keyword.get(options, :search, []) - - before_closing_head_tag = - Keyword.get(options, :before_closing_head_tag, &before_closing_head_tag/1) - - before_closing_body_tag = - Keyword.get(options, :before_closing_body_tag, &before_closing_body_tag/1) - - before_closing_footer_tag = - Keyword.get(options, :before_closing_footer_tag, &before_closing_footer_tag/1) - - preconfig = %__MODULE__{ - nest_modules_by_prefix: normalize_nest_modules_by_prefix(nest_modules_by_prefix), - output: normalize_output(output), - proglang: normalize_proglang(proglang), - project: project, - search: normalize_search(search), - skip_undefined_reference_warnings_on: - normalize_skip_list_function(skip_undefined_reference_warnings_on), - skip_code_autolink_to: normalize_skip_list_function(skip_code_autolink_to), - before_closing_head_tag: normalize_callback(before_closing_head_tag), - before_closing_body_tag: normalize_callback(before_closing_body_tag), - before_closing_footer_tag: normalize_callback(before_closing_footer_tag), - version: version - } - - formatter_options = - Keyword.take(options, [ - :main, - :api_reference, - :canonical, - :redirects, - :assets, - :logo, - :cover, - :favicon, - :extra_section, - :footer, - :formatters, - :homepage_url, - :source_url, - :language, - :authors, - :package, - :title, - :description, - :apps, - :deps - ]) - - config = struct!(preconfig, formatter_options) - - if not is_map(config.assets) do - raise ArgumentError, - ":assets configuration in ExDoc expects a map from %{source => target}, got: #{config.assets}" - end - - config - end - - # Helper functions - - defp normalize_output(output) do - Path.expand(String.trim_trailing(output, "/")) - end - - defp normalize_proglang(binary) when is_binary(binary) do - binary |> String.to_atom() |> normalize_proglang() - end - - defp normalize_proglang(proglang) when proglang in [:elixir, :erlang] do - proglang - end - - defp normalize_proglang(proglang) do - raise ArgumentError, "#{inspect(proglang)} is not supported" - end - - defp normalize_nest_modules_by_prefix(nest_modules_by_prefix) do - nest_modules_by_prefix - |> Enum.map(&inspect_atoms/1) - |> Enum.sort() - |> Enum.reverse() - end - - defp inspect_atoms(atom) when is_atom(atom), do: inspect(atom) - defp inspect_atoms(binary) when is_binary(binary), do: binary - - defp normalize_skip_list_function(strings) when is_list(strings), - do: &(&1 in strings) - - defp normalize_skip_list_function(fun) when is_function(fun, 1), - do: fun - - defp normalize_callback({m, f, a}) do - fn module -> apply(m, f, [module | a]) end - end - - defp normalize_callback(callback) when is_map(callback) do - fn module -> Map.get(callback, module, "") end - end - - defp normalize_callback(callback) when is_function(callback, 1) do - callback - end - - defp normalize_search([]) do - [%{name: "Default", help: "In-browser search", url: "search.html?q="}] - end - - defp normalize_search(search) when is_list(search) do - Enum.map(search, fn - %{packages: _, url: _} -> - raise ArgumentError, "search must provide either :url or :packages, but not both" - - %{url: url} = engine when not is_binary(url) -> - bad_search!(engine) - - %{name: name, help: help} = engine - when is_binary(name) and is_binary(help) -> - engine - |> Map.delete(:packages) - |> Map.put_new_lazy(:url, fn -> - if packages = engine[:packages] do - "https://hexdocs.pm/?packages=#{normalize_package_search(packages)}&q=" - else - "search.html?q=" - end - end) - - other -> - bad_search!(other) - end) - end - - defp normalize_search(other) do - raise ArgumentError, "search must be a list of maps, got: #{inspect(other)}" - end - - defp normalize_package_search([]) do - raise ArgumentError, ":packages requires a non-empty list" - end - - defp normalize_package_search(packages) do - packages - |> Enum.map_join(",", fn - package when is_atom(package) -> - "#{package}:latest" - - {package, version} when is_atom(package) and is_binary(version) -> - "#{package}:#{version}" - - other -> - raise ArgumentError, - "entries in :packages must be either a package name or a package-version tuple, got: #{inspect(other)}" - end) - |> URI.encode_www_form() - end - - defp bad_search!(other) do - raise ArgumentError, - """ - search entries must be a map with: - - * required :name as string - * required :help as string - * optional :url as a string - * optional :packages as a non-empty list of packages and versions - - got: #{inspect(other)} - """ - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub.ex deleted file mode 100644 index 64cafbf..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub.ex +++ /dev/null @@ -1,165 +0,0 @@ -defmodule ExDoc.Formatter.EPUB do - @moduledoc false - - @mimetype "application/epub+zip" - @assets_dir "OEBPS/assets" - alias __MODULE__.{Assets, Templates} - alias ExDoc.Formatter - - @doc false - def autolink_options do - [extension: ".xhtml", highlight_tag: "samp"] - end - - @doc """ - Generates EPUB documentation for the given modules. - """ - def run(config, project_nodes, extras) when is_map(config) do - config = normalize_config(config) - File.rm_rf!(config.output) - File.mkdir_p!(Path.join(config.output, "OEBPS")) - - static_files = - Formatter.copy_assets(config.assets, Path.join(config.output, "OEBPS")) ++ - Formatter.copy_assets(additional_assets(config), config.output) - - Formatter.copy_logo(config, Path.join(@assets_dir, "logo")) - Formatter.copy_cover(config, Path.join(@assets_dir, "cover")) - - {modules, tasks} = Enum.split_with(project_nodes, &(&1.type != :task)) - generate_content(config, modules, tasks, extras, static_files) - generate_nav(config, modules, tasks, extras) - generate_title(config) - generate_extras(config, extras) - generate_modules(config, modules) - generate_modules(config, tasks) - - {:ok, epub} = generate_epub(config.output) - File.rm_rf!(config.output) - entrypoint = Path.relative_to_cwd(epub) - %{entrypoint: entrypoint, build: [entrypoint]} - end - - defp normalize_config(config) do - output = Path.join(config.output, "#{config.project}") - %{config | output: output} - end - - defp generate_extras(config, extras) do - for %ExDoc.ExtraNode{} = node <- extras do - output = "#{config.output}/OEBPS/#{node.id}.xhtml" - html = Templates.extra_template(config, node) - - if File.regular?(output) do - ExDoc.warn("file #{Path.relative_to_cwd(output)} already exists", []) - end - - File.write!(output, html) - end - end - - defp generate_content(config, modules, tasks, extras, static_files) do - uuid = "urn:uuid:#{uuid4()}" - datetime = format_datetime() - - static_files = - for name <- static_files, - String.contains?(name, "OEBPS"), - media_type = Templates.media_type(Path.extname(name)), - do: {Path.relative_to(name, "OEBPS"), media_type} - - content = - Templates.content_template(config, modules, tasks, extras, uuid, datetime, static_files) - - File.write!("#{config.output}/OEBPS/content.opf", content) - end - - defp generate_nav(config, modules, tasks, extras) do - modules = group_by_group(modules) - tasks = group_by_group(tasks) - extras = group_by_group(extras) - - content = Templates.nav_template(config, modules, tasks, extras) - File.write!("#{config.output}/OEBPS/nav.xhtml", content) - end - - defp group_by_group(nodes) do - nodes - |> Enum.chunk_by(& &1.group) - |> Enum.map(&{hd(&1).group, &1}) - end - - defp generate_title(config) do - content = Templates.title_template(config) - File.write!("#{config.output}/OEBPS/title.xhtml", content) - end - - defp generate_modules(config, nodes) do - nodes - |> Task.async_stream( - fn module_node -> - content = Templates.module_template(config, module_node) - File.write!("#{config.output}/OEBPS/#{module_node.id}.xhtml", content) - end, - timeout: :infinity - ) - |> Enum.map(fn {:ok, result} -> result end) - end - - defp generate_epub(output) do - :zip.create( - String.to_charlist("#{output}.epub"), - [{~c"mimetype", @mimetype} | files_to_add(output)], - compress: ~w[.css .xhtml .html .ncx .js .opf .jpg .png .xml]c - ) - end - - ## Helpers - - defp additional_assets(config) do - [ - {Assets.dist(config.proglang), "OEBPS/dist"}, - {Assets.metainfo(), "META-INF"} - ] - end - - defp files_to_add(path) do - Enum.reduce(Path.wildcard(Path.join(path, "**/*")), [], fn file, acc -> - case File.read(file) do - {:ok, bin} -> - [{file |> Path.relative_to(path) |> String.to_charlist(), bin} | acc] - - {:error, _} -> - acc - end - end) - end - - # Helper to format Erlang datetime tuple - defp format_datetime do - {{year, month, day}, {hour, min, sec}} = :calendar.universal_time() - list = [year, month, day, hour, min, sec] - - "~4..0B-~2..0B-~2..0BT~2..0B:~2..0B:~2..0BZ" - |> :io_lib.format(list) - |> IO.iodata_to_binary() - end - - @two_power_16 65536 - @two_power_32 4_294_967_296 - @two_power_48 281_474_976_710_656 - - defp uuid4 do - Enum.map_join( - [ - <<:rand.uniform(@two_power_32) - 1::32>>, - <<:rand.uniform(@two_power_16) - 1::16>>, - <<:rand.uniform(@two_power_16) - 1::16>>, - <<:rand.uniform(@two_power_16) - 1::16>>, - <<:rand.uniform(@two_power_48) - 1::48>> - ], - <<45>>, - &Base.encode16(&1, case: :lower) - ) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/assets.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/assets.ex deleted file mode 100644 index 88bf4b2..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/assets.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule ExDoc.Formatter.EPUB.Assets do - @moduledoc false - - defmacrop embed_pattern(pattern) do - ["formatters/epub", pattern] - |> Path.join() - |> Path.wildcard() - |> Enum.map(fn path -> - Module.put_attribute(__CALLER__.module, :external_resource, path) - {Path.basename(path), File.read!(path)} - end) - end - - defp dist_js(), do: embed_pattern("dist/*.js") - defp dist_css(:elixir), do: embed_pattern("dist/epub-elixir-*.css") - defp dist_css(:erlang), do: embed_pattern("dist/epub-erlang-*.css") - - ## Assets - - def dist(proglang), do: dist_js() ++ dist_css(proglang) - def metainfo, do: embed_pattern("metainfo/*") - - ## Filenames - - def js_filename(), do: dist_js() |> extract_filename!() - def css_filename(language), do: dist_css(language) |> extract_filename!() - - ## Helpers - - defp extract_filename!([{location, _}]), do: location -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates.ex deleted file mode 100644 index 2bea6d4..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates.ex +++ /dev/null @@ -1,125 +0,0 @@ -defmodule ExDoc.Formatter.EPUB.Templates do - @moduledoc false - - require EEx - - import ExDoc.Utils, only: [h: 1, text_to_id: 1] - - alias ExDoc.Formatter.HTML.Templates, as: H - alias ExDoc.Formatter.EPUB.Assets - - # The actual rendering happens here - defp render_doc(ast), do: ast && ExDoc.DocAST.to_html(ast) - - @doc """ - Generated ID for static file. - """ - def static_file_to_id(static_file) do - static_file |> Path.basename() |> text_to_id() - end - - @doc """ - Creates the Package Document Definition. - - this definition encapsulates the publication metadata and the resource - information that constitute the EPUB publication. This definition also - includes the default reading order. - - See http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-package-def. - """ - EEx.function_from_file( - :def, - :content_template, - Path.expand("templates/content_template.eex", __DIR__), - [:config, :modules, :tasks, :extras, :uuid, :datetime, :static_files], - trim: true - ) - - @doc """ - Creates a chapter which contains all the details about an individual module. - - This chapter can include the following sections: *functions*, *types*, *callbacks*. - """ - EEx.function_from_file( - :def, - :module_template, - Path.expand("templates/module_template.eex", __DIR__), - [:config, :module], - trim: true - ) - - @doc """ - Creates the table of contents. - - This template follows the EPUB Navigation Document Definition. - - See http://www.idpf.org/epub/30/spec/epub30-contentdocs.html#sec-xhtml-nav. - """ - EEx.function_from_file( - :def, - :nav_template, - Path.expand("templates/nav_template.eex", __DIR__), - [:config, :modules, :tasks, :extras], - trim: true - ) - - @doc """ - Creates a new chapter when the user provides additional files. - """ - EEx.function_from_file( - :def, - :extra_template, - Path.expand("templates/extra_template.eex", __DIR__), - [:config, :node], - trim: true - ) - - @doc """ - Creates the cover page for the EPUB document. - """ - EEx.function_from_file( - :def, - :title_template, - Path.expand("templates/title_template.eex", __DIR__), - [:config], - trim: true - ) - - EEx.function_from_file( - :defp, - :head_template, - Path.expand("templates/head_template.eex", __DIR__), - [:config, :title], - trim: true - ) - - EEx.function_from_file( - :defp, - :nav_grouped_item_template, - Path.expand("templates/nav_grouped_item_template.eex", __DIR__), - [:nodes], - trim: true - ) - - EEx.function_from_file( - :defp, - :toc_item_template, - Path.expand("templates/toc_item_template.eex", __DIR__), - [:nodes], - trim: true - ) - - "templates/media-types.txt" - |> Path.expand(__DIR__) - |> File.read!() - |> String.split("\n", trim: true) - |> Enum.each(fn line -> - [extension, media] = String.split(line, ",") - - def media_type("." <> unquote(extension)) do - unquote(media) - end - end) - - def media_type(_arg), do: nil -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/content_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/content_template.eex deleted file mode 100644 index eecaf0d..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/content_template.eex +++ /dev/null @@ -1,46 +0,0 @@ - - - - <%= config.project %> - <%= config.version %> - <%= uuid %> - <%= config.language %> - <%= for {author, index} <- Enum.with_index(config.authors || [], 1) do %> - <%= author %> - <% end %> - <%= datetime %> - <%= if config.cover do %> - - <% end %> - - - - - <%= for extra <- extras do %> - - <% end %> - <%= for node <- modules ++ tasks do %> - - <% end %> - <%= for {static_file, media_type} <- static_files do %> - - <% end %> - <%= if config.cover do %> - - <% end %> - <%= if config.logo do %> - - - - - <%= for extra <- extras do %> - - <% end %> - <%= for node <- modules ++ tasks do %> - - <% end %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/extra_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/extra_template.eex deleted file mode 100644 index ccffe69..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/extra_template.eex +++ /dev/null @@ -1,6 +0,0 @@ -<%= head_template(config, node.title) %> -

    <%= ExDoc.DocAST.to_html(node.title_doc) %>

    - <%= render_doc(node.doc) %> - <%= config.before_closing_body_tag.(:epub) %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/head_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/head_template.eex deleted file mode 100644 index 5f60b6b..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/head_template.eex +++ /dev/null @@ -1,12 +0,0 @@ - - - - - <%= title %> - <%= config.project %> v<%= config.version %> - - - - <%= config.before_closing_head_tag.(:epub) %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/media-types.txt b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/media-types.txt deleted file mode 100644 index 0896b10..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/media-types.txt +++ /dev/null @@ -1,19 +0,0 @@ -gif,image/gif -jpg,image/jpeg -jpeg,image/jpeg -png,image/png -svg,image/svg+xml -xhtml,application/xhtml+xml -html,application/xhtml+xml -ncx,application/x-dtbncx+xml -otf,application/vnd.ms-opentype -ttf,application/vnd.ms-opentype -ttc,application/vnd.ms-opentype -eot,application/vnd.ms-opentype -woff,application/font-woff -opf,application/oebps-package+xml -mp3,audio/mpeg -mp4,video/mp4 -css,text/css -js,text/javascript -license,text/plain diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/module_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/module_template.eex deleted file mode 100644 index cf8b1cc..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/module_template.eex +++ /dev/null @@ -1,40 +0,0 @@ -<%= head_template(config, module.title) %> -

    - <%= H.breakable_module_title(module.title) %> <%= H.module_type(module) %> -

    - - <%= if deprecated = module.deprecated do %> -
    - This <%= module.type %> is deprecated. <%=h deprecated %>. -
    - <% end %> - - <%= if doc = module.doc do %> -
    - <%= render_doc(doc) %> -
    - <% end %> - - <%= if module.docs_groups != [] do %> -
    -

    Summary

    - <%= for group <- module.docs_groups, do: H.summary_template(group.title, group.docs) %> -
    - <% end %> - - <%= for group <- module.docs_groups, key = text_to_id(group.title) do %> -
    -

    <%=h to_string(group.title) %>

    - <%= if doc = group.doc do %> -
    - <%= render_doc(doc) %> -
    - <% end %> -
    - <%= for node <- group.docs, do: H.detail_template(node, module) %> -
    -
    - <% end %> - <%= config.before_closing_body_tag.(:epub) %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex deleted file mode 100644 index 67b1d03..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_grouped_item_template.eex +++ /dev/null @@ -1,13 +0,0 @@ -<%= for {title, nodes} <- nodes do %> - <%= if title do %> -
  • <%=h to_string(title) %> -
      - <% end %> - <%= for node <- nodes do %> -
    1. <%=h node.title %>
    2. - <% end %> - <%= if title do %> -
    -
  • - <% end %> -<% end %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_template.eex deleted file mode 100644 index 87d0d34..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/nav_template.eex +++ /dev/null @@ -1,24 +0,0 @@ -<%= head_template(config, "Table Of Contents") %> -

    Table of contents

    - - <%= config.before_closing_body_tag.(:epub) %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/title_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/title_template.eex deleted file mode 100644 index 1c22350..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/title_template.eex +++ /dev/null @@ -1,15 +0,0 @@ -<%= head_template(config, "Cover") %> -
    - <%= if cover = config.cover do %> -
    - <% else %> -

    <%= config.project %>

    -

    v<%= config.version %>

    - <%= if logo = config.logo do %> -
    Logo
    - <% end %> - <% end %> -
    - <%= config.before_closing_body_tag.(:epub) %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/toc_item_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/toc_item_template.eex deleted file mode 100644 index efba3c6..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/epub/templates/toc_item_template.eex +++ /dev/null @@ -1,8 +0,0 @@ -<%= for node <- nodes do %> - - - <%=URI.encode node.id %> - - - -<% end %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html.ex deleted file mode 100644 index 1413062..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html.ex +++ /dev/null @@ -1,239 +0,0 @@ -defmodule ExDoc.Formatter.HTML do - @moduledoc false - - alias __MODULE__.{Assets, Templates, SearchData} - alias ExDoc.Formatter - - @assets_dir "assets" - - def autolink_options do - [extension: ".html"] - end - - def run(config, project_nodes, extras) when is_map(config) do - if config.main == "index" do - raise ArgumentError, - ~S("main" cannot be set to "index", otherwise it will recursively link to itself) - end - - File.mkdir_p!(config.output) - - static_files = - Formatter.copy_assets(config.assets, config.output) ++ - Formatter.copy_assets(additional_assets(config), config.output) - - search_data = generate_search_data(config, project_nodes, extras) - - {modules, tasks} = Enum.split_with(project_nodes, &(&1.type != :task)) - - all_files = - search_data ++ - static_files ++ - generate_sidebar_items(config, modules, tasks, extras) ++ - generate_api_reference(config, modules, tasks) ++ - generate_extras(config, extras) ++ - Formatter.copy_favicon(config, Path.join(@assets_dir, "favicon")) ++ - Formatter.copy_logo(config, Path.join(@assets_dir, "logo")) ++ - generate_search(config) ++ - generate_not_found(config) ++ - generate_list(config, modules) ++ - generate_list(config, tasks) ++ - generate_redirects(config, ".html") - - entrypoint = config.output |> Path.join("index.html") |> Path.relative_to_cwd() - %{entrypoint: entrypoint, build: all_files} - end - - defp generate_not_found(config) do - filename = "404.html" - config = set_canonical_url(config, filename) - content = Templates.not_found_template(config) - write!(config, filename, content) - [filename] - end - - defp generate_search(config) do - filename = "search.html" - config = set_canonical_url(config, filename) - content = Templates.search_template(config) - write!(config, filename, content) - [filename] - end - - defp generate_sidebar_items(config, modules, tasks, extras) do - content = Templates.create_sidebar_items(config, modules, tasks, extras) - path = "dist/sidebar_items-#{digest(content)}.js" - write!(config, path, content) - [path] - end - - defp generate_search_data(config, linked, extras) do - content = SearchData.create(linked, extras, config.proglang) - path = "dist/search_data-#{digest(content)}.js" - write!(config, path, content) - [path] - end - - defp digest(content) do - content - |> :erlang.md5() - |> Base.encode16(case: :upper) - |> binary_part(0, 8) - end - - defp generate_extras(config, extras) do - generated_extras = - extras - |> Enum.reject(&is_map_key(&1, :url)) - |> with_prev_next() - |> Enum.map(fn {node, prev, next} -> - filename = "#{node.id}.html" - output = Path.join(config.output, filename) - config = set_canonical_url(config, filename) - - refs = %{ - prev: prev && %{path: "#{prev.id}.html", title: prev.title}, - next: next && %{path: "#{next.id}.html", title: next.title} - } - - html = Templates.extra_template(config, node, refs) - - if File.regular?(output) do - ExDoc.warn("file #{Path.relative_to_cwd(output)} already exists", []) - end - - File.write!(output, html) - filename - end) - - generated_extras ++ copy_extras(config, extras) - end - - defp copy_extras(config, extras) do - for %{source_path: source_path, id: id} when source_path != nil <- extras, - ext = extension_name(source_path), - ext == ".livemd" do - output = Path.join(config.output, "#{id}#{ext}") - - File.copy!(source_path, output) - - output - end - end - - defp with_prev_next([]), do: [] - - defp with_prev_next([head | tail]) do - Enum.zip([[head | tail], [nil, head | tail], tail ++ [nil]]) - end - - defp additional_assets(config) do - [ - {Assets.dist(config.proglang), "dist"}, - {Assets.fonts(), "dist"} - ] - end - - defp generate_api_reference(%{api_reference: false}, _modules, _tasks) do - [] - end - - defp generate_api_reference(config, modules, tasks) do - filename = "api-reference.html" - output = Path.join(config.output, filename) - config = set_canonical_url(config, filename) - - html = Templates.api_reference_template(config, modules, tasks) - - if File.regular?(output) do - ExDoc.warn("file #{Path.relative_to_cwd(output)} already exists", []) - end - - File.write!(output, html) - [filename] - end - - defp generate_redirects(config, ext) do - config.redirects - |> Map.new() - |> Map.put_new("index", config.main) - |> Enum.map(fn {from, to} -> - unless is_binary(from), - do: raise("expected a string for the source of a redirect, got: #{inspect(from)}") - - unless is_binary(to), - do: raise("expected a string for the destination of a redirect, got: #{inspect(to)}") - - source = from <> ext - - destination = - case String.split(to, "#", parts: 2) do - [to, anchor] -> to <> ext <> "#" <> anchor - _ -> to <> ext - end - - generate_redirect(config, source, destination) - - source - end) - end - - defp extension_name(input) do - input - |> Path.extname() - |> String.downcase() - end - - defp generate_redirect(config, filename, redirect_to) do - without_anchor = redirect_to |> String.split("#") |> hd() - - unless config.output |> Path.join(without_anchor) |> case_sensitive_file_regular?() do - ExDoc.warn("#{filename} redirects to #{redirect_to}, which does not exist", []) - end - - content = Templates.redirect_template(config, redirect_to) - write!(config, filename, content) - end - - defp case_sensitive_file_regular?(path) do - if File.regular?(path) do - files = path |> Path.dirname() |> File.ls!() - Path.basename(path) in files - else - false - end - end - - defp generate_list(config, nodes) do - nodes - |> Task.async_stream(&generate_module_page(config, &1), timeout: :infinity) - |> Enum.map(&elem(&1, 1)) - end - - defp generate_module_page(config, module_node) do - filename = "#{module_node.id}.html" - config = set_canonical_url(config, filename) - content = Templates.module_template(config, module_node) - write!(config, filename, content) - filename - end - - defp set_canonical_url(config, filename) do - if config.canonical do - canonical_url = - config.canonical - |> String.trim_trailing("/") - |> Kernel.<>("/" <> filename) - - Map.put(config, :canonical, canonical_url) - else - config - end - end - - defp write!(config, filename, content) do - config.output - |> Path.join(filename) - |> File.write!(content) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/assets.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/assets.ex deleted file mode 100644 index 2026b5e..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/assets.ex +++ /dev/null @@ -1,55 +0,0 @@ -defmodule ExDoc.Formatter.HTML.Assets do - @moduledoc false - - defmacrop embed_pattern(pattern) do - ["formatters/html", pattern] - |> Path.join() - |> Path.wildcard() - |> Enum.map(fn path -> - Module.put_attribute(__CALLER__.module, :external_resource, path) - {Path.basename(path), File.read!(path)} - end) - end - - defp dist_js(), do: embed_pattern("dist/html-*.js") - defp dist_inline_js(), do: embed_pattern("dist/inline_html-*.js") - defp dist_css(:elixir), do: embed_pattern("dist/html-elixir-*.css") - defp dist_css(:erlang), do: embed_pattern("dist/html-erlang-*.css") - defp dist_license(), do: embed_pattern("dist/*.LICENSE.txt") - - ## Assets - - def dist(proglang), do: dist_js() ++ dist_css(proglang) ++ dist_license() - def fonts, do: embed_pattern("dist/*.woff2") - - ## Sources - - def inline_js_source(), do: dist_inline_js() |> extract_source!() - - ## Filenames - - def js_filename(), do: dist_js() |> extract_filename!() - def css_filename(language), do: dist_css(language) |> extract_filename!() - - ## Helpers - - @doc """ - Some assets are generated automatically, so we find the revision at runtime. - """ - def rev(output, pattern) do - output = Path.expand(output) - - matches = - output - |> Path.join(pattern) - |> Path.wildcard() - - case matches do - [] -> raise("could not find matching #{output}/#{pattern}") - [asset | _] -> Path.relative_to(asset, output) - end - end - - defp extract_filename!([{location, _}]), do: location - defp extract_source!([{_, source}]), do: source -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/search_data.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/search_data.ex deleted file mode 100644 index 1d519b0..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/search_data.ex +++ /dev/null @@ -1,122 +0,0 @@ -defmodule ExDoc.Formatter.HTML.SearchData do - @moduledoc false - - def create(nodes, extras, proglang) do - items = Enum.flat_map(nodes, &module/1) ++ Enum.flat_map(extras, &extra/1) - - data = %{ - items: items, - content_type: "text/markdown", - proglang: proglang, - producer: %{ - name: "ex_doc", - version: to_string(Application.spec(:ex_doc)[:vsn]) - } - } - - ["searchData=" | ExDoc.Utils.to_json(data)] - end - - defp extra(%ExDoc.URLNode{}), do: [] - - defp extra(%ExDoc.ExtraNode{search_data: search_data} = map) when is_list(search_data) do - Enum.map(search_data, fn item -> - link = - if item.anchor === "" do - "#{URI.encode(map.id)}.html" - else - "#{URI.encode(map.id)}.html##{URI.encode(item.anchor)}" - end - - encode(link, item.title <> " - #{map.id}", item.type, clean_markdown(item.body)) - end) - end - - defp extra(%ExDoc.ExtraNode{} = map) do - page = URI.encode(map.id) <> ".html" - {intro, sections} = extract_sections_from_markdown(map.source_doc, "") - - intro = encode(page, map.title, :extras, intro) - [intro | render_sections(sections, page, map.title, :extras)] - end - - defp module(%ExDoc.ModuleNode{} = node) do - page = URI.encode(node.id) <> ".html" - {intro, sections} = extract_sections(node.source_format, node, "module-") - module = encode(page, node.title, node.type, intro) - - docs = - node.docs_groups - |> Enum.flat_map(& &1.docs) - |> Enum.flat_map(&node_child(&1, node, page)) - - [module] ++ render_sections(sections, page, node.title, node.type) ++ docs - end - - defp node_child(node, module_node, page) do - title = "#{module_node.id}.#{node.name}/#{node.arity}" - {intro, sections} = extract_sections(module_node.source_format, node, node.id <> "-") - - child = encode("#{page}##{URI.encode(node.id)}", title, node.type, intro) - [child | render_sections(sections, page, title, node.type)] - end - - defp encode(ref, title, type, doc) do - %{ref: ref, title: title, type: type, doc: doc} - end - - defp extract_sections("text/markdown", %{source_doc: %{"en" => doc}}, prefix) do - extract_sections_from_markdown(doc, prefix) - end - - defp extract_sections(_format, %{doc: nil}, _prefix) do - {"", []} - end - - defp extract_sections(_format, %{doc: doc}, _prefix) do - {ExDoc.DocAST.text(doc, " "), []} - end - - defp extract_sections_from_markdown(string, prefix) do - [intro | headers_sections] = - Regex.split(~r/(?\b.+)/, string, include_captures: true) - - sections = - headers_sections - |> Enum.chunk_every(2) - |> Enum.map(fn [header, section] -> - # Hardcoded markdown processor because we use it only for header matching - opts = [markdown_processor: ExDoc.Markdown.Earmark] - [{tag, attrs, content, meta}] = ExDoc.Markdown.to_ast(header, opts) - {tag, [section: section] ++ attrs, content, meta} - end) - |> ExDoc.DocAST.add_ids_to_headers([:h2, :h3], prefix) - |> Enum.map(fn {_, attrs, inner, _} -> - id = Keyword.fetch!(attrs, :id) - section = Keyword.fetch!(attrs, :section) - {ExDoc.DocAST.text(inner), id, clean_markdown(section)} - end) - - {clean_markdown(intro), sections} - end - - defp clean_markdown(text) do - text - |> ExDoc.Utils.strip_tags(" ") - |> drop_ignorable_codeblocks() - |> String.trim() - end - - defp render_sections(sections, page, title, type) do - for {header, anchor, body} <- sections do - encode("#{page}##{anchor}", header <> " - " <> title, type, body) - end - end - - @ignored_codeblocks ~w[vega-lite] - - defp drop_ignorable_codeblocks(section) do - block_names = Enum.join(@ignored_codeblocks, "|") - String.replace(section, ~r/^```(?:#{block_names})\n(?:[\s\S]*?)```$/m, "") - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates.ex deleted file mode 100644 index 5c88922..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates.ex +++ /dev/null @@ -1,268 +0,0 @@ -defmodule ExDoc.Formatter.HTML.Templates do - @moduledoc false - require EEx - alias ExDoc.Formatter.HTML.Assets - - import ExDoc.Utils, only: [h: 1, text_to_id: 1] - - defp enc(binary), do: ExDoc.Utils.h(URI.encode(binary)) - - @doc """ - Get the pretty name of a function node - """ - def pretty_type(%{type: t}) do - Atom.to_string(t) - end - - @doc """ - Returns the title with `` after each fullstop, allowing word breaks in long module names. - """ - def breakable_module_title(title) when is_binary(title) do - String.replace(title, ".", ".") - end - - @doc """ - Returns the HTML formatted title for the module page. - """ - def module_type(%{type: :task}), do: "" - def module_type(%{type: :module}), do: "" - def module_type(%{type: type}), do: "#{type}" - - @doc """ - Renders a "Copy Markdown" link if markdown formatter is enabled. - """ - def copy_markdown_link(config, path) do - if "markdown" in config.formatters do - """ - - - Copy Markdown - - """ - else - "" - end - end - - @doc """ - Create a JS object which holds all the items displayed in the sidebar area - """ - def create_sidebar_items(config, modules, tasks, extras) do - nodes = - [modules: modules, tasks: tasks] - |> Enum.map(&sidebar_module/1) - |> Map.new() - |> Map.put(:extras, api_reference(config, modules, tasks) ++ sidebar_extras(extras)) - - ["sidebarNodes=" | ExDoc.Utils.to_json(nodes)] - end - - defp api_reference(%{api_reference: false}, _modules, _tasks), do: [] - - defp api_reference(_config, modules, tasks) do - headers = - if(modules != [], do: [%{id: "Modules", anchor: "modules"}], else: []) ++ - if(tasks != [], do: [%{id: "Mix Tasks", anchor: "tasks"}], else: []) - - [%{id: "api-reference", title: "API Reference", group: "", headers: headers}] - end - - defp sidebar_extras(extras) do - for extra <- extras do - %{id: id, title: title, group: group} = extra - item = %{id: to_string(id), title: to_string(title), group: to_string(group)} - - case extra do - %{search_data: search_data} when is_list(search_data) -> - search_data = - Enum.map(search_data, fn search_item -> - %{ - anchor: search_item.anchor, - id: search_item.title, - labels: [search_item.type] - } - end) - - item - |> Map.put(:headers, headers(extra.doc)) - |> Map.put(:searchData, search_data) - - %{url: url} when is_binary(url) -> - Map.put(item, :url, url) - - _ -> - Map.put(item, :headers, headers(extra.doc)) - end - end - end - - defp sidebar_module({id, modules}) do - modules = - for module <- modules do - groups = - case module.docs_groups do - [] -> [] - entries -> [nodeGroups: Enum.map(entries, &sidebar_entries/1)] - end - - pairs = - for key <- [:id, :title, :nested_title, :nested_context], - value = Map.get(module, key), - do: {key, value} - - others = [ - deprecated: not is_nil(module.deprecated), - sections: headers(module.doc || []), - group: to_string(module.group) - ] - - Map.new(groups ++ pairs ++ others) - end - - {id, modules} - end - - defp sidebar_entries(group) do - nodes = - for node <- group.docs do - id = - if "struct" in node.annotations do - node.signature - else - if node.name == nil do - "nil/#{node.arity}" - else - "#{node.name}/#{node.arity}" - end - end - - deprecated? = not is_nil(node.deprecated) - - %{id: id, title: node.signature, anchor: URI.encode(node.id), deprecated: deprecated?} - end - - %{key: text_to_id(group.title), name: group.title, nodes: nodes} - end - - defp headers(doc) do - doc - |> ExDoc.DocAST.extract_headers_with_ids([:h2]) - |> Enum.map(fn {:h2, text, anchor} -> - %{id: text, anchor: anchor} - end) - end - - defp favicon_path(%{favicon: favicon}), do: "assets/favicon#{Path.extname(favicon)}" - - defp logo_path(%{logo: logo}), do: "assets/logo#{Path.extname(logo)}" - - defp sidebar_type(:exception), do: "modules" - defp sidebar_type(:module), do: "modules" - defp sidebar_type(:behaviour), do: "modules" - defp sidebar_type(:protocol), do: "modules" - defp sidebar_type(:task), do: "tasks" - - defp sidebar_type(:search), do: "search" - defp sidebar_type(:cheatmd), do: "extras" - defp sidebar_type(:livemd), do: "extras" - defp sidebar_type(:extra), do: "extras" - - @section_header_class_name "section-heading" - - @doc """ - Renders the document in the page. - - For now it enriches the document by adding fancy anchors - around h2 and h3 tags with IDs. - """ - def render_doc(nil), do: "" - - def render_doc(ast) do - ast - |> add_fancy_anchors() - |> add_nofollow() - |> ExDoc.DocAST.to_html() - end - - @official_domains ~w(hex.pm hexdocs.pm elixir-lang.org erlang.org) - - defp add_nofollow(ast) do - ExDoc.DocAST.map_tags(ast, fn - {:a, attrs, inner, meta} -> - href = Keyword.get(attrs, :href, "") - - if nofollow?(href) do - {:a, Keyword.put(attrs, :rel, "nofollow"), inner, meta} - else - {:a, attrs, inner, meta} - end - - other -> - other - end) - end - - defp nofollow?(href) do - uri = URI.parse(href) - uri.scheme in ["http", "https"] and not official_host?(uri.host) - end - - defp official_host?(nil), do: false - - defp official_host?(host) do - Enum.any?(@official_domains, fn domain -> - host == domain or String.ends_with?(host, "." <> domain) - end) - end - - defp add_fancy_anchors(ast) do - ExDoc.DocAST.map_tags(ast, fn - {tag, attrs, inner, meta} = ast - when tag in [:h2, :h3] and not is_map_key(meta, :verbatim) -> - if id = Keyword.get(attrs, :id) do - attrs = - Keyword.update( - attrs, - :class, - @section_header_class_name, - &(&1 <> " " <> @section_header_class_name) - ) - - {tag, attrs, - [ - {:a, [href: "##{id}", class: "hover-link"], - [ - {:i, [class: "ri-link-m", "aria-hidden": "true"], [], %{}} - ], %{}}, - {:span, [class: "text"], inner, %{}} - ], meta} - else - ast - end - - ast -> - ast - end) - end - - templates = [ - detail_template: [:node, :module], - footer_template: [:config, :source_path], - head_template: [:config, :title, :noindex], - module_template: [:config, :module], - not_found_template: [:config], - api_reference_entry_template: [:module_node], - api_reference_template: [:config, :modules, :tasks], - extra_template: [:config, :node, :refs], - search_template: [:config], - sidebar_template: [:config, :type], - summary_template: [:name, :nodes], - redirect_template: [:config, :redirect_to] - ] - - Enum.each(templates, fn {name, args} -> - filename = Path.expand("templates/#{name}.eex", __DIR__) - @doc false - EEx.function_from_file(:def, name, filename, args, trim: true) - end) -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex deleted file mode 100644 index 759555a..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_entry_template.eex +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    - <%= breakable_module_title(h module_node.title) %> - <%= if deprecated = module_node.deprecated do %> - deprecated - <% end %> -
    - <%= if doc = module_node.doc do %> -
    <%= ExDoc.DocAST.synopsis(doc) %>
    - <% end %> -
    diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_template.eex deleted file mode 100644 index 4b164a2..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/api_reference_template.eex +++ /dev/null @@ -1,39 +0,0 @@ -<%= head_template(config, "API Reference", false) %> -<%= sidebar_template(config, :extra) %> - -
    -
    -

    API Reference <%= config.project %> v#<%= config.version %>

    - <%= copy_markdown_link(config, "api-reference.md") %> - <%= if config.source_url do %> - - - View Source - - <% end %> -
    - - <%= if modules != [] do %> -
    -

    Modules

    -
    - <%= for module_node <- Enum.sort_by(modules, & &1.id) do - api_reference_entry_template(module_node) - end %> -
    -
    - <% end %> - - <%= if tasks != [] do %> -
    -

    Mix Tasks

    -
    - <%= for task_node <- tasks do - api_reference_entry_template(task_node) - end %> -
    -
    - <% end %> -
    - -<%= footer_template(config, nil) %> \ No newline at end of file diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/detail_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/detail_template.eex deleted file mode 100644 index 7be26d9..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/detail_template.eex +++ /dev/null @@ -1,38 +0,0 @@ -
    - <%= for {default_name, default_arity} <- node.defaults do %> - "> - <% end %> -
    - - - -
    -

    <%=h node.signature %>

    - <%= for annotation <- node.annotations do %> - (<%= annotation %>) - <% end %> - <%= if node.source_url do %> - - - - <% end %> -
    -
    - <%= if deprecated = node.deprecated do %> -
    - This <%= node.type %> is deprecated. <%= h(deprecated) %>. -
    - <% end %> - -
    - <%= if node.specs != [] do %> -
    - <%= for spec <- node.specs do %> -
    <%= module.language.format_spec_attribute(node) %> <%= spec %>
    - <% end %> -
    - <% end %> - - <%= render_doc(node.doc) %> -
    -
    diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/extra_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/extra_template.eex deleted file mode 100644 index 97af056..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/extra_template.eex +++ /dev/null @@ -1,64 +0,0 @@ -<%= head_template(config, node.title, false) %> -<%= sidebar_template(config, node.type) %> - -
    -
    -

    <%= ExDoc.DocAST.to_html(node.title_doc) %>

    - <%= if node.type == :cheatmd do %> - - - Print Cheatsheet - - <% end %> - <%= copy_markdown_link(config, "#{node.id}.md") %> - <%= if node.source_url do %> - - - View Source - - <% end %> -
    - - <%= if node.type == :livemd do %> -
    - - Run in Livebook - -
    - <% end %> - - <%= if node.type == :cheatmd do %> - <%= node.doc |> ExDoc.DocAST.sectionize([:h2, :h3]) |> render_doc() %> - <% else %> - <%= render_doc(node.doc) %> - <% end %> -
    - -
    -
    - <%= if refs.prev do %> - - <% end %> -
    -
    - <%= if refs.next do %> - - <% end %> -
    -
    - -<%= footer_template(config, node.source_path) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/footer_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/footer_template.eex deleted file mode 100644 index 58d4e34..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/footer_template.eex +++ /dev/null @@ -1,58 +0,0 @@ - -
    -
    -
    - <%= config.before_closing_body_tag.(:html) %> - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/head_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/head_template.eex deleted file mode 100644 index 2bac354..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/head_template.eex +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - <%= if config.authors do %> - "> - <% end %> - <%= if noindex do %> - - <% end %> - <%= title %> — <%= config.project %> v<%= config.version %> - <%= if config.favicon do %> - - <% end %> - - <%= if config.canonical do %> - - <% end %> - - - - <%= config.before_closing_head_tag.(:html) %> - - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/module_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/module_template.eex deleted file mode 100644 index bb8008b..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/module_template.eex +++ /dev/null @@ -1,68 +0,0 @@ -<%= head_template(config, module.title, false) %> -<%= sidebar_template(config, module.type) %> - -
    -
    -

    - <%= breakable_module_title(module.title) %> <%= module_type(module) %> - (<%= config.project %> v<%= config.version %>) - <%= for annotation <- module.annotations do %> - (<%= annotation %>) - <% end %> -

    - <%= if module.source_format == "text/markdown" do %> - <%= copy_markdown_link(config, "#{module.id}.md") %> - <% end %> - <%= if module.source_url do %> - - - View Source - - <% end %> -
    - - <%= if deprecated = module.deprecated do %> -
    - This <%= module.type %> is deprecated. <%= h(deprecated) %>. -
    - <% end %> - - <%= if doc = module.doc do %> -
    - <%= render_doc(doc) %> -
    - <% end %> -
    - -<%= if module.docs_groups != [] do %> -
    -

    - - - - Summary -

    - <%= for group <- module.docs_groups, do: summary_template(group.title, group.docs) %> -
    -<% end %> - -<%= for group <- module.docs_groups, key = text_to_id(group.title) do %> -
    -

    - - - - <%= group.title %> -

    - <%= if doc = group.doc do %> -
    - <%= render_doc(doc) %> -
    - <% end %> -
    - <%= for node <- group.docs, do: detail_template(node, module) %> -
    -
    -<% end %> - -<%= footer_template(config, module.source_path) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/not_found_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/not_found_template.eex deleted file mode 100644 index d4503d1..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/not_found_template.eex +++ /dev/null @@ -1,15 +0,0 @@ -<%= head_template(config, "404", true) %> -<%= sidebar_template(config, :extra) %> - -

    - Page not found -

    - -

    Sorry, but the page you were trying to get to, does not exist. You -may want to try searching this site using the sidebar -<%= if config.api_reference do %> - or using our API Reference page -<% end %> -to find what you were looking for.

    - -<%= footer_template(config, nil) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/redirect_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/redirect_template.eex deleted file mode 100644 index a6a9c7c..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/redirect_template.eex +++ /dev/null @@ -1,10 +0,0 @@ - - - - - <%= config.project %> v<%= config.version %> — Documentation - - - - - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/search_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/search_template.eex deleted file mode 100644 index 732b3b5..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/search_template.eex +++ /dev/null @@ -1,12 +0,0 @@ -<%= head_template(config, "Search", true) %> -<%= sidebar_template(config, :search) %> - - - -<%= footer_template(config, nil) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/sidebar_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/sidebar_template.eex deleted file mode 100644 index ba2feab..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/sidebar_template.eex +++ /dev/null @@ -1,72 +0,0 @@ -
    - - - - - - - -
    -
    - diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/summary_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/summary_template.eex deleted file mode 100644 index 5005183..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/html/templates/summary_template.eex +++ /dev/null @@ -1,18 +0,0 @@ -
    -

    - <%= name %> -

    - <%= for node <- nodes do %> -
    -
    - <%=h node.signature %> - <%= if deprecated = node.deprecated do %> - deprecated - <% end %> -
    - <%= if doc = node.doc do %> -
    <%= ExDoc.DocAST.synopsis(doc) %>
    - <% end %> -
    - <% end %> -
    diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown.ex deleted file mode 100644 index 63620c3..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown.ex +++ /dev/null @@ -1,120 +0,0 @@ -defmodule ExDoc.Formatter.MARKDOWN do - @moduledoc false - - alias __MODULE__.Templates - - def run(config, project_nodes, extras) when is_map(config) do - File.mkdir_p!(config.output) - - static_files = - if Enum.all?(config.assets, fn {_k, target_dir} -> - config.output |> Path.join(target_dir) |> File.exists?() - end) do - [] - else - ExDoc.Formatter.copy_assets(config.assets, config.output) - end - - {modules, tasks} = - project_nodes - |> Enum.filter(&(&1.source_format == "text/markdown")) - |> Enum.split_with(&(&1.type != :task)) - - all_files = - generate_nav(config, modules, tasks, extras) ++ - generate_api_reference(config, modules, tasks) ++ - generate_extras(config, extras) ++ - generate_list(config, modules) ++ - generate_list(config, tasks) ++ - static_files - - entrypoint = config.output |> Path.join("llms.txt") |> Path.relative_to_cwd() - %{entrypoint: entrypoint, build: List.flatten(all_files)} - end - - defp normalize_output(output) do - output - |> String.replace("\r\n", "\n") - |> String.replace(~r/\n{3,}/, "\n\n") - end - - defp generate_nav(config, modules, tasks, extras) do - modules = group_by_group(modules) - mix_tasks = group_by_group(tasks) - extras = group_by_group(extras) - - content = - config - |> Templates.llms_txt_template(modules, mix_tasks, extras) - |> normalize_output() - - filename = "llms.txt" - - write!(config, filename, content) - - [filename] - end - - defp generate_api_reference(%{api_reference: false}, _modules, _tasks) do - [] - end - - defp generate_api_reference(config, modules, tasks) do - modules = group_by_group(modules) - mix_tasks = group_by_group(tasks) - - content = - config - |> Templates.api_reference_template(modules, mix_tasks) - |> normalize_output() - - filename = "api-reference.md" - - write!(config, filename, content) - - [filename] - end - - defp group_by_group(nodes) do - nodes - |> Enum.chunk_by(& &1.group) - |> Enum.map(&{hd(&1).group, &1}) - end - - defp generate_extras(config, extras) do - for %ExDoc.ExtraNode{id: id, source_doc: content} <- extras do - filename = "#{id}.md" - - write!(config, filename, content) - - filename - end - end - - defp generate_list(config, nodes) do - nodes - |> Task.async_stream(&generate_module(config, &1), timeout: :infinity) - |> Enum.map(&elem(&1, 1)) - end - - ## Helpers - - defp generate_module(config, module_node) do - content = - config - |> Templates.module_template(module_node) - |> normalize_output() - - filename = "#{module_node.id}.md" - - write!(config, filename, content) - - filename - end - - defp write!(config, filename, content) do - config.output - |> Path.join(filename) - |> File.write!(content) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates.ex deleted file mode 100644 index 556a1a6..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates.ex +++ /dev/null @@ -1,65 +0,0 @@ -defmodule ExDoc.Formatter.MARKDOWN.Templates do - @moduledoc false - - require EEx - import ExDoc.Utils, only: [h: 1] - - @doc """ - Returns the original markdown documentation from source_doc. - """ - def node_doc(%{source_doc: %{"en" => source}}) when is_binary(source), do: source - def node_doc(_), do: nil - - @doc """ - Computes the synopsis based on source_doc. - """ - def synopsis(%{source_doc: %{"en" => source}}) when is_binary(source), - do: - source - |> String.split(["\r\n\r\n", "\n\n"], parts: 2) - |> hd() - |> String.trim_trailing(":") - |> then(&": #{&1}") - - def synopsis(_), do: nil - - EEx.function_from_file( - :def, - :module_template, - Path.expand("templates/module_template.eex", __DIR__), - [:config, :module], - trim: true - ) - - EEx.function_from_file( - :def, - :api_reference_template, - Path.expand("templates/api_reference_template.eex", __DIR__), - [:config, :modules, :mix_tasks], - trim: true - ) - - EEx.function_from_file( - :def, - :llms_txt_template, - Path.expand("templates/llms_txt_template.eex", __DIR__), - [:config, :modules, :mix_tasks, :extras], - trim: true - ) - - EEx.function_from_file( - :defp, - :nav_group_template, - Path.expand("templates/nav_group_template.eex", __DIR__), - [:title, :groups], - trim: true - ) - - EEx.function_from_file( - :defp, - :detail_template, - Path.expand("templates/detail_template.eex", __DIR__), - [:node, :module], - trim: true - ) -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/api_reference_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/api_reference_template.eex deleted file mode 100644 index 80e822a..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/api_reference_template.eex +++ /dev/null @@ -1,3 +0,0 @@ -# <%= config.project %> v<%= config.version %> - API Reference -<%= nav_group_template "Modules", modules %> -<%= nav_group_template "Mix Tasks", mix_tasks %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/detail_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/detail_template.eex deleted file mode 100644 index b19cfa5..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/detail_template.eex +++ /dev/null @@ -1,14 +0,0 @@ -# `<%= node.name %>` -<%= for annotation <- node.annotations do %>*<%= annotation %>* <% end %> - -<%= if deprecated = node.deprecated do %> -> This <%= node.type %> is deprecated. <%= h(deprecated) %>. -<% end %> - -<%= if node.source_specs != [] do %> -```<%= module.language.highlight_info().language_name %> -<%= for spec <- node.source_specs do %><%= module.language.format_spec_attribute(node) %> <%= module.language.format_spec(spec) %> -<% end %>``` -<% end %> - -<%= node_doc(node) %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex deleted file mode 100644 index 42725d4..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/llms_txt_template.eex +++ /dev/null @@ -1,8 +0,0 @@ -# <%= config.project %> v<%= config.version %> - Table of Contents -<%= if config.description do %> -> <%= config.description %> - -<% end %> -<%= nav_group_template config.extra_section, extras %> -<%= nav_group_template "Modules", modules %> -<%= nav_group_template "Mix Tasks", mix_tasks %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/module_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/module_template.eex deleted file mode 100644 index cb2a22a..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/module_template.eex +++ /dev/null @@ -1,16 +0,0 @@ -# `<%=h module.title %>` -<%= if module.source_url do %>[🔗](<%= module.source_url %>)<% end %> - -<%= for annotation <- module.annotations do %>*<%= annotation %>* <% end %> -<%= if deprecated = module.deprecated do %> -> This <%= module.type %> is deprecated. <%=h deprecated %>. -<% end %> -<%= node_doc(module) %> -<%= for group <- module.docs_groups, node <- group.docs do %> -<%= detail_template(node, module) %> -<% end %><%= if config.api_reference do %> - ---- - -*Consult [api-reference.md](api-reference.md) for complete listing* -<% end %> diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/nav_group_template.eex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/nav_group_template.eex deleted file mode 100644 index 8594a3b..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/formatter/markdown/templates/nav_group_template.eex +++ /dev/null @@ -1,14 +0,0 @@ -<%= if groups != [] do %> -## <%= title %> - -<%= for {title, nodes} <- groups do %> -<%= if title do %> -- <%=h to_string(title) %> -<%= for node <- nodes do %> - [<%=h node.title %>](<%= URI.encode node.id %>.md)<%= synopsis(node) %> -<% end %> -<% else %> -<%= for node <- nodes do %>- [<%=h node.title %>](<%= URI.encode node.id %>.md)<%= synopsis(node) %> -<% end %> -<% end %> -<% end %> -<% end %> \ No newline at end of file diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language.ex deleted file mode 100644 index 78c4e30..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language.ex +++ /dev/null @@ -1,170 +0,0 @@ -defmodule ExDoc.Language do - @moduledoc false - - @typedoc """ - The map has the following keys: - - * `:module` - the module - - * `:default_groups` - the default groups used by this module - - * `:docs` - the docs chunk - - * `:language` - the language callback - - * `:id` - module page name - - * `:title` - module display title - - * `:type` - module type - - * `:source_line` - the line where the module code is located, defmodule in Elixir, or -module in Erlang - - * `:source_file` - the source file the module code is located, defmodule in Elixir, or -module in Erlang - - * `:source_basedir` - the absolute directory where the Elixir/Erlang compiler was run. - - * `:nesting_info` - a `{nested_title, nested_context}` tuple or `nil`. - For example, `"A.B.C"` becomes `{"C", "A.B."}`. - - * `:private` - a map with language-specific data - """ - @type module_data() :: %{ - module: module(), - default_groups: [binary()], - docs: tuple(), - language: module(), - id: String.t(), - title: String.t(), - type: atom() | nil, - source_basedir: String.t(), - source_file: String.t() | nil, - source_line: non_neg_integer(), - nesting_info: {String.t(), String.t()} | nil, - private: map() - } - - @doc """ - Returns a map with module information. - """ - @callback module_data(module(), tuple(), ExDoc.Config.t()) :: module_data() | false - - @doc """ - Returns a map with documentation information about a given node or `false`. - - The map has the following keys: - - * `:id_key` - the key used to namespace this entry - - * `:default_group` - the default group this definition falls under - - * `:doc_fallback` - if set, a 0-arity function that returns DocAST which - will be used as fallback to empty docs on the function node - - * `:extra_annotations` - additional annotations - - * `:signature` - the function signature - - * `:source_file` - the source file where the code in located - - * `:source_line` - the line where the code is located, def/defp in Elixir, foo(...) in Erlang - - * `:specs` - a list of specs that will be later formatted by `c:typespec/2` - - * `:type` - the type of the doc (`:function`, `:macro`, `:type`, etc) - - """ - @callback doc_data(entry :: tuple(), module_data()) :: - %{ - id_key: binary(), - default_group: binary(), - doc_fallback: (-> ExDoc.DocAST.t()), - extra_annotations: [String.t()], - signature: [binary()], - source_file: String.t() | nil, - source_line: non_neg_integer() | nil, - specs: [ExDoc.DocNode.spec_ast()], - type: atom() - } - | false - - @doc """ - Autolinks docs. - """ - @callback autolink_doc(doc :: ExDoc.DocAST.t(), config :: ExDoc.Autolink.t()) :: - ExDoc.DocAST.t() - - @doc """ - Autolinks typespecs. - """ - @callback autolink_spec(spec :: term(), config :: ExDoc.Autolink.t()) :: iodata() - - @doc """ - Formats typespecs (without autolinking). - """ - @callback format_spec(spec :: term()) :: iodata() - - @doc """ - Returns information for syntax highlighting. - """ - @callback highlight_info() :: %{ - language_name: String.t(), - lexer: module(), - opts: keyword() - } - - @doc """ - Return an attribute in the canonical representation. - """ - @callback format_spec_attribute(%ExDoc.DocNode{}) :: String.t() - - @doc """ - Parse a module.function string and return it. - """ - @callback parse_module_function(String.t()) :: - {:local, function :: atom()} - | {:remote, module :: module(), function :: atom()} - | :error - - @doc """ - Parse a module string and return it. - """ - @callback parse_module(String.t(), mode :: :regular_link | :custom_link) :: - {:module, atom()} | :error - - @doc """ - Return a URL to autoimported function if atom+arity are autoimported - """ - @callback try_autoimported_function( - name :: atom(), - arity :: non_neg_integer(), - mode :: :regular_link | :custom_link, - config :: ExDoc.Autolink.t(), - original_text :: String.t() - ) :: - nil | String.t() - - @doc """ - Return a URL to built-in type if atom+arity are built-in - """ - @callback try_builtin_type( - name :: atom(), - arity :: non_neg_integer(), - mode :: :regular_link | :custom_link, - config :: ExDoc.Autolink.t(), - original_text :: String.t() - ) :: - nil | String.t() - - def get(:elixir, _module), do: {:ok, ExDoc.Language.Elixir} - def get(:erlang, _module), do: {:ok, ExDoc.Language.Erlang} - - def get(language, module) when is_atom(language) and is_atom(module) do - ExDoc.warn( - "skipping module #{module}, reason: unsupported language (#{language})", - [] - ) - - :error - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/elixir.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/elixir.ex deleted file mode 100644 index 05ab578..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/elixir.ex +++ /dev/null @@ -1,729 +0,0 @@ -defmodule ExDoc.Language.Elixir do - @moduledoc false - - @behaviour ExDoc.Language - - alias ExDoc.Autolink - alias ExDoc.Language.Source - - @impl true - @spec module_data(atom, any, any) :: - false - | %{ - docs: any, - id: binary, - language: ExDoc.Language.Elixir, - source_line: pos_integer, - source_file: Path.t(), - source_basedir: Path.t(), - module: module, - nesting_info: nil, - private: %{ - abst_code: any, - callbacks: map, - impls: map, - optional_callbacks: any, - specs: map - }, - title: binary, - type: :module | :behaviour | :task | :protocol | :implementation | :exception - } - def module_data(module, docs_chunk, config) do - {type, skip} = module_type_and_skip(module) - - cond do - skip -> - false - - abst_code = Source.get_abstract_code(module) -> - title = module_title(module, type) - - source_basedir = Source.fetch_basedir!(abst_code, module) - - {source_file, source_line} = - Source.fetch_module_location!(abst_code, source_basedir, module) - - optional_callbacks = Source.get_optional_callbacks(module, type) - - %{ - module: module, - default_groups: ~w(Types Callbacks Functions), - docs: docs_chunk, - language: __MODULE__, - id: inspect(module), - title: title, - type: type, - source_line: source_line, - source_file: source_file, - source_basedir: source_basedir, - nesting_info: nesting_info(title, config.nest_modules_by_prefix), - private: %{ - abst_code: abst_code, - specs: Source.get_specs(abst_code, source_basedir), - callbacks: Source.get_callbacks(abst_code, source_basedir), - impls: get_impls(module), - optional_callbacks: optional_callbacks - } - } - - true -> - ExDoc.warn( - "skipping docs for module #{inspect(module)}, reason: :no_debug_info", - [] - ) - - false - end - end - - @impl true - def doc_data(entry, %{type: type} = module_data) do - case entry do - {_key, _anno, _sig, :hidden, _metadata} -> - false - - {{_kind, name, _arity}, _anno, _sig, _doc, _metadata} - when name in [:impl_for, :impl_for!] and type == :protocol -> - false - - {{kind, _, _}, _anon, _sig, _doc, _metadata} when kind in [:function, :macro] -> - function_data(entry, module_data) - - {{kind, _, _}, _anon, _sig, _doc, _metadata} - when kind in [:callback, :macrocallback] and type != :protocol -> - callback_data(entry, module_data) - - {{:type, _, _}, _anon, _sig, _doc, _metadata} -> - type_data(entry, module_data) - - _ -> - false - end - end - - defp function_data(entry, module_data) do - {{kind, name, arity}, anno, signature, _doc_content, metadata} = entry - - extra_annotations = - case {kind, name, arity} do - {:macro, _, _} -> ["macro"] - {_, :__struct__, _} -> ["struct"] - _ -> [] - end - - actual_def = actual_def(name, arity, kind) - - %{ - id_key: "", - default_group: "Functions", - doc_fallback: fn -> - impl = Map.fetch(module_data.private.impls, actual_def) - callback_doc_ast(name, arity, impl) || delegate_doc_ast(metadata[:delegate_to]) - end, - extra_annotations: extra_annotations, - signature: signature, - source_file: nil, - source_line: find_function_line(module_data, actual_def) || Source.anno_line(anno), - specs: specs(kind, name, actual_def, module_data), - type: kind - } - end - - defp callback_data(entry, module_data) do - {{kind, name, arity}, anno, _signature, _doc, _metadata} = entry - actual_def = actual_def(name, arity, kind) - - extra_annotations = - if actual_def in module_data.private.optional_callbacks, do: ["optional"], else: [] - - {anno, specs} = - case module_data.private.callbacks do - %{^actual_def => {:attribute, anno, :callback, {_name, specs}}} -> - {anno, - if kind == :macrocallback do - Enum.map(specs, &remove_callback_term/1) - else - specs - end} - - %{} -> - {anno, []} - end - - line = Source.anno_line(anno) - quoted = Enum.map(specs, &Code.Typespec.spec_to_quoted(name, &1)) - signature = [get_typespec_signature(hd(quoted), arity)] - - %{ - id_key: "c:", - default_group: "Callbacks", - doc_fallback: fn -> nil end, - extra_annotations: extra_annotations, - signature: signature, - source_file: nil, - source_line: line, - specs: quoted, - type: kind - } - end - - defp remove_callback_term({:type, num, :bounded_fun, [lhs, rhs]}) do - {:type, num, :bounded_fun, [remove_callback_term(lhs), rhs]} - end - - defp remove_callback_term({:type, num, :fun, [{:type, num, :product, [_ | rest_args]} | rest]}) do - {:type, num, :fun, [{:type, num, :product, rest_args} | rest]} - end - - defp type_data(entry, module_data) do - {{_kind, name, arity}, _anno, _signature, _doc, _metadata} = entry - - %{type: type, spec: spec, source_file: source, source_line: line} = - Source.fetch_type!(module_data, name, arity) - - quoted = spec |> Code.Typespec.type_to_quoted() |> process_type_ast(type) - signature = [get_typespec_signature(quoted, arity)] - - %{ - id_key: "t:", - default_group: "Types", - doc_fallback: fn -> nil end, - extra_annotations: [], - source_file: source, - source_line: line, - signature: signature, - specs: [quoted], - type: type - } - end - - @autoimported_modules [Kernel, Kernel.SpecialForms] - - @impl true - def try_autoimported_function(name, arity, mode, %Autolink{} = config, original_text) do - Enum.find_value(@autoimported_modules, fn module -> - Autolink.remote_url({:function, module, name, arity}, config, original_text, - warn?: false, - mode: mode - ) - end) - end - - @basic_types [ - any: 0, - none: 0, - atom: 0, - map: 0, - pid: 0, - port: 0, - reference: 0, - struct: 0, - tuple: 0, - float: 0, - integer: 0, - neg_integer: 0, - non_neg_integer: 0, - pos_integer: 0, - list: 1, - nonempty_list: 1, - maybe_improper_list: 2, - nonempty_improper_list: 2, - nonempty_maybe_improper_list: 2 - ] - - @built_in_types [ - term: 0, - arity: 0, - as_boolean: 1, - binary: 0, - bitstring: 0, - boolean: 0, - byte: 0, - char: 0, - charlist: 0, - nonempty_charlist: 0, - fun: 0, - function: 0, - identifier: 0, - iodata: 0, - iolist: 0, - keyword: 0, - keyword: 1, - list: 0, - nonempty_list: 0, - maybe_improper_list: 0, - nonempty_maybe_improper_list: 0, - mfa: 0, - module: 0, - no_return: 0, - node: 0, - number: 0, - struct: 0, - timeout: 0 - ] - - @impl true - def try_builtin_type(name, arity, _mode, %Autolink{} = config, _original_text) - when {name, arity} in @basic_types do - Autolink.ex_doc_app_url(Kernel, config, "typespecs", config.ext, "#basic-types") - end - - def try_builtin_type(name, arity, _mode, %Autolink{} = config, _original_text) - when {name, arity} in @built_in_types do - Autolink.ex_doc_app_url(Kernel, config, "typespecs", config.ext, "#built-in-types") - end - - def try_builtin_type(_name, _arity, _mode, %Autolink{}, _original_text) do - nil - end - - @impl true - def parse_module_function(string) do - case string |> String.split(".") |> Enum.reverse() do - [string] -> - with {:function, function} <- parse_function(string) do - {:local, function} - end - - ["", "", ""] -> - {:local, :..} - - ["//", "", ""] -> - {:local, :..//} - - ["", ""] -> - {:local, :.} - - ["", "", "" | rest] -> - module_string = rest |> Enum.reverse() |> Enum.join(".") - - with {:module, module} <- parse_module(module_string, :custom_link) do - {:remote, module, :..} - end - - ["", "" | rest] -> - module_string = rest |> Enum.reverse() |> Enum.join(".") - - with {:module, module} <- parse_module(module_string, :custom_link) do - {:remote, module, :.} - end - - [function_string | rest] -> - module_string = rest |> Enum.reverse() |> Enum.join(".") - - with {:module, module} <- parse_module(module_string, :custom_link), - {:function, function} <- parse_function(function_string) do - {:remote, module, function} - end - end - end - - # There are special forms that are forbidden by the tokenizer - defp parse_function("__aliases__"), do: {:function, :__aliases__} - defp parse_function("__block__"), do: {:function, :__block__} - defp parse_function("%"), do: {:function, :%} - - defp parse_function(string) do - case Code.string_to_quoted("& #{string}/0", warnings: false) do - {:ok, {:&, _, [{:/, _, [{function, _, _}, 0]}]}} when is_atom(function) -> - {:function, function} - - _ -> - :error - end - end - - @impl true - def parse_module(<> <> _ = string, _mode) when first in ?A..?Z do - if string =~ ~r/^[A-Za-z0-9_.]+$/ do - do_parse_module(string) - else - :error - end - end - - def parse_module(":" <> _ = string, :custom_link) do - do_parse_module(string) - end - - def parse_module(_, _) do - :error - end - - defp do_parse_module(string) do - case Code.string_to_quoted(string, warn_on_unnecessary_quotes: false, emit_warnings: false) do - {:ok, module} when is_atom(module) -> - {:module, module} - - {:ok, {:__aliases__, _, parts}} -> - if Enum.all?(parts, &is_atom/1) do - {:module, Module.concat(parts)} - else - :error - end - - _ -> - :error - end - end - - @impl true - def autolink_doc(ast, %Autolink{} = config) do - true = config.language == __MODULE__ - - config = %{config | force_module_prefix: false} - walk_doc(ast, config) - end - - @impl true - def format_spec(ast) do - ast - |> Macro.to_string() - |> safe_format_string!() - |> ExDoc.Utils.h() - end - - @impl true - def autolink_spec(ast, %Autolink{} = config) do - string = format_spec(ast) - name = typespec_name(ast) - {name, rest} = split_name(string, name) - name <> autolink_typespec(rest, config) - end - - @impl true - def highlight_info() do - %{ - language_name: "elixir", - lexer: Makeup.Lexers.ElixirLexer, - opts: [] - } - end - - @impl true - def format_spec_attribute(%{type: :type}), do: "@type" - def format_spec_attribute(%{type: :opaque}), do: "@opaque" - def format_spec_attribute(%{type: :nominal}), do: "@nominal" - def format_spec_attribute(%{type: :callback}), do: "@callback" - def format_spec_attribute(%{type: :macrocallback}), do: "@macrocallback" - def format_spec_attribute(%{}), do: "@spec" - - ## Module Helpers - - defp nesting_info(title, prefixes) do - prefixes - |> Enum.find(&String.starts_with?(title, &1 <> ".")) - |> case do - nil -> nil - prefix -> {"." <> String.trim_leading(title, prefix <> "."), prefix} - end - end - - defp module_type_and_skip(module) do - cond do - function_exported?(module, :__info__, 1) and - Enum.any?(module.__info__(:struct) || [], &(&1.field == :__exception__)) -> - {:exception, false} - - function_exported?(module, :__protocol__, 1) -> - {:protocol, false} - - function_exported?(module, :__impl__, 1) -> - {:impl, true} - - match?("Elixir.Mix.Tasks." <> _, Atom.to_string(module)) -> - {:task, false} - - function_exported?(module, :behaviour_info, 1) -> - {:behaviour, false} - - true -> - {:module, false} - end - end - - defp module_title(module, :task), do: "mix " <> task_name(module) - defp module_title(module, _), do: inspect(module) - - defp task_name(module) do - "Elixir.Mix.Tasks." <> name = Atom.to_string(module) - name |> String.split(".") |> Enum.map_join(".", &Macro.underscore/1) - end - - def get_impls(module) do - for behaviour <- behaviours_implemented_by(module), - abstract_code = Source.get_abstract_code(behaviour), - {callback, _} <- Source.get_callbacks(abstract_code, ""), - do: {callback, behaviour}, - into: %{} - end - - defp behaviours_implemented_by(module) do - for {:behaviour, list} <- module.module_info(:attributes), - behaviour <- list, - do: behaviour - end - - ## Helpers - - defp specs(kind, name, actual_def, module_data) do - specs = - module_data.private.specs - |> Map.get(actual_def) - |> then(fn - {:attribute, _anno, :spec, {_name, type}} -> type - nil -> [] - end) - |> Enum.map(&Code.Typespec.spec_to_quoted(name, &1)) - - if kind == :macro do - Enum.map(specs, &remove_first_macro_arg/1) - else - specs - end - end - - defp actual_def(name, arity, :macrocallback) do - {String.to_atom("MACRO-" <> to_string(name)), arity + 1} - end - - defp actual_def(name, arity, :macro) do - {String.to_atom("MACRO-" <> to_string(name)), arity + 1} - end - - defp actual_def(name, arity, _), do: {name, arity} - - defp remove_first_macro_arg({:"::", info, [{name, info2, [_term_arg | rest_args]}, return]}) do - {:"::", info, [{name, info2, rest_args}, return]} - end - - defp remove_first_macro_arg({:when, meta, [lhs, rhs]}) do - {:when, meta, [remove_first_macro_arg(lhs), rhs]} - end - - defp delegate_doc_ast({m, f, a}) do - [ - {:p, [], - ["See ", {:code, [class: "inline"], [Exception.format_mfa(m, f, a)], %{line: 1}}, "."], - %{}} - ] - end - - defp delegate_doc_ast(nil) do - nil - end - - defp callback_doc_ast(name, arity, {:ok, behaviour}) do - [ - {:p, [], - [ - "Callback implementation for ", - {:code, [class: "inline"], ["c:#{inspect(behaviour)}.#{name}/#{arity}"], %{}}, - "." - ], %{}} - ] - end - - defp callback_doc_ast(_, _, _) do - nil - end - - defp find_function_line(module_data, na) do - {_source, line} = Source.fetch_function_location!(module_data, na) - line - end - - defp get_typespec_signature({:when, _, [{:"::", _, [{name, meta, args}, _]}, _]}, arity) do - Macro.to_string({name, meta, strip_types(args, arity)}) - end - - defp get_typespec_signature({:"::", _, [{name, meta, args}, _]}, arity) do - Macro.to_string({name, meta, strip_types(args, arity)}) - end - - defp get_typespec_signature({name, meta, args}, arity) do - Macro.to_string({name, meta, strip_types(args, arity)}) - end - - defp strip_types(args, arity) do - args - |> Enum.take(-arity) - |> Enum.with_index(1) - |> Enum.map(fn - {{:"::", _, [left, _]}, position} -> to_var(left, position) - {{:|, _, _}, position} -> to_var({}, position) - {left, position} -> to_var(left, position) - end) - |> Macro.prewalk(fn node -> Macro.update_meta(node, &Keyword.delete(&1, :line)) end) - end - - defp to_var({:%, meta, [name, _]}, _), do: {:%, meta, [name, {:%{}, meta, []}]} - defp to_var({:%{}, _, _}, _), do: {:map, [], nil} - defp to_var({:<<>>, _, _}, _), do: {:binary, [], nil} - defp to_var({:{}, _, _}, _), do: {:tuple, [], nil} - defp to_var({name, meta, _}, _) when is_atom(name), do: {name, meta, nil} - - defp to_var({{:., meta, [_module, name]}, _, _args}, _) when is_atom(name), - do: {name, meta, nil} - - defp to_var([{:->, _, _} | _], _), do: {:function, [], nil} - defp to_var({_, _}, _), do: {:tuple, [], nil} - defp to_var(integer, _) when is_integer(integer), do: {:integer, [], nil} - defp to_var(float, _) when is_float(float), do: {:float, [], nil} - defp to_var(list, _) when is_list(list), do: {:list, [], nil} - defp to_var(atom, _) when is_atom(atom), do: {:atom, [], nil} - defp to_var(_, position), do: {:"arg#{position}", [], nil} - - # Cut off the body of an opaque type while leaving it on a normal type. - defp process_type_ast({:"::", _, [d | _]}, :opaque), do: d - defp process_type_ast(ast, _), do: ast - - defp walk_doc(list, config) when is_list(list) do - Enum.map(list, &walk_doc(&1, config)) - end - - defp walk_doc(binary, _) when is_binary(binary) do - binary - end - - defp walk_doc({:pre, _, _, _} = ast, _config) do - ast - end - - defp walk_doc({:a, attrs, inner, meta} = ast, config) do - case Autolink.custom_link(attrs, config) do - url when is_binary(url) -> - {:a, Keyword.put(attrs, :href, url), inner, meta} - - :remove_link -> - remove_link(ast) - - nil -> - ast - end - end - - defp walk_doc({:code, attrs, [code], meta} = ast, config) do - config = %{config | line: meta[:line]} - - case Autolink.url(code, :regular_link, config) do - url when is_binary(url) -> - code = - code - |> remove_prefix() - |> remove_fragment() - - {:a, [href: url], [{:code, attrs, [code], meta}], %{}} - - :remove_link -> - {:code, attrs, [code], meta} - - nil -> - ast - end - end - - defp walk_doc({tag, attrs, ast, meta}, config) do - {tag, attrs, List.flatten(walk_doc(ast, config)), meta} - end - - defp remove_link({:a, _attrs, inner, _meta}) do - inner - end - - defp remove_prefix("c:" <> rest), do: rest - defp remove_prefix("t:" <> rest), do: rest - defp remove_prefix("m:" <> rest), do: rest - defp remove_prefix(rest), do: rest - - defp remove_fragment(string) do - string |> String.split("#") |> hd() - end - - defp safe_format_string!(string) do - try do - string - |> Code.format_string!(line_length: 80) - |> IO.iodata_to_binary() - rescue - _ -> string - end - end - - defp typespec_name({:"::", _, [{name, _, _}, _]}), do: Atom.to_string(name) - defp typespec_name({:when, _, [left, _]}), do: typespec_name(left) - defp typespec_name({name, _, _}) when is_atom(name), do: Atom.to_string(name) - - # extract out function name so we don't process it. This is to avoid linking it when there's - # a type with the same name - defp split_name(string, name) do - if String.starts_with?(string, name) do - {name, binary_part(string, byte_size(name), byte_size(string) - byte_size(name))} - else - {"", string} - end - end - - defp autolink_typespec(string, config) do - regex = ~r{ - ( # - (?: - ( # - (?: - \:[a-z][_a-zA-Z0-9]* # Erlang module - )| - (?: - [A-Z][_a-zA-Z0-9]* # Elixir module - (?:\.[A-Z][_a-zA-Z0-9]*)* # Elixir submodule - ) - ) # - \. # Dot operator - )? - ([a-z_][_a-zA-Z0-9]*[\?\!]?) # Name - ) # - (\(.*\)) # Arguments - }x - - Regex.replace(regex, string, fn _all, call_string, module_string, name_string, rest -> - module = string_to_module(module_string) - name = String.to_atom(name_string) - arity = count_args(rest, 0, 0) - original_text = call_string <> "()" - - url = - if module do - Autolink.remote_url({:type, module, name, arity}, config, original_text) - else - Autolink.local_url(:type, name, arity, config, original_text) - end - - if url do - ~s[#{ExDoc.Utils.h(call_string)}] - else - call_string - end <> autolink_typespec(rest, config) - end) - end - - defp string_to_module(""), do: nil - - defp string_to_module(string) do - if String.starts_with?(string, ":") do - string |> String.trim_leading(":") |> String.to_atom() - else - Module.concat([string]) - end - end - - defp count_args("()" <> _, 0, 0), do: 0 - defp count_args("(" <> rest, counter, acc), do: count_args(rest, counter + 1, acc) - defp count_args("[" <> rest, counter, acc), do: count_args(rest, counter + 1, acc) - defp count_args("{" <> rest, counter, acc), do: count_args(rest, counter + 1, acc) - defp count_args(")" <> _, 1, acc), do: acc + 1 - defp count_args(")" <> rest, counter, acc), do: count_args(rest, counter - 1, acc) - defp count_args("]" <> rest, counter, acc), do: count_args(rest, counter - 1, acc) - defp count_args("}" <> rest, counter, acc), do: count_args(rest, counter - 1, acc) - defp count_args("," <> rest, 1, acc), do: count_args(rest, 1, acc + 1) - defp count_args(<<_>> <> rest, counter, acc), do: count_args(rest, counter, acc) - defp count_args("", _counter, acc), do: acc -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/erlang.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/erlang.ex deleted file mode 100644 index 2ad0ff5..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/erlang.ex +++ /dev/null @@ -1,736 +0,0 @@ -defmodule ExDoc.Language.Erlang do - @moduledoc false - - @behaviour ExDoc.Language - - alias ExDoc.Language.Source - alias ExDoc.{Autolink, Refs} - - @impl true - @spec module_data(atom, any, any) :: - false - | %{ - docs: any, - id: binary, - language: ExDoc.Language.Erlang, - source_line: pos_integer, - source_file: Path.t(), - source_basedir: Path.t(), - module: module, - nesting_info: nil, - private: %{abst_code: any, callbacks: map, optional_callbacks: any, specs: map}, - title: binary, - type: :module | :behaviour - } - def module_data(module, docs_chunk, _config) do - if abst_code = Source.get_abstract_code(module) do - id = Atom.to_string(module) - source_basedir = Source.fetch_basedir!(abst_code, module) - - {source_file, source_line} = - Source.fetch_module_location!(abst_code, source_basedir, module) - - type = module_type(module) - - %{ - module: module, - default_groups: ~w(Types Callbacks Functions), - docs: docs_chunk, - language: __MODULE__, - id: id, - title: id, - type: type, - source_line: source_line, - source_file: source_file, - source_basedir: source_basedir, - nesting_info: nil, - private: %{ - abst_code: abst_code, - specs: Source.get_specs(abst_code, source_basedir), - callbacks: Source.get_callbacks(abst_code, source_basedir), - optional_callbacks: Source.get_optional_callbacks(module, type) - } - } - else - ExDoc.warn("skipping docs for module #{inspect(module)}, reason: :no_debug_info", []) - false - end - end - - @impl true - def doc_data(entry, module_data) do - {{kind, name, arity}, anno, signature, doc_content, metadata} = entry - - cond do - doc_content == :hidden -> - false - - # Edoc on Erlang/OTP24.1+ includes private functions in - # the chunk, so we manually yank them out. - kind == :function and function_exported?(module_data.module, name, arity) -> - function_data(name, arity, signature, metadata, module_data) - - kind == :callback -> - callback_data(name, arity, anno, signature, metadata, module_data) - - kind == :type -> - type_data(name, arity, signature, metadata, module_data) - - true -> - false - end - end - - defp function_data(name, arity, signature, metadata, module_data) do - specs = - case Map.fetch(module_data.private.specs, {name, arity}) do - {:ok, spec} -> - [spec] - - :error -> - case Map.fetch(module_data.private.specs, {module_data.module, name, arity}) do - {:ok, spec} -> - [spec] - - :error -> - [] - end - end - - {file, line} = Source.fetch_function_location!(module_data, {name, arity}) - - %{ - id_key: "", - default_group: "Functions", - doc_fallback: fn -> equiv_data(module_data.module, file, line, metadata) end, - extra_annotations: [], - signature: signature, - source_file: file, - source_line: line, - specs: specs, - type: :function - } - end - - defp callback_data(name, arity, anno, signature, metadata, module_data) do - extra_annotations = - if {name, arity} in module_data.private.optional_callbacks, do: ["optional"], else: [] - - {specs, anno} = - case Map.fetch(module_data.private.callbacks, {name, arity}) do - {:ok, spec} -> - {[spec], elem(spec, 1)} - - :error -> - {[], anno} - end - - file = Source.anno_file(anno) - line = Source.anno_line(anno) - - %{ - id_key: "c:", - default_group: "Callbacks", - doc_fallback: fn -> equiv_data(module_data.module, file, line, metadata, "c:") end, - extra_annotations: extra_annotations, - signature: signature, - source_file: file, - source_line: line, - specs: specs, - type: :callback - } - end - - defp type_data(name, arity, signature, metadata, module_data) do - %{attr: attr, source_file: file, source_line: line, type: type} = - Source.fetch_type!(module_data, name, arity) - - %{ - id_key: "t:", - default_group: "Types", - doc_fallback: fn -> equiv_data(module_data.module, file, line, metadata, "t:") end, - extra_annotations: [], - signature: signature, - source_file: file, - source_line: line, - specs: [attr], - type: type - } - end - - defp equiv_data(module, file, line, metadata, prefix \\ "") do - case metadata[:equiv] do - nil -> - nil - - equiv when is_binary(equiv) -> - ## We try to parse the equiv in order to link to the target - with {:ok, toks, _} <- :erl_scan.string(:unicode.characters_to_list(equiv <> ".")), - {:ok, [{:call, _, {:atom, _, func}, args}]} <- :erl_parse.parse_exprs(toks) do - equivalent_to( - {:a, [href: "`#{prefix}#{func}/#{length(args)}`"], - [{:code, [class: "inline"], [equiv], %{}}], %{}} - ) - else - {:ok, [{:op, _, :/, {:atom, _, _}, {:integer, _, _}}]} -> - equivalent_to({:code, [class: "inline"], ["#{prefix}#{equiv}"], %{}}) - - _ -> - equivalent_to({:code, [class: "inline"], [equiv], %{}}) - end - - equiv -> - ExDoc.warn("invalid equiv #{inspect(equiv)}", - file: file, - line: line, - module: module - ) - - nil - end - end - - defp equivalent_to(node) do - [{:p, [], ["Equivalent to ", node, "."], %{}}] - end - - @impl true - def autolink_doc(ast, %Autolink{} = config) do - true = config.language == __MODULE__ - - config = %{config | force_module_prefix: true} - walk_doc(ast, config) - end - - @impl true - def autolink_spec(nil, _opts) do - nil - end - - def autolink_spec(ast, %Autolink{} = config) do - {name, anno, quoted} = - case ast do - {:attribute, anno, kind, {mfa, ast}} when kind in [:spec, :callback] -> - {mn, name} = - case mfa do - {name, _} -> {name, name} - {module, name, _} -> {{module, name}, name} - end - - {mn, anno, Enum.map(ast, &Code.Typespec.spec_to_quoted(name, &1))} - - {:attribute, anno, kind, ast} when kind in [:type, :opaque, :nominal] -> - {name, _, _} = ast - {name, anno, Code.Typespec.type_to_quoted(ast)} - end - - formatted = format_spec(ast) - config = %{config | file: Source.anno_file(anno), line: Source.anno_line(anno)} - autolink_spec(quoted, name, formatted, config) - end - - @impl true - def highlight_info() do - %{ - language_name: "erlang", - lexer: Makeup.Lexers.ErlangLexer, - opts: [] - } - end - - @impl true - def format_spec_attribute(%{type: :type}), do: "-type" - def format_spec_attribute(%{type: :opaque}), do: "-opaque" - def format_spec_attribute(%{type: :nominal}), do: "-nominal" - def format_spec_attribute(%{type: :callback}), do: "-callback" - def format_spec_attribute(%{}), do: "-spec" - - ## Autolink - - defp walk_doc(list, config) when is_list(list) do - Enum.map(list, &walk_doc(&1, config)) - end - - defp walk_doc(binary, _) when is_binary(binary) do - binary - end - - defp walk_doc({:pre, _, _, _} = ast, _config) do - ast - end - - defp walk_doc({:code, attrs, [code], meta} = ast, config) when is_binary(code) do - config = %{config | line: meta[:line]} - - case Autolink.url(code, :regular_link, config) do - url when is_binary(url) -> - code = - code - |> remove_prefix() - |> remove_fragment() - - {:a, [href: url], [{:code, attrs, [code], meta}], %{}} - - _ -> - ast - end - end - - defp walk_doc({:a, attrs, inner, meta} = ast, config) do - case attrs[:rel] do - "https://erlang.org/doc/link/seeerl" -> - {fragment, url} = extract_fragment(attrs[:href] || "", "#") - - case String.split(url, ":") do - [module] -> - walk_doc({:a, [href: "`m:#{maybe_quote(module)}#{fragment}`"], inner, meta}, config) - - [app, module] -> - inner = strip_app(inner, app) - walk_doc({:a, [href: "`m:#{maybe_quote(module)}#{fragment}`"], inner, meta}, config) - - _ -> - warn_ref(attrs[:href], config) - inner - end - - "https://erlang.org/doc/link/seemfa" -> - {prefix, url} = - case String.split(attrs[:href], "Module:") do - [url] -> - {"", url} - - [left, right] -> - {"c:", left <> right} - end - - {mfa, inner} = - case String.split(url, ":") do - [mfa] -> - {mfa, inner} - - [app, mfa] -> - {mfa, strip_app(inner, app)} - end - - walk_doc({:a, [href: "`#{prefix}#{fixup(mfa)}`"], inner, meta}, config) - - "https://erlang.org/doc/link/seetype" -> - {type, inner} = - case String.split(attrs[:href], ":") do - [type] -> - {type, inner} - - [app, type] -> - {type, strip_app(inner, app)} - end - - type = - case String.split(type, "(") do - [type] -> - type - - [type, _] -> - type <> "/0" - end - - walk_doc({:a, [href: "`t:#{fixup(type)}`"], inner, meta}, config) - - "https://erlang.org/doc/link/" <> see -> - warn_ref(attrs[:href] <> " (#{see})", %{config | id: nil}) - inner - - _ -> - case Autolink.custom_link(attrs, config) do - :remove_link -> - remove_link(ast) - - nil -> - ast - - url -> - {:a, Keyword.put(attrs, :href, url), inner, meta} - end - end - end - - defp walk_doc({tag, attrs, ast, meta}, config) do - {tag, attrs, walk_doc(ast, config), meta} - end - - defp remove_link({:a, _attrs, inner, _meta}) do - inner - end - - defp remove_prefix("c:" <> rest), do: rest - defp remove_prefix("m:" <> rest), do: rest - defp remove_prefix("t:" <> rest), do: rest - defp remove_prefix("\\" <> rest), do: rest - defp remove_prefix(rest), do: rest - - defp remove_fragment(string) do - string |> String.split("#") |> hd() - end - - defp extract_fragment(url, prefix) do - case String.split(url, "#", parts: 2) do - [url] -> {"", url} - [url, fragment] -> {prefix <> fragment, url} - end - end - - defp fixup(mfa) do - {m, fa} = - case String.split(mfa, "#") do - ["", mfa] -> - {"", mfa} - - [m, fa] -> - {"#{maybe_quote(m)}:", fa} - end - - [f, a] = String.split(fa, "/") - m <> maybe_quote(f) <> "/" <> a - end - - defp maybe_quote(m) do - to_string(:io_lib.write_atom(String.to_atom(m))) - end - - defp strip_app([{:code, attrs, [code], meta}], app) do - [{:code, attrs, List.wrap(strip_app(code, app)), meta}] - end - - defp strip_app(code, app) when is_binary(code) do - List.wrap(String.trim_leading(code, "//#{app}/")) - end - - defp strip_app(other, _app) do - List.wrap(other) - end - - defp warn_ref(href, config) do - message = "invalid reference: #{href}" - nil = config.id - Autolink.maybe_warn(config, message, nil, %{}) - end - - defp final_url({kind, name, arity}, _config) do - Autolink.fragment(kind, name, arity) - end - - defp final_url({kind, module, name, arity}, config) do - tool = Autolink.tool(module, config) - Autolink.app_module_url(tool, module, Autolink.fragment(kind, name, arity), config) - end - - @impl true - def parse_module_function(string) do - case String.split(string, ":") do - [module_string, function_string] -> - with {:module, module} <- parse_module(module_string, :custom_link), - {:function, function} <- parse_function(function_string) do - {:remote, module, function} - end - - [function_string] -> - with {:function, function} <- parse_function(function_string) do - {:local, function} - end - - _ -> - :error - end - end - - defp parse_function(string) do - with {:ok, toks, _} <- :erl_scan.string(String.to_charlist("fun #{string}/0.")), - {:ok, [{:fun, _, {:function, name, _arity}}]} <- :erl_parse.parse_exprs(toks) do - {:function, name} - else - _ -> - :error - end - end - - @impl true - def try_autoimported_function(name, arity, mode, %Autolink{} = config, original_text) do - if :erl_internal.bif(name, arity) do - Autolink.remote_url({:function, :erlang, name, arity}, config, original_text, - warn?: false, - mode: mode - ) - end - end - - @impl true - def try_builtin_type(name, arity, mode, %Autolink{} = config, original_text) do - if :erl_internal.is_type(name, arity) do - Autolink.remote_url({:type, :erlang, name, arity}, config, original_text, - warn?: false, - mode: mode - ) - end - end - - @impl true - def parse_module(string, _mode) do - case :erl_scan.string(String.to_charlist(string)) do - {:ok, [{:atom, _, module}], _} when is_atom(module) -> - {:module, module} - - _ -> - :error - end - end - - @impl true - def format_spec(ast) do - {:attribute, _, type, _} = ast - - # `-type ` => 6 - offset = byte_size(Atom.to_string(type)) + 2 - - options = [linewidth: 98 + offset] - - spec = - :erl_pp.attribute(ast, options) - |> IO.chardata_to_string() - |> String.trim() - |> String.trim_leading("-#{Atom.to_string(type)} ") - - if type == :opaque do - String.replace(spec, ~r/ ::.*$/s, "") - else - spec - end - end - - # Traverses quoted and formatted string of the typespec AST, replacing refs with links. - # - # Let's say we have this typespec: - # - # -spec f(X) -> #{atom() => bar(), integer() => X}. - # - # We traverse the AST and find types and their string representations: - # - # -spec f(X) -> #{atom() => bar(), integer() => X}. - # ^^^^ ^^^ ^^^^^^^ - # - # atom/0 => atom - # bar/0 => bar - # integer/0 => integer - # - # We then traverse the formatted string, *in order*, replacing the type strings with links: - # - # "atom(" => "atom(" - # "bar(" => "bar(" - # "integer(" => "integer(" - # - # Finally we end up with: - # - # -spec f(X) -> #{atom() => bar(), integer() => X}. - # - # All of this hassle is to preserve the original *text layout* of the initial representation, - # all the spaces, newlines, etc. - defp autolink_spec(quoted, name, formatted, config) do - acc = - for quoted <- List.wrap(quoted) do - {_quoted, acc} = - Macro.prewalk(quoted, [], fn - # module.name(args) - {{:., _, [module, name]}, _, args}, acc -> - {{:t, [], args}, [{pp({module, name}), {module, name, length(args)}} | acc]} - - {name, _, _}, acc when name in [:<<>>, :..] -> - {nil, acc} - - # -1, +1 - {op, _, [int]}, acc when is_integer(int) and op in [:+, :-] -> - {nil, acc} - - # fun() (spec_to_quoted expands it to (... -> any() in Elixir v1.17 and earlier) - # TODO: Remove me when we require Elixir v1.18+ - {:->, _, [[{name, _, _}], {:any, _, _}]} = node, acc when name == :... -> - if Version.match?(System.version(), ">= 1.18.0-rc") do - {node, acc} - else - {nil, acc} - end - - # record{type :: remote:type/arity} - {:field_type, _, [name, {{:., _, [r_mod, r_type]}, _, args}]}, acc -> - {{name, [], args}, [{pp({r_mod, r_type}), {r_mod, r_type, length(args)}} | acc]} - - # #{x :: t()} - {:field_type, _, [name, type]}, acc when is_atom(name) -> - {[type], acc} - - {name, _, args} = ast, acc when is_atom(name) and is_list(args) -> - arity = length(args) - - cond do - name == :record and args != [] and acc != [] -> - {ast, acc} - - name in [:"::", :when, :%{}, :{}, :|, :->, :..., :fun] -> - {ast, acc} - - # %{required(...) => ..., optional(...) => ...} - name in [:required, :optional] and arity == 1 -> - {ast, acc} - - # name(args) - true -> - {ast, [{pp(name), {name, arity}} | acc]} - end - - other, acc -> - {other, acc} - end) - - acc - |> Enum.reverse() - # drop the name of the typespec - |> Enum.drop(1) - end - |> Enum.concat() - - put_stack(acc) - - # Drop and re-add type name (it, the first element in acc, is dropped there too) - # - # 1. foo() :: bar() - # 2. () :: bar() - # 3. () :: bar() - # 4. foo() :: bar() - name = pp(name) - formatted = trim_name(formatted, name) - formatted = replace(formatted, acc, config) - name <> formatted - end - - defp trim_name(string, name) do - name_size = byte_size(name) - binary_part(string, name_size, byte_size(string) - name_size) - end - - defp replace(formatted, [], _config) do - formatted - end - - defp replace(formatted, acc, config) do - String.replace(formatted, Enum.map(acc, &"#{elem(&1, 0)}("), fn string -> - string = String.trim_trailing(string, "(") - - ref = - case get_stack() do - [{^string, ref} | tail] -> - put_stack(tail) - ref - - _ -> - Autolink.maybe_warn( - config, - "internal inconsistency when processing #{inspect(formatted)}", - nil, - nil - ) - end - - what = - case config.current_kfa do - {:function, _, _} -> :spec - {kind, _, _} -> kind - end - - url = - case ref do - {name, arity} -> - ref = {:type, config.current_module, name, arity} - visibility = Refs.get_visibility(ref) - - cond do - config.skip_code_autolink_to.("t:#{name}/#{arity}") -> - nil - - visibility in [:public] -> - final_url({:type, name, arity}, config) - - :erl_internal.is_type(name, arity) -> - final_url({:type, :erlang, name, arity}, config) - - true -> - Autolink.maybe_warn( - config, - "#{what} references type \"#{name}/#{arity}\" but it is " <> - Autolink.format_visibility(visibility, :type), - nil, - nil - ) - - nil - end - - {module, name, arity} -> - ref = {:type, module, name, arity} - visibility = Refs.get_visibility(ref) - - cond do - config.skip_code_autolink_to.("t:#{module}:#{name}/#{arity}") -> - nil - - visibility in [:public] -> - final_url(ref, config) - - true -> - Autolink.maybe_warn( - config, - "#{what} references type \"#{module}:#{name}/#{arity}\" but it is " <> - Autolink.format_visibility(visibility, :type), - nil, - nil - ) - - nil - end - end - - if url do - ~s|#{string}(| - else - string <> "(" - end - end) - end - - defp put_stack(items) do - Process.put({__MODULE__, :stack}, items) - end - - defp get_stack() do - Process.get({__MODULE__, :stack}) - end - - defp pp(:fun), do: "fun" - - defp pp(name) when is_atom(name) do - :io_lib.format("~p", [name]) |> IO.iodata_to_binary() - end - - defp pp({module, name}) when is_atom(module) and is_atom(name) do - :io_lib.format("~p:~p", [module, name]) |> IO.iodata_to_binary() - end - - ## Helpers - - defp module_type(module) do - cond do - function_exported?(module, :behaviour_info, 1) -> - :behaviour - - true -> - :module - end - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/source.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/source.ex deleted file mode 100644 index de29702..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/language/source.ex +++ /dev/null @@ -1,275 +0,0 @@ -defmodule ExDoc.Language.Source do - # Shared helpers used by both Erlang and Elixir. - @moduledoc false - - def anno_line(line) when is_integer(line), do: abs(line) - def anno_line(anno), do: anno |> :erl_anno.line() |> abs() - - def anno_file(anno) do - case :erl_anno.file(anno) do - :undefined -> - nil - - file -> - String.Chars.to_string(file) - end - end - - @doc """ - Get abstract code and basedir for a module - - The abstract code has been traversed so that all records in types - have had their fields in-lined. - """ - def get_abstract_code(module) do - with {^module, binary, _file} <- :code.get_object_code(module), - {:ok, {_, [{:abstract_code, {_vsn, abst_code}}]}} <- - :beam_lib.chunks(binary, [:abstract_code]) do - expand_records_in_types(abst_code) - else - _ -> nil - end - end - - defp expand_records_in_types(abst_code) do - # Find all records in ast and collect any fields with type declarations - records = - filtermap_ast(abst_code, nil, fn - {:attribute, anno, :record, {name, fields}} -> - {name, - fields - |> Enum.flat_map(fn - {:typed_record_field, record_field, type} -> - [{:type, anno, :field_type, [elem(record_field, 2), type]}] - - record_field when elem(record_field, 0) == :record_field -> - [{:type, anno, :field_type, [elem(record_field, 2), {:type, anno, :term, []}]}] - - _ -> - [] - end)} - - _ -> - nil - end) - |> Map.new() - - # Expand records in all specs, callbacks, types, opaques and nominals - filtermap_ast(abst_code, nil, fn - {:attribute, anno, kind, {mfa, ast}} when kind in [:spec, :callback] -> - ast = Enum.map(ast, &expand_records(&1, records)) - {:attribute, anno, kind, {mfa, ast}} - - {:attribute, anno, type, {name, ast, args}} when type in [:opaque, :nominal, :type] -> - {:attribute, anno, type, {name, expand_records(ast, records), args}} - - otherwise -> - otherwise - end) - end - - defp expand_records(types, records) when is_list(types) do - Enum.map(types, &expand_records(&1, records)) - end - - defp expand_records({:ann_type, anno, [name, type]}, records) do - {:ann_type, anno, [name, expand_records(type, records)]} - end - - # When we encounter a record, we fetch the type definitions in the record and - # merge then with the type. If there are duplicates we take the one in the type - # declaration - defp expand_records({:type, anno, :record, [{:atom, _, record} = name | args]}, records) do - args = - (args ++ Map.get(records, record, [])) - |> Enum.uniq_by(fn {:type, _, :field_type, [{:atom, _, name} | _]} -> name end) - - # We delete the record from the map so that recursive - # record definitions are not expanded. - records = Map.delete(records, record) - - {:type, anno, :record, expand_records([name | args], records)} - end - - defp expand_records({type, anno, what, args}, records) when type in [:type, :user_type] do - {type, anno, what, expand_records(args, records)} - end - - defp expand_records({:remote_type, anno, [m, t, args]}, records) do - {:remote_type, anno, [m, t, expand_records(args, records)]} - end - - defp expand_records(otherwise, _records) do - otherwise - end - - @doc """ - Fetches the basedir of a module. - - The basedir is the cwd of the Elixir/Erlang compiler when compiling the module. - All `-file` attributes in the module is relative to this directory. - """ - def fetch_basedir!(abst_code, module) do - # We look for the first -file attribute to see what the source file that - # was compiled is called. Both Erlang and Elixir places one at the top. - filename = - Enum.find_value(abst_code, fn - {:attribute, _anno, :file, {filename, _line}} -> - filename - - _ -> - nil - end) || raise "could not find base directory for #{inspect(module)}" - - # The first -file attribute will be either relative or absolute - # depending on whether the compiler was called with an absolute - # or relative path. - if Path.type(filename) == :relative do - # If the compiler was called with a relative path, then any other - # relative -file attribute will be relative to the same directory. - # We use `module_info(:compile)[:source]` to get an absolute path - # to the source file and calculate the basedir from that - compile_source = - cond do - source = module.module_info(:compile)[:source] -> - source - - module in :erlang.pre_loaded() -> - {:ok, {_, [compile_info: compile_info]}} = - :beam_lib.chunks( - Application.app_dir(:erts, "ebin/#{module}.beam") - |> String.to_charlist(), - [:compile_info] - ) - - compile_info[:source] - - true -> - # This happens only when building docs for Makeup itself, - # as some of its modules may have been loaded from the escript. - filename - end - - compile_source - |> String.Chars.to_string() - |> Path.absname() - |> Path.split() - |> Enum.drop(Path.split(filename) |> Enum.count() |> Kernel.*(-1)) - |> Path.join() - else - # If an absolute path was used, then any relative -file attribute - # is relative to the directory of the source file - Path.dirname(filename) - end - end - - def fetch_module_location!(abst_code, source_basedir, module) do - find_ast(abst_code, source_basedir, fn - {:attribute, anno, :module, ^module} -> - {anno_file(anno), anno_line(anno)} - - _ -> - nil - end) || raise "could not find module definition for #{inspect(module)}" - end - - def fetch_function_location!(module_data, {name, arity}) do - find_ast(module_data.private.abst_code, module_data.source_basedir, fn - {:function, anno, ^name, ^arity, _} -> {anno_file(anno), anno_line(anno)} - _ -> nil - end) || raise "could not find function definition for #{name}/#{arity}" - end - - # Returns a map of {name, arity} => spec. - def get_specs(abst_code, source_basedir) do - filtermap_ast(abst_code, source_basedir, fn - {:attribute, _anno, :spec, {name, _types}} = spec -> - {name, spec} - - _ -> - nil - end) - |> Map.new() - end - - def fetch_type!(module_data, name, arity) do - find_ast(module_data.private.abst_code, module_data.source_basedir, fn - {:attribute, anno, type, {^name, _, args} = spec} = attr -> - if type in [:nominal, :opaque, :type] and length(args) == arity do - %{ - type: type, - spec: spec, - attr: attr, - source_file: anno_file(anno), - source_line: anno_line(anno) - } - end - - _ -> - nil - end) || raise "could not find type definition for #{name}/#{arity}" - end - - def get_callbacks(abst_code, source_basedir) do - filtermap_ast(abst_code, source_basedir, fn - {:attribute, _anno, :callback, {name, _types}} = callback -> - {name, callback} - - _ -> - nil - end) - |> Map.new() - end - - def get_optional_callbacks(module, :behaviour) do - module.behaviour_info(:optional_callbacks) - rescue - FunctionClauseError -> [] - end - - def get_optional_callbacks(_module, _type), do: [] - - defp find_ast(ast, source_basedir, fun) do - filtermap_ast(ast, source_basedir, fun) |> List.first() - end - - # The file which a form belongs to is decided by the previous :file - # attribute in the AST. The :file can be either relative, or absolute - # depending on how the file was included. So when traversing the AST - # we need to keep track of the :file attributes and update the anno - # with the correct file. - defp filtermap_ast(ast, source_basedir, fun) do - Enum.reduce(ast, {nil, []}, fn - {:attribute, _anno, :file, {filename, _line}} = entry, {_file, acc} -> - {if Path.type(filename) == :relative && source_basedir do - Path.join(source_basedir, filename) - else - filename - end, - if entry = fun.(entry) do - [entry | acc] - else - acc - end} - - entry, {file, acc} -> - anno = - if file && source_basedir do - :erl_anno.set_file(file, elem(entry, 1)) - else - elem(entry, 1) - end - - if entry = fun.(put_elem(entry, 1, anno)) do - {file, [entry | acc]} - else - {file, acc} - end - - _, file_acc -> - file_acc - end) - |> elem(1) - |> Enum.reverse() - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown.ex deleted file mode 100644 index 73f5cec..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown.ex +++ /dev/null @@ -1,54 +0,0 @@ -defmodule ExDoc.Markdown do - @moduledoc """ - Adapter behaviour and conveniences for converting Markdown to HTML. - - ExDoc is compatible with any markdown processor that implements the - functions defined in this module. The markdown processor can be changed - via the `:markdown_processor` option. Note that doing such change is global. - - ExDoc supports the following Markdown parsers out of the box: - - * [EarmarkParser](https://github.com/robertdober/earmark_parser) - - ExDoc uses EarmarkParser by default. - """ - - @doc """ - Converts markdown into HTML. - """ - @callback to_ast(String.t(), Keyword.t()) :: term() - - @doc """ - Returns true if all dependencies necessary are available. - """ - @callback available?() :: boolean() - - @doc """ - Converts the given markdown document to HTML AST. - - ## Options - - * `:markdown_processor` - The markdown processor to use, - either as a module or a `{module, keyword}` tuple - - All other options are passed through to the markdown processor. - """ - def to_ast(text, opts \\ []) when is_binary(text) do - {processor_pair, options} = Keyword.pop!(opts, :markdown_processor) - - {processor, options} = - case processor_pair do - {processor, processor_options} when is_atom(processor) and is_list(processor_options) -> - {processor, Keyword.merge(processor_options, options)} - - processor when is_atom(processor) and processor != nil -> - {processor, options} - - _ -> - raise ArgumentError, - ":markdown_processor must be either `Mod` or `{Mod, options}`, got: #{inspect(processor_pair)}" - end - - processor.to_ast(text, options) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown/earmark.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown/earmark.ex deleted file mode 100644 index d451cd2..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/markdown/earmark.ex +++ /dev/null @@ -1,168 +0,0 @@ -defmodule ExDoc.Markdown.Earmark do - @moduledoc """ - ExDoc extension for the EarmarkParser Markdown parser. - """ - - @behaviour ExDoc.Markdown - - @admonition_classes ~w(warning error info tip neutral) - - @impl true - def available? do - match?({:ok, _}, Application.ensure_all_started(:earmark_parser)) and - Code.ensure_loaded?(EarmarkParser) - end - - @doc """ - Generate HTML AST. - - ## Options - - * `:gfm` - (boolean) turns on Github Flavored Markdown extensions. Defaults to `true`. - - * `:breaks` - (boolean) only applicable if `gfm` is enabled. Makes all line - breaks significant (so every line in the input is a new line in the output). - - """ - @impl true - def to_ast(text, opts) do - options = [ - gfm: true, - line: 1, - file: "nofile", - breaks: false, - pure_links: true, - math: true - ] - - options = Keyword.merge(options, opts) - - case EarmarkParser.as_ast(text, options) do - {:ok, ast, messages} -> - print_messages(messages, options) - fixup(ast) - - {:error, ast, messages} -> - print_messages(messages, options) - fixup(ast) - end - end - - defp print_messages(messages, options) do - for {_severity, line, message} <- messages do - ExDoc.warn(message, file: options[:file], line: line) - end - end - - defp fixup(list) when is_list(list) do - fixup_list(list, []) - end - - defp fixup(binary) when is_binary(binary) do - binary - end - - defp fixup({tag, attrs, ast}) do - fixup({tag, attrs, ast, %{}}) - end - - # Rewrite math back to the original syntax, it's up to the user to render it - - defp fixup({"code", [{"class", "math-inline"}], [content], _}) do - "$#{content}$" - end - - defp fixup({"code", [{"class", "math-display"}], [content], _}) do - "$$\n#{content}\n$$" - end - - # Convert admonition blockquotes to sections for screen reader accessibility - defp fixup( - {"blockquote", blockquote_attrs, [{tag, h_attrs, h_content, h_meta} | rest] = ast, - blockquote_meta} - ) - when tag in ["h3", "h4"] do - h_admonition = - with {{"class", classes}, attrs} <- List.keytake(h_attrs, "class", 0), - class_list <- String.split(classes, " "), - adm_classes = [_ | _] <- Enum.filter(class_list, &(&1 in @admonition_classes)) do - {"admonition " <> Enum.join(adm_classes, " "), - [{"class", "admonition-title #{classes}"} | attrs]} - else - _ -> nil - end - - section_attrs_fn = fn admonition_classes -> - {classes, attrs} = - case List.keytake(blockquote_attrs, "class", 0) do - nil -> - {admonition_classes, blockquote_attrs} - - {{"class", classes}, attrs} -> - {"#{admonition_classes} #{classes}", attrs} - end - - [{"role", "note"}, {"class", classes} | attrs] - end - - if h_admonition do - {admonition_classes, h_attrs} = h_admonition - section_attrs = section_attrs_fn.(admonition_classes) - h_elem = {tag, h_attrs, h_content, h_meta} - - fixup({"section", section_attrs, [h_elem | rest], blockquote_meta}) - else - # regular blockquote, copied fixup/1 here to avoid infinite loop - {:blockquote, Enum.map(blockquote_attrs, &fixup_attr/1), fixup(ast), blockquote_meta} - end - end - - defp fixup({tag, attrs, ast, meta}) when is_binary(tag) and is_list(attrs) and is_map(meta) do - {fixup_tag(tag), Enum.map(attrs, &fixup_attr/1), fixup(ast), meta} - end - - defp fixup({:comment, _, _, _} = comment) do - comment - end - - # We are matching on Livebook outputs here, because we prune comments at this point - defp fixup_list( - [ - {:comment, _, [~s/ livebook:{"output":true} /], %{comment: true}}, - {"pre", pre_attrs, [{"code", code_attrs, [source], code_meta}], pre_meta} - | ast - ], - acc - ) do - code_attrs = - case Enum.split_with(code_attrs, &match?({"class", _}, &1)) do - {[], attrs} -> [{"class", "output"} | attrs] - {[{"class", class}], attrs} -> [{"class", "#{class} output"} | attrs] - end - - code_node = {"code", code_attrs, [source], code_meta} - fixup_list([{"pre", pre_attrs, [code_node], pre_meta} | ast], acc) - end - - defp fixup_list([head | tail], acc) do - fixed = fixup(head) - - if fixed == [] do - fixup_list(tail, acc) - else - fixup_list(tail, [fixed | acc]) - end - end - - defp fixup_list([], acc) do - Enum.reverse(acc) - end - - defp fixup_tag(tag) do - String.to_atom(tag) - end - - defp fixup_attr({name, value}) do - {String.to_atom(name), value} - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/nodes.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/nodes.ex deleted file mode 100644 index ba46505..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/nodes.ex +++ /dev/null @@ -1,158 +0,0 @@ -# TODO: source_doc should only be a string once we remove application/html+erlang. -defmodule ExDoc.ModuleNode do - @moduledoc """ - Represents a module. - """ - - defstruct id: nil, - title: nil, - nested_context: nil, - nested_title: nil, - module: nil, - group: nil, - deprecated: nil, - doc: nil, - source_doc: nil, - source_format: nil, - moduledoc_line: nil, - moduledoc_file: nil, - source_path: nil, - source_url: nil, - docs_groups: [], - typespecs: [], - type: nil, - language: nil, - annotations: [], - metadata: nil - - @type t :: %__MODULE__{ - id: String.t(), - title: String.t(), - nested_context: String.t() | nil, - nested_title: String.t() | nil, - module: module(), - group: atom() | nil, - deprecated: String.t() | nil, - doc: ExDoc.DocAST.t() | nil, - source_doc: term() | nil, - source_format: String.t() | nil, - moduledoc_line: non_neg_integer(), - moduledoc_file: String.t(), - source_path: String.t() | nil, - source_url: String.t() | nil, - docs_groups: [ExDoc.DocGroupNode.t()], - typespecs: [ExDoc.DocNode.t()], - type: atom(), - language: module(), - annotations: [atom()], - metadata: map() - } -end - -defmodule ExDoc.DocNode do - @moduledoc """ - Represents a function, macro, callback, or type. - """ - - defstruct id: nil, - name: nil, - arity: 0, - defaults: [], - deprecated: nil, - doc: nil, - source_doc: nil, - type: nil, - signature: nil, - source_specs: [], - specs: [], - annotations: [], - group: nil, - doc_line: nil, - doc_file: nil, - source_url: nil - - @type annotation :: String.t() - @type function_default :: {name :: atom(), arity :: non_neg_integer()} - @type spec_ast :: term() - - @type t :: %__MODULE__{ - id: String.t(), - name: atom(), - arity: non_neg_integer(), - defaults: [function_default()], - deprecated: String.t() | nil, - doc: ExDoc.DocAST.t() | nil, - source_doc: term() | nil, - type: atom(), - signature: String.t(), - source_specs: [spec_ast()], - specs: [String.t()], - annotations: [annotation()], - group: String.t() | nil, - doc_file: String.t(), - doc_line: non_neg_integer(), - source_url: String.t() | nil - } -end - -defmodule ExDoc.DocGroupNode do - @moduledoc """ - Represents a group of functions, macros, callbacks, or types. - """ - defstruct title: nil, description: nil, doc: nil, docs: [] - - @type t :: %__MODULE__{ - title: String.t() | atom(), - description: String.t() | nil, - doc: ExDoc.DocAST.t() | nil, - docs: [ExDoc.DocNode.t()] - } -end - -defmodule ExDoc.ExtraNode do - @moduledoc """ - Represents an extra page. - """ - - defstruct id: nil, - title: nil, - title_doc: nil, - group: nil, - type: nil, - doc: nil, - source_doc: nil, - source_path: nil, - source_url: nil, - search_data: nil - - @type t :: %__MODULE__{ - id: String.t(), - title: String.t(), - title_doc: ExDoc.DocAST.t() | String.t(), - group: atom() | nil, - type: atom(), - doc: ExDoc.DocAST.t() | nil, - source_doc: String.t(), - source_path: String.t(), - source_url: String.t(), - search_data: [map()] | nil - } -end - -defmodule ExDoc.URLNode do - @moduledoc """ - Represents an extra URL. - """ - - defstruct id: nil, - title: nil, - group: nil, - url: nil - - @type t :: %__MODULE__{ - id: String.t(), - title: String.t(), - group: atom() | nil, - url: String.t() - } -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/refs.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/refs.ex deleted file mode 100644 index ddf9173..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/refs.ex +++ /dev/null @@ -1,191 +0,0 @@ -defmodule ExDoc.Refs do - # A read-through cache of documentation references. - # - # A given ref is always associated with a module. If we don't have a ref - # in the cache we fetch the module's docs chunk and fill in the cache. - # - # If the module does not have the docs chunk, we fetch it's functions, - # callbacks and types from other sources. - @moduledoc false - - @typep entry() :: {ref(), visibility()} - - @typep ref() :: - {:module, module()} - | {kind(), module(), name :: atom(), arity()} - @typep kind() :: :function | :callback | :type - @typep visibility() :: :hidden | :public | :undefined | :partial - - @name __MODULE__ - - use GenServer - - @spec start_link(any()) :: GenServer.on_start() - def start_link(arg) do - GenServer.start_link(__MODULE__, arg, name: @name) - end - - @spec init(any()) :: {:ok, nil} - def init(_) do - :ets.new(@name, [:named_table, :public, :set]) - {:ok, nil} - end - - @spec clear() :: :ok - def clear() do - :ets.delete_all_objects(@name) - :ok - end - - @spec get_visibility(ref()) :: visibility() - def get_visibility(ref) do - case lookup(ref) do - {:ok, visibility} -> - visibility - - :error -> - case fetch(ref) do - {:ok, visibility} -> visibility - :error -> :undefined - end - end - end - - @spec insert([entry()]) :: :ok - def insert(entries) do - true = :ets.insert(@name, entries) - :ok - end - - @spec insert_from_chunk(module, tuple()) :: :ok - def insert_from_chunk(module, result) do - module - |> fetch_entries(result) - |> insert() - - :ok - end - - defp lookup(ref) do - case :ets.lookup(@name, ref) do - [{^ref, visibility}] -> {:ok, visibility} - [] -> :error - end - rescue - _ -> :error - end - - defp fetch({:module, module} = ref) do - insert_from_chunk(module, Code.fetch_docs(module)) - lookup(ref) - end - - defp fetch({_kind, module, _name, _arity} = ref) do - get_visibility({:module, module}) - lookup(ref) - end - - defp fetch_entries(module, result) do - case result do - {:docs_v1, _, _, _, module_doc, _, docs} -> - module_visibility = visibility(module_doc) - - [{{:module, module}, module_visibility}] ++ - for {{kind, name, arity}, _, _, doc, metadata} <- docs, - ref_kind = to_ref_kind(kind), - visibility = visibility(module_doc, {ref_kind, name, doc}), - arity <- (arity - (metadata[:defaults] || 0))..arity do - {{ref_kind, module, name, arity}, visibility} - end - - {:error, _reason} -> - with true <- :code.which(module) != :non_existing, - true <- Code.ensure_loaded?(module) do - # We say it is limited because the types may not actually be available in the beam file. - [{{:module, module}, :limited}] ++ - to_refs(exports(module), module, :function) ++ - to_refs(callbacks(module), module, :callback) ++ - to_refs(types(module, [:type, :opaque, :nominal]), module, :type) - else - _ -> - [{{:module, module}, :undefined}] - end - end - end - - defguardp has_no_docs(doc) when doc == :none or doc == %{} - - defp starts_with_underscore?(name), do: match?([?_ | _], Atom.to_charlist(name)) - - defp visibility(:hidden), - do: :hidden - - defp visibility(_module_doc), - do: :public - - defp visibility(_module_doc, {kind, _name, _doc}) - when kind not in [:callback, :function, :type], - do: raise(ArgumentError, "Unknown kind #{inspect(kind)}") - - defp visibility(:hidden, {_kind, _name, _doc}), - do: :hidden - - defp visibility(_, {_kind, _name, :hidden}), - do: :hidden - - defp visibility(_, {kind, name, doc}) when has_no_docs(doc) do - cond do - kind in [:callback, :type] -> - :public - - kind == :function and starts_with_underscore?(name) -> - :hidden - - kind == :function -> - :public - end - end - - defp visibility(_, {_, _, _}) do - :public - end - - defp to_ref_kind(:macro), do: :function - defp to_ref_kind(:macrocallback), do: :callback - defp to_ref_kind(other), do: other - - defp exports(module) do - if function_exported?(module, :__info__, 1) do - module.__info__(:functions) ++ module.__info__(:macros) - else - module.module_info(:exports) - end - end - - defp callbacks(module) do - if function_exported?(module, :behaviour_info, 1) do - module.behaviour_info(:callbacks) - else - [] - end - end - - defp types(module, kind_list) do - case Code.Typespec.fetch_types(module) do - {:ok, list} -> - for {kind, {name, _, args}} <- list, - kind in kind_list do - {name, length(args)} - end - - :error -> - [] - end - end - - defp to_refs(list, module, kind, visibility \\ :public) do - for {name, arity} <- list do - {{kind, module, name, arity}, visibility} - end - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/retriever.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/retriever.ex deleted file mode 100644 index e66a859..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/retriever.ex +++ /dev/null @@ -1,415 +0,0 @@ -defmodule ExDoc.Retriever do - # Functions to extract documentation information from modules. - @moduledoc false - - defmodule Error do - @moduledoc false - defexception [:message] - end - - alias ExDoc.{Config, DocAST, Refs} - alias ExDoc.Retriever.Error - - @doc """ - Extract documentation from all modules in the specified directories. - - Returns a tuple containing `{modules, filtered}`, using `config.filter_modules` - as a filter criteria. - """ - @spec docs_from_dir([Path.t()], ExDoc.Config.t()) :: - {[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]} - def docs_from_dir(dirs, config) when is_list(dirs) do - dirs - |> Enum.reduce({[], []}, &docs_from_dir(&1, &2, config)) - |> sort_modules(config) - end - - defp docs_from_dir(dir, acc, config) do - files = Path.wildcard(Path.expand("*.beam", dir)) - - files - |> Enum.map(&filename_to_module/1) - |> docs_from_modules(acc, config) - end - - @doc """ - Extract documentation from all modules and returns a tuple containing - `{modules, filtered}`, two lists of modules that were extracted and filtered - by `config.filter_modules`, respectively. - """ - @spec docs_from_modules([atom], ExDoc.Config.t()) :: - {[ExDoc.ModuleNode.t()], [ExDoc.ModuleNode.t()]} - def docs_from_modules(modules, config) when is_list(modules) do - modules |> docs_from_modules({[], []}, config) |> sort_modules(config) - end - - defp docs_from_modules(modules, acc, config) do - modules - |> Task.async_stream(&get_module(&1, config), timeout: :infinity) - |> Enum.reduce(acc, fn {:ok, result}, {modules, filtered} = acc -> - case result do - {:error, _module} -> - acc - - {:ok, module_node} -> - if config.filter_modules.(module_node.module, module_node.metadata), - do: {[module_node | modules], filtered}, - else: {modules, [module_node | filtered]} - end - end) - end - - defp sort_modules({modules, filtered}, config) do - {sort_modules(modules, config), sort_modules(filtered, config)} - end - - defp sort_modules(modules, config) when is_list(modules) do - Enum.sort_by(modules, fn module -> - {Config.index(config.groups_for_modules, module.group), module.nested_context, - module.nested_title, module.id} - end) - end - - defp filename_to_module(name) do - name = Path.basename(name, ".beam") - String.to_atom(name) - end - - defp get_module(module, config) do - with {:docs_v1, _, language, _, _, _metadata, _} = docs_chunk <- docs_chunk(module), - {:ok, language} <- ExDoc.Language.get(language, module), - %{} = module_data <- language.module_data(module, docs_chunk, config) do - {:ok, generate_node(module, module_data, config)} - else - _ -> - {:error, module} - end - end - - defp docs_chunk(module) do - result = Code.fetch_docs(module) - Refs.insert_from_chunk(module, result) - - case result do - {:docs_v1, _, _, _, :hidden, _, _} -> - false - - {:docs_v1, _, _, _, _, _, _} = docs -> - case Code.ensure_loaded(module) do - {:module, _} -> - docs - - {:error, reason} -> - ExDoc.warn("skipping docs for module #{inspect(module)}, reason: #{reason}", []) - false - end - - {:error, :chunk_not_found} -> - false - - {:error, :module_not_found} -> - unless Code.ensure_loaded?(module) do - raise Error, "module #{inspect(module)} is not defined/available" - end - - {:error, _} = error -> - raise Error, "error accessing #{inspect(module)}: #{inspect(error)}" - - _ -> - raise Error, - "unknown format in Docs chunk. This likely means you are running on " <> - "a more recent Elixir version that is not supported by ExDoc. Please update." - end - end - - defp generate_node(module, module_data, config) do - source = %{ - url_pattern: config.source_url_pattern, - path: module_data.source_file, - relative_path: path_relative_to_cwd(module_data.source_file) - } - - {doc_line, doc_file, format, source_doc, doc_ast, metadata} = - get_module_docs(module_data, source, config) - - group_for_doc = config.group_for_doc - annotations_for_docs = config.annotations_for_docs - - {docs, nodes_groups} = - get_docs(module_data, source, group_for_doc, annotations_for_docs, config) - - docs = ExDoc.Utils.natural_sort_by(docs, &"#{&1.name}/#{&1.arity}") - - moduledoc_groups = Map.get(metadata, :groups, []) - - docs_groups = - get_docs_groups( - moduledoc_groups ++ config.docs_groups ++ module_data.default_groups, - nodes_groups, - docs, - config - ) - - metadata = Map.put(metadata, :kind, module_data.type) - group = Config.match_module(config.groups_for_modules, module, module_data.id, metadata) - {nested_title, nested_context} = module_data.nesting_info || {nil, nil} - - %ExDoc.ModuleNode{ - id: module_data.id, - title: module_data.title, - nested_title: nested_title, - nested_context: nested_context, - group: group, - module: module, - type: module_data.type, - deprecated: metadata[:deprecated], - docs_groups: docs_groups, - doc: normalize_doc_ast(doc_ast, "module-"), - source_doc: source_doc, - source_format: format, - moduledoc_line: doc_line, - moduledoc_file: doc_file, - source_url: source_link(source, module_data.source_line), - language: module_data.language, - annotations: List.wrap(metadata[:tags]), - metadata: metadata - } - end - - defp doc_ast(format, %{"en" => doc_content}, options) do - DocAST.parse!(doc_content, format, options) - end - - defp doc_ast(_format, _, _options) do - nil - end - - defp normalize_doc_ast(doc_ast, prefix) do - doc_ast - |> DocAST.add_ids_to_headers([:h2, :h3], prefix) - end - - # Helpers - - defp get_module_docs(module_data, source, config) do - {:docs_v1, anno, _, format, moduledoc, metadata, _} = module_data.docs - doc_file = anno_file(anno, source) - doc_line = anno_line(anno) - options = [file: doc_file, line: doc_line + 1, markdown_processor: config.markdown_processor] - {doc_line, doc_file, format, moduledoc, doc_ast(format, moduledoc, options), metadata} - end - - defp get_docs(module_data, source, group_for_doc, annotations_for_docs, config) do - {:docs_v1, _, _, _, _, _, docs} = module_data.docs - - {nodes, groups} = - for doc <- docs, - doc_data = module_data.language.doc_data(doc, module_data) do - {_node, _group} = - get_doc(doc, doc_data, module_data, source, group_for_doc, annotations_for_docs, config) - end - |> Enum.unzip() - - {filter_defaults(nodes), groups} - end - - defp get_doc(doc, doc_data, module_data, source, group_for_doc, annotations_for_docs, config) do - {:docs_v1, _, _, content_type, _, module_metadata, _} = module_data.docs - {{type, name, arity}, anno, _signature, source_doc, metadata} = doc - doc_file = anno_file(anno, source) - doc_line = anno_line(anno) - - metadata = - Map.merge( - %{kind: type, name: name, arity: arity, module: module_data.module}, - metadata - ) - - source_url = source_link(doc_data.source_file, source, doc_data.source_line) - - annotations = - annotations_for_docs.(metadata) ++ - annotations_from_metadata(metadata, module_metadata) ++ doc_data.extra_annotations - - defaults = get_defaults(name, arity, Map.get(metadata, :defaults, 0)) - - doc_ast = - doc_ast(content_type, source_doc, - file: doc_file, - line: doc_line + 1, - markdown_processor: config.markdown_processor - ) || - doc_data.doc_fallback.() - - group = normalize_group(group_for_doc.(metadata) || doc_data.default_group) - id = doc_data.id_key <> nil_or_name(name, arity) - - doc_node = %ExDoc.DocNode{ - id: id, - name: name, - arity: arity, - deprecated: metadata[:deprecated], - doc: normalize_doc_ast(doc_ast, id <> "-"), - source_doc: source_doc, - doc_line: doc_line, - doc_file: doc_file, - defaults: ExDoc.Utils.natural_sort_by(defaults, fn {name, arity} -> "#{name}/#{arity}" end), - signature: signature(doc_data.signature), - source_specs: doc_data.specs, - source_url: source_url, - type: doc_data.type, - group: group.title, - annotations: annotations - } - - {doc_node, group} - end - - defp get_defaults(_name, _arity, 0), do: [] - - defp get_defaults(name, arity, defaults) do - for default <- (arity - defaults)..(arity - 1), do: {name, default} - end - - defp filter_defaults(nodes) do - Enum.map(nodes, &filter_defaults(&1, nodes)) - end - - defp filter_defaults(node, nodes) do - update_in(node.defaults, fn defaults -> - Enum.reject(defaults, fn {name, arity} -> - Enum.any?(nodes, &match?(%{name: ^name, arity: ^arity}, &1)) - end) - end) - end - - defp get_docs_groups(module_groups, nodes_groups, doc_nodes, config) do - module_groups = Enum.map(module_groups, &normalize_group/1) - - nodes_groups_descriptions = Map.new(nodes_groups, &{&1.title, &1.description}) - - # Doc nodes already have normalized groups - nodes_groups = ExDoc.Utils.natural_sort_by(nodes_groups, & &1.title) - normal_groups = module_groups ++ nodes_groups - nodes_by_group_title = Enum.group_by(doc_nodes, & &1.group) - - {docs_groups, _} = - Enum.flat_map_reduce(normal_groups, %{}, fn - group, seen when is_map_key(seen, group.title) -> - {[], seen} - - group, seen -> - seen = Map.put(seen, group.title, true) - - case Map.get(nodes_by_group_title, group.title, []) do - [] -> - {[], seen} - - child_nodes -> - group = finalize_group(group, child_nodes, nodes_groups_descriptions, config) - {[group], seen} - end - end) - - docs_groups - end - - defp finalize_group(group, doc_nodes, description_fallbacks, config) do - description = - case group.description do - nil -> Map.get(description_fallbacks, group.title) - text -> text - end - - doc_ast = - case description do - nil -> - nil - - text -> - doc_ast = - doc_ast("text/markdown", %{"en" => text}, - markdown_processor: config.markdown_processor - ) - - sub_id = ExDoc.Utils.text_to_id(group.title) - normalize_doc_ast(doc_ast, "group-#{sub_id}-") - end - - %ExDoc.DocGroupNode{ - title: group.title, - description: description, - doc: doc_ast, - docs: doc_nodes - } - end - - ## General helpers - - defp nil_or_name(name, arity) do - if name == nil do - "nil/#{arity}" - else - "#{name}/#{arity}" - end - end - - defp signature(list) when is_list(list), do: Enum.join(list, " ") - - defp annotations_from_metadata(metadata, module_metadata) do - # Give precedence to the function/callback/type metadata over the module metadata. - cond do - since = metadata[:since] -> ["since #{since}"] - since = module_metadata[:since] -> ["since #{since}"] - true -> [] - end - end - - defp anno_line(line) when is_integer(line), do: abs(line) - defp anno_line(anno), do: anno |> :erl_anno.line() |> abs() - - defp anno_file(anno, source) do - case :erl_anno.file(anno) do - :undefined -> - source.relative_path - - file -> - source.path - |> Path.dirname() - |> Path.join(file) - |> path_relative_to_cwd() - end - end - - # TODO: Remove when we require Elixir 1.16 - if function_exported?(Path, :relative_to_cwd, 2) do - defp path_relative_to_cwd(path), do: Path.relative_to_cwd(path, force: true) - else - defp path_relative_to_cwd(path), do: Path.relative_to_cwd(path) - end - - defp source_link(nil, source, line), do: source_link(source, line) - - defp source_link(file, %{url_pattern: url_pattern}, line) do - url_pattern.(path_relative_to_cwd(file), line) - end - - defp source_link(%{url_pattern: url_pattern, relative_path: path}, line) do - url_pattern.(path, line) - end - - defp normalize_group(group) do - case group do - %{title: title, description: description} - when is_binary(title) and (is_binary(description) or is_nil(description)) -> - %{group | title: title, description: description} - - kw when is_list(kw) -> - true = Keyword.keyword?(kw) - %{title: to_string(Keyword.fetch!(kw, :title)), description: kw[:description]} - - title when is_binary(title) when is_atom(title) -> - %{title: to_string(title), description: nil} - end - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/shell_lexer.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/shell_lexer.ex deleted file mode 100644 index c55824e..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/shell_lexer.ex +++ /dev/null @@ -1,43 +0,0 @@ -defmodule ExDoc.ShellLexer do - # Makeup lexer for sh, bash, etc commands. - # The only thing it does is making the `$ ` prompt not selectable. - @moduledoc false - - @behaviour Makeup.Lexer - - @impl true - def lex(text, _opts) do - text - |> String.split("\n") - |> Enum.flat_map(fn - "$ " <> rest -> - [ - {:generic_prompt, %{selectable: false}, "$ "}, - {:text, %{}, rest <> "\n"} - ] - - text -> - [{:text, %{}, text <> "\n"}] - end) - end - - @impl true - def match_groups(_arg0, _arg1) do - raise "not implemented yet" - end - - @impl true - def postprocess(_arg0, _arg1) do - raise "not implemented yet" - end - - @impl true - def root(_arg0) do - raise "not implemented yet" - end - - @impl true - def root_element(_arg0) do - raise "not implemented yet" - end -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/utils.ex b/plugins/mob_iap/deps/ex_doc/lib/ex_doc/utils.ex deleted file mode 100644 index 44571a0..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/ex_doc/utils.ex +++ /dev/null @@ -1,159 +0,0 @@ -defmodule ExDoc.Utils do - # General helpers used throughout ExDoc or extracted for testing. - # Avoid adding functions to this module whenever possible, - # instead prefer defining modules closer to the context they are used. - @moduledoc false - - @doc """ - HTML escapes the given string. - - iex> ExDoc.Utils.h("") - "<foo>" - - """ - def h(string) do - String.replace(string, ~w|& < > "|, fn - "&" -> "&" - "<" -> "<" - ">" -> ">" - ~S(") -> """ - end) - end - - @doc """ - Strips HTML tags from text leaving their text content - """ - def strip_tags(text, replace_with \\ "") when is_binary(text) do - clean_html_regex = ~r/<\/?\s*[a-zA-Z]+(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>/ - String.replace(text, clean_html_regex, replace_with) - end - - @doc """ - Generates an ID from some text. - - Used primarily with titles, headings, and functions group names. - """ - def text_to_id(atom) when is_atom(atom), do: text_to_id(Atom.to_string(atom)) - - def text_to_id(text) when is_binary(text) do - text - |> String.replace(~r/&#\d+;/, "") - |> String.replace(~r/&[A-Za-z0-9]+;/, "") - |> String.replace(~r/\W+/u, "-") - |> String.trim("-") - |> String.downcase() - end - - @doc """ - Sorts mapped strings by natural order. - """ - def natural_sort_by(enumerable, mapper) when is_function(mapper, 1) do - Enum.sort_by(enumerable, fn elem -> - elem - |> mapper.() - |> to_sortable_charlist() - end) - end - - defp to_sortable_charlist(string) do - string - |> :unicode.characters_to_nfkd_list() - |> make_sortable() - end - - @offset -1_000_000_000 - - # Numbers come first, so group and pad them with offset - defp make_sortable([digit | chars]) when digit in ?0..?9 do - {digits, chars} = Enum.split_while(chars, &(&1 in ?0..?9)) - [@offset + List.to_integer([digit | digits]) | make_sortable(chars)] - end - - # Then Elixir special punctuation - trailing bang `!` - defp make_sortable([?! | chars]), do: [?0 | make_sortable(chars)] - - # Then Elixir special punctuation - question mark `?` - defp make_sortable([?? | chars]), do: [?1 | make_sortable(chars)] - - # Then underscore - defp make_sortable([?_ | chars]), do: [?2 | make_sortable(chars)] - - # Then uppercased letters and lowercased letters - defp make_sortable([char | chars]) when char in ?a..?z do - [char - 31.5 | make_sortable(chars)] - end - - defp make_sortable([char | chars]), do: [char | make_sortable(chars)] - defp make_sortable([]), do: [] - - @doc """ - A very simple JSON encoder. - - We want to minimize the number of dependencies ExDoc has, - because we don't want someone to be allowed to not upgrade - their app due to an ExDoc restriction, so we ship with a - simple JSON implementation. - """ - # TODO: Remove this once we require Elixir v1.20+ - def to_json(nil), do: "null" - def to_json(true), do: "true" - def to_json(false), do: "false" - - def to_json(map) when is_map(map) do - mapped = - Enum.map_intersperse(Map.to_list(map), ?,, fn {key, value} -> - [key |> Atom.to_string() |> inspect(), ?:, to_json(value)] - end) - - [?{, mapped, ?}] - end - - def to_json(list) when is_list(list) do - mapped = Enum.map_intersperse(list, ?,, &to_json/1) - [?[, mapped, ?]] - end - - def to_json(atom) when is_atom(atom) do - atom |> Atom.to_string() |> inspect() - end - - def to_json(binary) when is_binary(binary) do - to_json_string(binary, "\"") - end - - def to_json(integer) when is_integer(integer) do - Integer.to_string(integer) - end - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc) when x <= 0x000F, - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc) when x <= 0x001F, - do: to_json_string(rest, <>) - - defp to_json_string(<>, acc), - do: to_json_string(rest, <>) - - defp to_json_string(<<>>, acc), do: <> -end diff --git a/plugins/mob_iap/deps/ex_doc/lib/mix/tasks/docs.ex b/plugins/mob_iap/deps/ex_doc/lib/mix/tasks/docs.ex deleted file mode 100644 index fcef373..0000000 --- a/plugins/mob_iap/deps/ex_doc/lib/mix/tasks/docs.ex +++ /dev/null @@ -1,333 +0,0 @@ -defmodule Mix.Tasks.Docs do - use Mix.Task - - @shortdoc "Generate documentation for the project" - @requirements ["compile"] - - @moduledoc ~S""" - Uses ExDoc to generate a static web page from the project documentation. - - ## Command line options - - * `--canonical`, `-n` - Indicate the preferred URL with - `rel="canonical"` link element, defaults to no canonical path - - * `--formatter`, `-f` - Which formatters to use, `html`, - `epub`, or `markdown`. This option can be given more than once. By default, - `html`, `epub`, and `markdown` are generated. - - * `--language` - Specifies the language to annotate the - EPUB output in valid [BCP 47](https://tools.ietf.org/html/bcp47) - - * `--open` - open browser window pointed to the documentation - - * `--output`, `-o` - Output directory for the generated - docs, default: `"doc"` - - * `--proglang` - Chooses the main programming language: `elixir` - or `erlang` - - * `--warnings-as-errors` - Exits with non-zero exit code if any warnings are found - - The command line options have higher precedence than the options - specified in your `mix.exs` file below. - - ## Configuration - - ExDoc will automatically pull in information from your project, - like the application and version. However, you may want to set - `:name`, `:source_url` and `:homepage_url` to have a nicer output - from ExDoc, for example: - - def project do - [ - app: :my_app, - version: "0.1.0-dev", - deps: deps(), - - # Docs - name: "My App", - source_url: "https://github.com/USER/PROJECT", - homepage_url: "http://YOUR_PROJECT_HOMEPAGE", - docs: [ - main: "MyApp", # The main page in the docs - favicon: "path/to/favicon.png", - logo: "path/to/logo.png", - extras: ["README.md"] - ] - ] - end - - ExDoc also allows configuration specific to the documentation to - be set. The following options should be put under the `:docs` key - in your project's main configuration. The `:docs` options should - be a keyword list or a function returning a keyword list that will - be lazily executed. See all supported options in `ExDoc.generate/4`. - - ## Umbrella project - - ExDoc can be used in an umbrella project and generates a single documentation - for all child apps. You can use the `:ignore_apps` configuration to exclude - certain projects in the umbrella from documentation. - - Generating documentation per each child app can be achieved by running: - - mix cmd mix docs - - See `mix help cmd` for more information. - """ - - @switches [ - canonical: :string, - formatter: :keep, - language: :string, - open: :boolean, - output: :string, - proglang: :string, - warnings_as_errors: :boolean - ] - - @aliases [ - f: :formatter, - n: :canonical, - o: :output - ] - - @doc false - def run(args, config \\ Mix.Project.config(), generator \\ &ExDoc.generate/4) do - {:ok, _} = Application.ensure_all_started(:ex_doc) - - unless Code.ensure_loaded?(ExDoc.Formatter.Config) do - Mix.raise( - "Could not load ExDoc configuration. Please make sure you are running the " <> - "docs task in the same Mix environment it is listed in your deps" - ) - end - - {cli_opts, args, _} = OptionParser.parse(args, aliases: @aliases, switches: @switches) - - if args != [] do - Mix.raise("Extraneous arguments on the command line") - end - - project = - to_string( - config[:name] || config[:app] || - Mix.raise("expected :name or :app to be found in the project definition in mix.exs") - ) - - version = config[:version] || "dev" - - cli_opts = - Keyword.update(cli_opts, :proglang, :elixir, fn proglang -> - if proglang not in ~w(erlang elixir) do - Mix.raise("--proglang must be elixir or erlang") - end - - String.to_atom(proglang) - end) - - options = - config - |> get_docs_opts() - |> Keyword.merge(cli_opts) - # accepted at root level config - |> normalize_source_url(config) - # accepted at root level config - |> normalize_homepage_url(config) - # accepted at root level config - |> normalize_description(config) - |> normalize_apps(config) - |> normalize_main() - |> normalize_deps() - |> normalize_formatters() - |> put_package(config) - - source_beams = source_beams(options, config) - Code.prepend_path(source_beams) - - for path <- Keyword.get_values(options, :paths), - path <- Path.wildcard(path) do - Code.prepend_path(path) - end - - Mix.shell().info("Generating docs...") - generated_docs = generator.(project, version, source_beams, options) - - Enum.each(generated_docs, fn %{entrypoint: entrypoint, formatter: formatter} -> - extension = formatter_module_to_extension(formatter) - Mix.shell().info([:green, "View #{extension} docs at #{inspect(entrypoint)}"]) - end) - - open? = Keyword.get(cli_opts, :open, false) - - with [%{entrypoint: entrypoint} | _] when open? <- generated_docs do - browser_open(entrypoint) - end - - warned = Enum.filter(generated_docs, & &1.warned?) - - if options[:warnings_as_errors] == true and warned != [] do - formatters = Enum.map(warned, &formatter_module_to_extension(&1.formatter)) - - format_message = - case formatters do - [formatter] -> "#{formatter} format" - _ -> "#{Enum.join(formatters, ", ")} formats" - end - - message = - "Documents have been generated, but generation for #{format_message} failed " <> - "due to warnings while using the --warnings-as-errors option" - - message_formatted = IO.ANSI.format([:red, message, :reset]) - IO.puts(:stderr, message_formatted) - exit({:shutdown, 1}) - else - Enum.map(generated_docs, & &1.entrypoint) - end - end - - defp formatter_module_to_extension(module) do - module |> Module.split() |> List.last() |> String.downcase() - end - - defp normalize_formatters(options) do - formatters = - case Keyword.get_values(options, :formatter) do - [] -> options[:formatters] || ["html", "markdown", "epub"] - values -> values - end - - options - |> Keyword.delete(:formatter) - |> Keyword.put(:formatters, formatters) - end - - defp get_docs_opts(config) do - docs = config[:docs] - - cond do - is_function(docs, 0) -> docs.() - is_nil(docs) -> [] - true -> docs - end - end - - defp normalize_source_url(options, config) do - if source_url = config[:source_url] do - Keyword.put(options, :source_url, source_url) - else - options - end - end - - defp normalize_homepage_url(options, config) do - if homepage_url = config[:homepage_url] do - Keyword.put(options, :homepage_url, homepage_url) - else - options - end - end - - defp normalize_description(options, config) do - if description = config[:description] do - Keyword.put(options, :description, description) - else - options - end - end - - defp source_beams(options, config) do - if Mix.Project.umbrella?(config) do - umbrella_compile_paths(Keyword.get(options, :ignore_apps, [])) - else - [Mix.Project.compile_path()] - end - end - - defp umbrella_compile_paths(ignored_apps) do - build = Mix.Project.build_path() - - for {app, _} <- Mix.Project.apps_paths(), - app not in ignored_apps do - Path.join([build, "lib", Atom.to_string(app), "ebin"]) - end - end - - defp normalize_apps(options, config) do - if Mix.Project.umbrella?(config) do - ignore = Keyword.get(options, :ignore_apps, []) - - apps = - for {app, _} <- Mix.Project.apps_paths(), app not in ignore do - app - end - - Keyword.put(options, :apps, Enum.sort(apps)) - else - Keyword.put(options, :apps, List.wrap(config[:app])) - end - end - - defp normalize_main(options) do - main = options[:main] - - cond do - is_nil(main) -> - Keyword.delete(options, :main) - - is_atom(main) -> - Keyword.put(options, :main, inspect(main)) - - is_binary(main) -> - options - end - end - - defp normalize_deps(options) do - user_deps = Keyword.get(options, :deps, []) - - deps = - for {app, doc} <- Keyword.merge(get_deps(), user_deps), - lib_dir = :code.lib_dir(app), - is_list(lib_dir), - do: {app, doc} - - Keyword.put(options, :deps, deps) - end - - defp get_deps do - for {key, _} <- Mix.Project.deps_paths(), - _ = Application.load(key), - vsn = Application.spec(key, :vsn) do - {key, "https://hexdocs.pm/#{key}/#{vsn}/"} - end - end - - defp put_package(options, config) do - if package = config[:package] do - Keyword.put(options, :package, package[:name] || config[:app]) - else - options - end - end - - defp browser_open(path) do - {cmd, args, options} = - case :os.type() do - {:win32, _} -> - dirname = Path.dirname(path) - basename = Path.basename(path) - {"cmd", ["/c", "start", basename], [cd: dirname]} - - {:unix, :darwin} -> - {"open", [path], []} - - {:unix, _} -> - {"xdg-open", [path], []} - end - - System.cmd(cmd, args, options) - end -end diff --git a/plugins/mob_iap/deps/ex_doc/mix.exs b/plugins/mob_iap/deps/ex_doc/mix.exs deleted file mode 100644 index 6e2eb7f..0000000 --- a/plugins/mob_iap/deps/ex_doc/mix.exs +++ /dev/null @@ -1,166 +0,0 @@ -defmodule ExDoc.Mixfile do - use Mix.Project - - @source_url "https://github.com/elixir-lang/ex_doc" - @version "0.40.3" - - def project do - [ - app: :ex_doc, - version: @version, - elixir: "~> 1.15", - deps: deps(), - aliases: aliases(), - package: package(), - escript: escript(), - elixirc_paths: elixirc_paths(Mix.env()), - source_url: @source_url, - test_elixirc_options: [docs: true, debug_info: true], - test_ignore_filters: [&String.starts_with?(&1, "test/fixtures/")], - name: "ExDoc", - description: "ExDoc is a documentation generation tool for Elixir", - docs: docs() - ] - end - - def cli do - [preferred_envs: ["hex.publish": :prod]] - end - - def application do - [ - extra_applications: [:eex] ++ extra_applications(Mix.env()), - mod: {ExDoc.Application, []} - ] - end - - defp extra_applications(:test), do: [:edoc, :xmerl] - defp extra_applications(_), do: [] - - defp deps do - [ - {:earmark_parser, "~> 1.4.44"}, - {:makeup_elixir, "~> 0.14 or ~> 1.0"}, - {:makeup_erlang, "~> 0.1 or ~> 1.0"}, - # Add other makeup lexers as optional for the executable - {:makeup_c, ">= 0.1.0", optional: true}, - {:makeup_html, ">= 0.1.0", optional: true}, - {:jason, "~> 1.2", only: :test}, - {:lazy_html, "~> 0.1.0", only: :test} - ] - end - - defp aliases do - [ - build: ["cmd --cd assets npm run build", "compile --force", &docs/1], - clean: [&clean_test_fixtures/1, "clean"], - fix: ["format", "cmd --cd assets npm run lint:fix"], - lint: ["format --check-formatted", "cmd --cd assets npm run lint"], - setup: ["deps.get", "cmd --cd assets npm install"] - ] - end - - defp package do - [ - licenses: ["Apache-2.0"], - maintainers: ["José Valim", "Milton Mazzarri", "Wojtek Mach"], - files: ~w(CHANGELOG.md Cheatsheet.cheatmd formatters lib LICENSE mix.exs README.md), - links: %{ - "GitHub" => @source_url, - "Changelog" => "https://hexdocs.pm/ex_doc/changelog.html", - "Writing documentation" => "https://hexdocs.pm/elixir/writing-documentation.html" - } - ] - end - - defp escript do - [ - main_module: ExDoc.CLI - ] - end - - defp elixirc_paths(:test), do: ["lib", "test/support"] - defp elixirc_paths(_), do: ["lib"] - - defp docs do - if Mix.env() == :dev do - [ - search: [ - %{ - name: "ExDoc + Elixir", - help: "Search latest ExDoc + Elixir", - packages: [:ex_doc, elixir: "main"] - }, - %{ - name: "ExDoc on Google", - help: "Search everything about ExDoc on Google", - url: "https://google.com/?q=" - }, - %{name: "Lunr", help: "Search using Lunr in browser"} - ] - ] - else - [] - end ++ - [ - main: "readme", - extras: - [ - "README.md", - "Cheatsheet.cheatmd", - "CHANGELOG.md" - ] ++ test_dev_examples(Mix.env()), - source_ref: "v#{@version}", - source_url: @source_url, - groups_for_modules: [ - Markdown: [ - ExDoc.Markdown, - ExDoc.Markdown.Earmark - ], - Formatter: [ - ExDoc.Formatter, - ExDoc.Formatter.Config - ], - Nodes: [ - ExDoc.ModuleNode, - ExDoc.DocNode, - ExDoc.DocGroupNode, - ExDoc.ExtraNode, - ExDoc.URLNode - ] - ], - groups_for_extras: [ - Examples: ~r"test/examples" - ], - skip_undefined_reference_warnings_on: [ - "CHANGELOG.md" - ] - ] - end - - defp docs(args) do - Mix.Task.run("docs", args) - {text_tags, 0} = System.cmd("git", ["tag"]) - - versions = - for("v" <> rest <- String.split(text_tags), do: Version.parse!(rest)) - |> Enum.sort({:desc, Version}) - - list_contents = - Enum.map_intersperse(versions, ", ", fn version -> - string = Version.to_string(version) - ~s[{"version":"v#{string}", "url":"https://hexdocs.pm/ex_doc/#{string}"}] - end) - - File.write!("doc/docs_config.js", """ - var versionNodes = [#{list_contents}]; - """) - end - - defp test_dev_examples(:dev), do: Path.wildcard("test/examples/*") - defp test_dev_examples(_), do: [] - - defp clean_test_fixtures(_args) do - File.rm_rf("test/tmp") - end -end diff --git a/plugins/mob_iap/deps/jason/.hex b/plugins/mob_iap/deps/jason/.hex deleted file mode 100644 index b7e0a46f02623131946948ed3f86d62a31d7cd9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261 zcmZ9H%WlIU5JlZMeXQ~+(EtO6UH_zFhH>qr;K)u&_WkkFEW0~8I=a&RnRZiO`$_I` z7rSfMbuV>aVwT+S`4qRM)YorWw|OZKrH**C^)1H|Kb!t)*;DQFd_UF0T#Bq8JPDt- zTmH-6wU`LtZ2$uDX)xZi1|5uq#2k1uVv|EXCLNVRK_()r#Ri)KLac}7cKts}z+RgW toB~appfMTNh$sYr5NOb&iwc2Nut4ky?U1~2WWaf>`&BN><4!w%$-j` tags). - - If you were not using the `html_safe` option, you are not affected. - - Affected versions: < 1.2.1 - Patched versions: >= 1.2.1 - -## 1.2.0 (17.03.2020) - -### Enhancements - -* Add `Jason.Encode.keyword/2` - ([cb1f26a](https://github.com/michalmuskala/jason/commit/cb1f26a)). - -### Bug fixes - -* Fix `Jason.Helpers.json_map/1` value expansion - ([70b046a](https://github.com/michalmuskala/jason/commit/70b046a)). - -## 1.1.2 (19.10.2018) - -### Bug fixes - -* correctly handle the `pretty: false` option - ([ba318c8](https://github.com/michalmuskala/jason/commit/ba318c8)). - -## 1.1.1 (10.07.2018) - -### Bug fixes - -* correctly handle escape sequences in strings when pretty printing - ([794bbe4](https://github.com/michalmuskala/jason/commit/794bbe4)). - -## 1.1.0 (02.07.2018) - -### Enhancements - -* pretty-printing support through `Jason.Formatter` and `pretty: true` option - in `Jason.encode/2` ([d758e36](https://github.com/michalmuskala/jason/commit/d758e36)). - -### Bug fixes - -* silence variable warnings for fields with underscores used during deriving - ([88dd85c](https://github.com/michalmuskala/jason/commit/88dd85c)). -* **potential incompatibility** don't raise `Protocol.UndefinedError` in non-bang functions - ([ad0f57b](https://github.com/michalmuskala/jason/commit/ad0f57b)). - -## 1.0.1 (02.07.2018) - -### Bug fixes - -* fix `Jason.Encode.escape` type ([a57b430](https://github.com/michalmuskala/jason/commit/a57b430)) -* multiple documentation improvements - -## 1.0.0 (26.01.2018) - -No changes - -## 1.0.0-rc.3 (26.01.2018) - -### Changes - -* update `escape` option of `Jason.encode/2` to take values: - `:json | :unicode_safe | :html_safe | :javascript_safe` for consistency. Old values of - `:unicode` and `:javascript` are still supported for compatibility with Poison. - ([f42dcbd](https://github.com/michalmuskala/jason/commit/f42dcbd)) - -## 1.0.0-rc.2 (07.01.2018) - -### Bug fixes - -* add type for `strings` option ([b459ee4](https://github.com/michalmuskala/jason/commit/b459ee4)) -* support iodata in `decode!` ([a1f3456](https://github.com/michalmuskala/jason/commit/a1f3456)) - -## 1.0.0-rc.1 (22.12.2017) - -* Initial release diff --git a/plugins/mob_iap/deps/jason/LICENSE b/plugins/mob_iap/deps/jason/LICENSE deleted file mode 100644 index f22c0ba..0000000 --- a/plugins/mob_iap/deps/jason/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2017-present Michał Muskała - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/plugins/mob_iap/deps/jason/README.md b/plugins/mob_iap/deps/jason/README.md deleted file mode 100644 index 7db1eaf..0000000 --- a/plugins/mob_iap/deps/jason/README.md +++ /dev/null @@ -1,157 +0,0 @@ -# Jason [![Hex Version](https://img.shields.io/hexpm/v/jason.svg)](https://hex.pm/packages/jason) [![Hex Docs](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/jason/) - -A blazing fast JSON parser and generator in pure Elixir. - -The parser and generator are at least twice as fast as other Elixir/Erlang libraries -(most notably `Poison`). -The performance is comparable to `jiffy`, which is implemented in C as a NIF. -Jason is usually only twice as slow. - -Both parser and generator fully conform to -[RFC 8259](https://tools.ietf.org/html/rfc8259) and -[ECMA 404](https://ecma-international.org/publications-and-standards/standards/ecma-404/) -standards. The parser is tested using [JSONTestSuite](https://github.com/nst/JSONTestSuite). - -## Installation - -The package can be installed by adding `jason` to your list of dependencies -in `mix.exs`: - -```elixir -def deps do - [{:jason, "~> 1.4"}] -end -``` - -## Basic Usage - -``` elixir -iex(1)> Jason.encode!(%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"}) -"{\"age\":44,\"name\":\"Steve Irwin\",\"nationality\":\"Australian\"}" - -iex(2)> Jason.decode!(~s({"age":44,"name":"Steve Irwin","nationality":"Australian"})) -%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"} -``` - -Full documentation can be found at [https://hexdocs.pm/jason](https://hexdocs.pm/jason). - -## Use with other libraries - -### Postgrex - -Versions starting at 0.14.0 use `Jason` by default. For earlier versions, please refer to -[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#postgrex). - -### Ecto - -Versions starting at 3.0.0 use `Jason` by default. For earlier versions, please refer to -[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#ecto). - -### Plug (and Phoenix) - -Phoenix starting at 1.4.0 uses `Jason` by default. For earlier versions, please refer to -[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#plug-and-phoenix). - -### Absinthe - -You need to pass the `:json_codec` option to `Absinthe.Plug` - -```elixir -# When called directly: -plug Absinthe.Plug, - schema: MyApp.Schema, - json_codec: Jason - -# When used in phoenix router: -forward "/api", - to: Absinthe.Plug, - init_opts: [schema: MyApp.Schema, json_codec: Jason] -``` - -## Benchmarks - -Detailed benchmarks (including memory measurements): -https://gist.github.com/michalmuskala/4d64a5a7696ca84ac7c169a0206640d5 - -HTML reports for the benchmark (only performance measurements): -http://michal.muskala.eu/jason/decode.html and http://michal.muskala.eu/jason/encode.html - -### Running - -Benchmarks against most popular Elixir & Erlang json libraries can be executed after -going into the `bench/` folder and then executing `mix bench.encode` and `mix bench.decode`. -A HTML report of the benchmarks (after their execution) can be found in -`bench/output/encode.html` and `bench/output/decode.html` respectively. - -## Differences to Poison - -Jason has a couple feature differences compared to Poison. - - * Jason follows the JSON spec more strictly, for example it does not allow - unescaped newline characters in JSON strings - e.g. `"\"\n\""` will - produce a decoding error. - * no support for decoding into data structures (the `as:` option). - * no built-in encoders for `MapSet`, `Range` and `Stream`. - * no support for encoding arbitrary structs - explicit implementation - of the `Jason.Encoder` protocol is always required. - * different pretty-printing customisation options (default `pretty: true` works the same) - -### Encoders - -If you require encoders for any of the unsupported collection types, I suggest -adding the needed implementations directly to your project: - -```elixir -defimpl Jason.Encoder, for: [MapSet, Range, Stream] do - def encode(struct, opts) do - Jason.Encode.list(Enum.to_list(struct), opts) - end -end -``` - -If you need to encode some struct that does not implement the protocol, -if you own the struct, you can derive the implementation specifying -which fields should be encoded to JSON: - -```elixir -@derive {Jason.Encoder, only: [....]} -defstruct # ... -``` - -It is also possible to encode all fields, although this should be -used carefully to avoid accidentally leaking private information -when new fields are added: - -```elixir -@derive Jason.Encoder -defstruct # ... -``` - -Finally, if you don't own the struct you want to encode to JSON, -you may use `Protocol.derive/3` placed outside of any module: - -```elixir -Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) -Protocol.derive(Jason.Encoder, NameOfTheStruct) -``` - -## Injecting an already encoded JSON inside a to-be-encoded structure - -If parts of the to-be-encoded structure are already JSON-encoded, you can -use `Jason.Fragment` to mark the parts as already encoded, and avoid a -decoding/encoding roundtrip. - -```elixir -already_encoded_json = Jason.encode!(%{hello: "world"}) -Jason.encode!(%{foo: Jason.Fragment.new(already_encoded_json)}) -``` - -This feature is especially useful if you need to cache a part of the JSON, -or if it is already provided by another system (e.g. `jsonb_agg` with Postgres). - -## License - -Jason is released under the Apache License 2.0 - see the [LICENSE](LICENSE) file. - -Some elements of tests and benchmarks have their origins in the -[Poison library](https://github.com/devinus/poison) and were initially licensed under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/plugins/mob_iap/deps/jason/hex_metadata.config b/plugins/mob_iap/deps/jason/hex_metadata.config deleted file mode 100644 index 60f4ac0..0000000 --- a/plugins/mob_iap/deps/jason/hex_metadata.config +++ /dev/null @@ -1,21 +0,0 @@ -{<<"links">>,[{<<"GitHub">>,<<"https://github.com/michalmuskala/jason">>}]}. -{<<"name">>,<<"jason">>}. -{<<"version">>,<<"1.4.5">>}. -{<<"description">>, - <<"A blazing fast JSON parser and generator in pure Elixir.">>}. -{<<"elixir">>,<<"~> 1.4">>}. -{<<"app">>,<<"jason">>}. -{<<"licenses">>,[<<"Apache-2.0">>]}. -{<<"requirements">>, - [[{<<"name">>,<<"decimal">>}, - {<<"app">>,<<"decimal">>}, - {<<"optional">>,true}, - {<<"requirement">>,<<"~> 1.0 or ~> 2.0 or ~> 3.0">>}, - {<<"repository">>,<<"hexpm">>}]]}. -{<<"files">>, - [<<"lib">>,<<"lib/jason.ex">>,<<"lib/encoder.ex">>,<<"lib/decoder.ex">>, - <<"lib/ordered_object.ex">>,<<"lib/formatter.ex">>,<<"lib/encode.ex">>, - <<"lib/codegen.ex">>,<<"lib/helpers.ex">>,<<"lib/sigil.ex">>, - <<"lib/fragment.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>, - <<"CHANGELOG.md">>]}. -{<<"build_tools">>,[<<"mix">>]}. diff --git a/plugins/mob_iap/deps/jason/lib/codegen.ex b/plugins/mob_iap/deps/jason/lib/codegen.ex deleted file mode 100644 index 2d7e464..0000000 --- a/plugins/mob_iap/deps/jason/lib/codegen.ex +++ /dev/null @@ -1,139 +0,0 @@ -defmodule Jason.Codegen do - @moduledoc false - - alias Jason.{Encode, EncodeError} - - def jump_table(ranges, default) do - ranges - |> ranges_to_orddict() - |> :array.from_orddict(default) - |> :array.to_orddict() - end - - def jump_table(ranges, default, max) do - ranges - |> ranges_to_orddict() - |> :array.from_orddict(default) - |> resize(max) - |> :array.to_orddict() - end - - defmacro bytecase(var, do: clauses) do - {ranges, default, literals} = clauses_to_ranges(clauses, [], __CALLER__) - - jump_table = jump_table(ranges, default) - - quote do - case unquote(var) do - unquote(jump_table_to_clauses(jump_table, literals)) - end - end - end - - defmacro bytecase(var, max, do: clauses) do - {ranges, default, empty} = clauses_to_ranges(clauses, [], __CALLER__) - - jump_table = jump_table(ranges, default, max) - - quote do - case unquote(var) do - unquote(jump_table_to_clauses(jump_table, empty)) - end - end - end - - def build_kv_iodata(kv, encode_args) do - elements = - kv - |> Enum.map(&encode_pair(&1, encode_args)) - |> Enum.intersperse(",") - - collapse_static(List.flatten(["{", elements] ++ ~c'}')) - end - - defp clauses_to_ranges([{:->, _, [[{:in, _, [byte, range]}, rest], action]} | tail], acc, env) do - range = Macro.expand(range, env) - clauses_to_ranges(tail, [{range, {byte, rest, action}} | acc], env) - end - - defp clauses_to_ranges([{:->, _, [[default, rest], action]} | tail], acc, _env) do - {Enum.reverse(acc), {default, rest, action}, literal_clauses(tail)} - end - - defp literal_clauses(clauses) do - Enum.map(clauses, fn {:->, _, [[literal], action]} -> - {literal, action} - end) - end - - defp jump_table_to_clauses([{val, {{:_, _, _}, rest, action}} | tail], empty) do - quote do - <> -> - unquote(action) - end ++ jump_table_to_clauses(tail, empty) - end - - defp jump_table_to_clauses([{val, {byte, rest, action}} | tail], empty) do - quote do - <> when unquote(byte) === unquote(val) -> - unquote(action) - end ++ jump_table_to_clauses(tail, empty) - end - - defp jump_table_to_clauses([], literals) do - Enum.flat_map(literals, fn {pattern, action} -> - quote do - unquote(pattern) -> - unquote(action) - end - end) - end - - defp resize(array, size), do: :array.resize(size, array) - - defp ranges_to_orddict(ranges) do - ranges - |> Enum.flat_map(fn - {int, value} when is_integer(int) -> - [{int, value}] - - {enum, value} -> - Enum.map(enum, &{&1, value}) - end) - |> :orddict.from_list() - end - - defp encode_pair({key, value}, encode_args) do - key = IO.iodata_to_binary(Encode.key(key, &escape_key/1)) - key = "\"" <> key <> "\":" - [key, quote(do: Encode.value(unquote(value), unquote_splicing(encode_args)))] - end - - defp escape_key(binary) do - check_safe_key!(binary) - binary - end - - defp check_safe_key!(binary) do - for <<(<> <- binary)>> do - if byte > 0x7F or byte < 0x1F or byte in ~c'"\\/' do - raise EncodeError, - "invalid byte #{inspect(byte, base: :hex)} in literal key: #{inspect(binary)}" - end - end - - :ok - end - - defp collapse_static([bin1, bin2 | rest]) when is_binary(bin1) and is_binary(bin2) do - collapse_static([bin1 <> bin2 | rest]) - end - - defp collapse_static([other | rest]) do - [other | collapse_static(rest)] - end - - defp collapse_static([]) do - [] - end -end diff --git a/plugins/mob_iap/deps/jason/lib/decoder.ex b/plugins/mob_iap/deps/jason/lib/decoder.ex deleted file mode 100644 index 7ccd86d..0000000 --- a/plugins/mob_iap/deps/jason/lib/decoder.ex +++ /dev/null @@ -1,708 +0,0 @@ -defmodule Jason.DecodeError do - @type t :: %__MODULE__{position: integer, data: String.t} - - defexception [:position, :token, :data] - - def message(%{position: position, token: token}) when is_binary(token) do - "unexpected sequence at position #{position}: #{inspect token}" - end - def message(%{position: position, data: data}) when position == byte_size(data) do - "unexpected end of input at position #{position}" - end - def message(%{position: position, data: data}) do - byte = :binary.at(data, position) - str = <> - if String.printable?(str) do - "unexpected byte at position #{position}: " <> - "#{inspect byte, base: :hex} (#{inspect str})" - else - "unexpected byte at position #{position}: " <> - "#{inspect byte, base: :hex}" - end - end -end - -defmodule Jason.Decoder do - @moduledoc false - - import Bitwise - - alias Jason.{DecodeError, Codegen} - - import Codegen, only: [bytecase: 2, bytecase: 3] - import Record - - @dialyzer :no_improper_lists - - # @compile :native - - # We use integers instead of atoms to take advantage of the jump table - # optimization - @terminate 0 - @array 1 - @key 2 - @object 3 - - defrecordp :decode, [keys: nil, strings: nil, objects: nil, floats: nil] - - def parse(data, opts) when is_binary(data) do - key_decode = key_decode_function(opts) - string_decode = string_decode_function(opts) - float_decode = float_decode_function(opts) - object_decode = object_decode_function(opts) - decode = decode(keys: key_decode, strings: string_decode, objects: object_decode, floats: float_decode) - try do - value(data, data, 0, [@terminate], decode) - catch - {:position, position} -> - {:error, %DecodeError{position: position, data: data}} - {:token, token, position} -> - {:error, %DecodeError{token: token, position: position, data: data}} - else - value -> - {:ok, value} - end - end - - defp key_decode_function(%{keys: :atoms}), do: &String.to_atom/1 - defp key_decode_function(%{keys: :atoms!}), do: &String.to_existing_atom/1 - defp key_decode_function(%{keys: :strings}), do: &(&1) - defp key_decode_function(%{keys: fun}) when is_function(fun, 1), do: fun - - defp string_decode_function(%{strings: :copy}), do: &:binary.copy/1 - defp string_decode_function(%{strings: :reference}), do: &(&1) - - defp object_decode_function(%{objects: :maps}), do: &:maps.from_list/1 - defp object_decode_function(%{objects: :ordered_objects}), do: &Jason.OrderedObject.new(:lists.reverse(&1)) - - defp float_decode_function(%{floats: :native}) do - fn string, token, skip -> - try do - :erlang.binary_to_float(string) - catch - :error, :badarg -> - token_error(token, skip) - end - end - end - - if Code.ensure_loaded?(Decimal) do - defp float_decode_function(%{floats: :decimals}) do - fn string, token, skip -> - try do - Decimal.new(string) - rescue - Decimal.Error -> - token_error(token, skip) - end - end - end - else - defp float_decode_function(%{floats: :decimals}) do - raise ArgumentError, "decimal library not found, :decimals option not available" - end - end - - defp value(data, original, skip, stack, decode) do - bytecase data do - _ in ~c'\s\n\t\r', rest -> - value(rest, original, skip + 1, stack, decode) - _ in ~c'0', rest -> - number_zero(rest, original, skip, stack, decode, 1) - _ in ~c'123456789', rest -> - number(rest, original, skip, stack, decode, 1) - _ in ~c'-', rest -> - number_minus(rest, original, skip, stack, decode) - _ in ~c'"', rest -> - string(rest, original, skip + 1, stack, decode, 0) - _ in ~c'[', rest -> - array(rest, original, skip + 1, stack, decode) - _ in ~c'{', rest -> - object(rest, original, skip + 1, stack, decode) - _ in ~c']', rest -> - empty_array(rest, original, skip + 1, stack, decode) - _ in ~c't', rest -> - case rest do - <<"rue", rest::bits>> -> - continue(rest, original, skip + 4, stack, decode, true) - <<_::bits>> -> - error(original, skip) - end - _ in ~c'f', rest -> - case rest do - <<"alse", rest::bits>> -> - continue(rest, original, skip + 5, stack, decode, false) - <<_::bits>> -> - error(original, skip) - end - _ in ~c'n', rest -> - case rest do - <<"ull", rest::bits>> -> - continue(rest, original, skip + 4, stack, decode, nil) - <<_::bits>> -> - error(original, skip) - end - _, rest -> - error(rest, original, skip + 1, stack, decode) - <<_::bits>> -> - error(original, skip) - end - end - - defp number_minus(<>, original, skip, stack, decode) do - number_zero(rest, original, skip, stack, decode, 2) - end - defp number_minus(<>, original, skip, stack, decode) - when byte in ~c'123456789' do - number(rest, original, skip, stack, decode, 2) - end - defp number_minus(<<_rest::bits>>, original, skip, _stack, _decode) do - error(original, skip + 1) - end - - if function_exported?(Application, :compile_env, 3) do - @integer_digit_limit Application.compile_env(:jason, :decoding_integer_digit_limit, 1024) - else - # use apply to avoid warnings in newer Elixir versions - @integer_digit_limit apply(Application, :get_env, [:jason, :decoding_integer_digit_limit, 1024]) - end - - defp number(<>, original, skip, stack, decode, len) - when byte in ~c'0123456789' do - number(rest, original, skip, stack, decode, len + 1) - end - defp number(<>, original, skip, stack, decode, len) do - number_frac(rest, original, skip, stack, decode, len + 1) - end - defp number(<>, original, skip, stack, decode, len) when e in ~c'eE' do - prefix = binary_part(original, skip, len) - number_exp_copy(rest, original, skip + len + 1, stack, decode, prefix) - end - defp number(<>, original, skip, stack, decode, len) do - token = binary_part(original, skip, len) - if byte_size(token) > @integer_digit_limit do - token_error(token, skip) - end - int = String.to_integer(token) - continue(rest, original, skip + len, stack, decode, int) - end - - defp number_frac(<>, original, skip, stack, decode, len) - when byte in ~c'0123456789' do - number_frac_cont(rest, original, skip, stack, decode, len + 1) - end - defp number_frac(<<_rest::bits>>, original, skip, _stack, _decode, len) do - error(original, skip + len) - end - - defp number_frac_cont(<>, original, skip, stack, decode, len) - when byte in ~c'0123456789' do - number_frac_cont(rest, original, skip, stack, decode, len + 1) - end - defp number_frac_cont(<>, original, skip, stack, decode, len) - when e in ~c'eE' do - number_exp(rest, original, skip, stack, decode, len + 1) - end - defp number_frac_cont(<>, original, skip, stack, decode, len) do - token = binary_part(original, skip, len) - decode(floats: float_decode) = decode - float = float_decode.(token, token, skip) - continue(rest, original, skip + len, stack, decode, float) - end - - defp number_exp(<>, original, skip, stack, decode, len) - when byte in ~c'0123456789' do - number_exp_cont(rest, original, skip, stack, decode, len + 1) - end - defp number_exp(<>, original, skip, stack, decode, len) - when byte in ~c'+-' do - number_exp_sign(rest, original, skip, stack, decode, len + 1) - end - defp number_exp(<<_rest::bits>>, original, skip, _stack, _decode, len) do - error(original, skip + len) - end - - defp number_exp_sign(<>, original, skip, stack, decode, len) - when byte in ~c'0123456789' do - number_exp_cont(rest, original, skip, stack, decode, len + 1) - end - defp number_exp_sign(<<_rest::bits>>, original, skip, _stack, _decode, len) do - error(original, skip + len) - end - - defp number_exp_cont(<>, original, skip, stack, decode, len) - when byte in ~c'0123456789' do - number_exp_cont(rest, original, skip, stack, decode, len + 1) - end - defp number_exp_cont(<>, original, skip, stack, decode, len) do - token = binary_part(original, skip, len) - decode(floats: float_decode) = decode - float = float_decode.(token, token, skip) - continue(rest, original, skip + len, stack, decode, float) - end - - defp number_exp_copy(<>, original, skip, stack, decode, prefix) - when byte in ~c'0123456789' do - number_exp_cont(rest, original, skip, stack, decode, prefix, 1) - end - defp number_exp_copy(<>, original, skip, stack, decode, prefix) - when byte in ~c'+-' do - number_exp_sign(rest, original, skip, stack, decode, prefix, 1) - end - defp number_exp_copy(<<_rest::bits>>, original, skip, _stack, _decode, _prefix) do - error(original, skip) - end - - defp number_exp_sign(<>, original, skip, stack, decode, prefix, len) - when byte in ~c'0123456789' do - number_exp_cont(rest, original, skip, stack, decode, prefix, len + 1) - end - defp number_exp_sign(<<_rest::bits>>, original, skip, _stack, _decode, _prefix, len) do - error(original, skip + len) - end - - defp number_exp_cont(<>, original, skip, stack, decode, prefix, len) - when byte in ~c'0123456789' do - number_exp_cont(rest, original, skip, stack, decode, prefix, len + 1) - end - defp number_exp_cont(<>, original, skip, stack, decode, prefix, len) do - suffix = binary_part(original, skip, len) - string = prefix <> ".0e" <> suffix - prefix_size = byte_size(prefix) - initial_skip = skip - prefix_size - 1 - final_skip = skip + len - token = binary_part(original, initial_skip, prefix_size + len + 1) - decode(floats: float_decode) = decode - float = float_decode.(string, token, initial_skip) - continue(rest, original, final_skip, stack, decode, float) - end - - defp number_zero(<>, original, skip, stack, decode, len) do - number_frac(rest, original, skip, stack, decode, len + 1) - end - defp number_zero(<>, original, skip, stack, decode, len) when e in ~c'eE' do - number_exp_copy(rest, original, skip + len + 1, stack, decode, "0") - end - defp number_zero(<>, original, skip, stack, decode, len) do - continue(rest, original, skip + len, stack, decode, 0) - end - - @compile {:inline, array: 5} - - defp array(rest, original, skip, stack, decode) do - value(rest, original, skip, [@array, [] | stack], decode) - end - - defp empty_array(<>, original, skip, stack, decode) do - case stack do - [@array, [] | stack] -> - continue(rest, original, skip, stack, decode, []) - _ -> - error(original, skip - 1) - end - end - - defp array(data, original, skip, stack, decode, value) do - bytecase data do - _ in ~c'\s\n\t\r', rest -> - array(rest, original, skip + 1, stack, decode, value) - _ in ~c']', rest -> - [acc | stack] = stack - value = :lists.reverse(acc, [value]) - continue(rest, original, skip + 1, stack, decode, value) - _ in ~c',', rest -> - [acc | stack] = stack - value(rest, original, skip + 1, [@array, [value | acc] | stack], decode) - _, _rest -> - error(original, skip) - <<_::bits>> -> - empty_error(original, skip) - end - end - - @compile {:inline, object: 5} - - defp object(rest, original, skip, stack, decode) do - key(rest, original, skip, [[] | stack], decode) - end - - defp object(data, original, skip, stack, decode, value) do - bytecase data do - _ in ~c'\s\n\t\r', rest -> - object(rest, original, skip + 1, stack, decode, value) - _ in ~c'}', rest -> - skip = skip + 1 - [key, acc | stack] = stack - decode(keys: key_decode) = decode - final = [{key_decode.(key), value} | acc] - decode(objects: object_decode) = decode - continue(rest, original, skip, stack, decode, object_decode.(final)) - _ in ~c',', rest -> - skip = skip + 1 - [key, acc | stack] = stack - decode(keys: key_decode) = decode - acc = [{key_decode.(key), value} | acc] - key(rest, original, skip, [acc | stack], decode) - _, _rest -> - error(original, skip) - <<_::bits>> -> - empty_error(original, skip) - end - end - - defp key(data, original, skip, stack, decode) do - bytecase data do - _ in ~c'\s\n\t\r', rest -> - key(rest, original, skip + 1, stack, decode) - _ in ~c'}', rest -> - case stack do - [[] | stack] -> - decode(objects: object_decode) = decode - continue(rest, original, skip + 1, stack, decode, object_decode.([])) - _ -> - error(original, skip) - end - _ in ~c'"', rest -> - string(rest, original, skip + 1, [@key | stack], decode, 0) - _, _rest -> - error(original, skip) - <<_::bits>> -> - empty_error(original, skip) - end - end - - defp key(data, original, skip, stack, decode, value) do - bytecase data do - _ in ~c'\s\n\t\r', rest -> - key(rest, original, skip + 1, stack, decode, value) - _ in ~c':', rest -> - value(rest, original, skip + 1, [@object, value | stack], decode) - _, _rest -> - error(original, skip) - <<_::bits>> -> - empty_error(original, skip) - end - end - - # TODO: check if this approach would be faster: - # https://git.ninenines.eu/cowlib.git/tree/src/cow_ws.erl#n469 - # http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - defp string(data, original, skip, stack, decode, len) do - bytecase data, 128 do - _ in ~c'"', rest -> - decode(strings: string_decode) = decode - string = string_decode.(binary_part(original, skip, len)) - continue(rest, original, skip + len + 1, stack, decode, string) - _ in ~c'\\', rest -> - part = binary_part(original, skip, len) - escape(rest, original, skip + len, stack, decode, part) - _ in unquote(0x00..0x1F), _rest -> - error(original, skip + len) - _, rest -> - string(rest, original, skip, stack, decode, len + 1) - <> when char <= 0x7FF -> - string(rest, original, skip, stack, decode, len + 2) - <> when char <= 0xFFFF -> - string(rest, original, skip, stack, decode, len + 3) - <<_char::utf8, rest::bits>> -> - string(rest, original, skip, stack, decode, len + 4) - <<_::bits>> -> - empty_error(original, skip + len) - end - end - - defp string(data, original, skip, stack, decode, acc, len) do - bytecase data, 128 do - _ in ~c'"', rest -> - last = binary_part(original, skip, len) - string = IO.iodata_to_binary([acc | last]) - continue(rest, original, skip + len + 1, stack, decode, string) - _ in ~c'\\', rest -> - part = binary_part(original, skip, len) - escape(rest, original, skip + len, stack, decode, [acc | part]) - _ in unquote(0x00..0x1F), _rest -> - error(original, skip + len) - _, rest -> - string(rest, original, skip, stack, decode, acc, len + 1) - <> when char <= 0x7FF -> - string(rest, original, skip, stack, decode, acc, len + 2) - <> when char <= 0xFFFF -> - string(rest, original, skip, stack, decode, acc, len + 3) - <<_char::utf8, rest::bits>> -> - string(rest, original, skip, stack, decode, acc, len + 4) - <<_::bits>> -> - empty_error(original, skip + len) - end - end - - defp escape(data, original, skip, stack, decode, acc) do - bytecase data do - _ in ~c'b', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\b'], 0) - _ in ~c't', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\t'], 0) - _ in ~c'n', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\n'], 0) - _ in ~c'f', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\f'], 0) - _ in ~c'r', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\r'], 0) - _ in ~c'"', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\"'], 0) - _ in ~c'/', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'/'], 0) - _ in ~c'\\', rest -> - string(rest, original, skip + 2, stack, decode, [acc | ~c'\\'], 0) - _ in ~c'u', rest -> - escapeu(rest, original, skip, stack, decode, acc) - _, _rest -> - error(original, skip + 1) - <<_::bits>> -> - empty_error(original, skip) - end - end - - defmodule Unescape do - @moduledoc false - - import Bitwise - - @digits Enum.concat([?0..?9, ?A..?F, ?a..?f]) - - def unicode_escapes(chars1 \\ @digits, chars2 \\ @digits) do - for char1 <- chars1, char2 <- chars2 do - {(char1 <<< 8) + char2, integer8(char1, char2)} - end - end - - defp integer8(char1, char2) do - (integer4(char1) <<< 4) + integer4(char2) - end - - defp integer4(char) when char in ?0..?9, do: char - ?0 - defp integer4(char) when char in ?A..?F, do: char - ?A + 10 - defp integer4(char) when char in ?a..?f, do: char - ?a + 10 - - defp token_error_clause(original, skip, len) do - quote do - _ -> - token_error(unquote_splicing([original, skip, len])) - end - end - - defmacro escapeu_first(int, last, rest, original, skip, stack, decode, acc) do - clauses = escapeu_first_clauses(last, rest, original, skip, stack, decode, acc) - quote location: :keep do - case unquote(int) do - unquote(clauses ++ token_error_clause(original, skip, 6)) - end - end - end - - defp escapeu_first_clauses(last, rest, original, skip, stack, decode, acc) do - for {int, first} <- unicode_escapes(), - not (first in 0xDC..0xDF) do - escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) - end - end - - defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) - when first in 0xD8..0xDB do - hi = - quote bind_quoted: [first: first, last: last] do - 0x10000 + ((((first &&& 0x03) <<< 8) + last) <<< 10) - end - args = [rest, original, skip, stack, decode, acc, hi] - [clause] = - quote location: :keep do - unquote(int) -> escape_surrogate(unquote_splicing(args)) - end - clause - end - - defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) - when first <= 0x00 do - skip = quote do: (unquote(skip) + 6) - acc = - quote bind_quoted: [acc: acc, first: first, last: last] do - if last <= 0x7F do - # 0????? - [acc, last] - else - # 110xxxx?? 10????? - byte1 = ((0b110 <<< 5) + (first <<< 2)) + (last >>> 6) - byte2 = (0b10 <<< 6) + (last &&& 0b111111) - [acc, byte1, byte2] - end - end - args = [rest, original, skip, stack, decode, acc, 0] - [clause] = - quote location: :keep do - unquote(int) -> string(unquote_splicing(args)) - end - clause - end - - defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) - when first <= 0x07 do - skip = quote do: (unquote(skip) + 6) - acc = - quote bind_quoted: [acc: acc, first: first, last: last] do - # 110xxx?? 10?????? - byte1 = ((0b110 <<< 5) + (first <<< 2)) + (last >>> 6) - byte2 = (0b10 <<< 6) + (last &&& 0b111111) - [acc, byte1, byte2] - end - args = [rest, original, skip, stack, decode, acc, 0] - [clause] = - quote location: :keep do - unquote(int) -> string(unquote_splicing(args)) - end - clause - end - - defp escapeu_first_clause(int, first, last, rest, original, skip, stack, decode, acc) - when first <= 0xFF do - skip = quote do: (unquote(skip) + 6) - acc = - quote bind_quoted: [acc: acc, first: first, last: last] do - # 1110xxxx 10xxxx?? 10?????? - byte1 = (0b1110 <<< 4) + (first >>> 4) - byte2 = ((0b10 <<< 6) + ((first &&& 0b1111) <<< 2)) + (last >>> 6) - byte3 = (0b10 <<< 6) + (last &&& 0b111111) - [acc, byte1, byte2, byte3] - end - args = [rest, original, skip, stack, decode, acc, 0] - [clause] = - quote location: :keep do - unquote(int) -> string(unquote_splicing(args)) - end - clause - end - - defmacro escapeu_last(int, original, skip) do - clauses = escapeu_last_clauses() - quote location: :keep do - case unquote(int) do - unquote(clauses ++ token_error_clause(original, skip, 6)) - end - end - end - - defp escapeu_last_clauses() do - for {int, last} <- unicode_escapes() do - [clause] = - quote do - unquote(int) -> unquote(last) - end - clause - end - end - - defmacro escapeu_surrogate(int, last, rest, original, skip, stack, decode, acc, - hi) do - clauses = escapeu_surrogate_clauses(last, rest, original, skip, stack, decode, acc, hi) - quote location: :keep do - case unquote(int) do - unquote(clauses ++ token_error_clause(original, skip, 12)) - end - end - end - - defp escapeu_surrogate_clauses(last, rest, original, skip, stack, decode, acc, hi) do - digits1 = ~c'Dd' - digits2 = Stream.concat([?C..?F, ?c..?f]) - for {int, first} <- unicode_escapes(digits1, digits2) do - escapeu_surrogate_clause(int, first, last, rest, original, skip, stack, decode, acc, hi) - end - end - - defp escapeu_surrogate_clause(int, first, last, rest, original, skip, stack, decode, acc, hi) do - skip = quote do: unquote(skip) + 12 - acc = - quote bind_quoted: [acc: acc, first: first, last: last, hi: hi] do - lo = ((first &&& 0x03) <<< 8) + last - [acc | <<(hi + lo)::utf8>>] - end - args = [rest, original, skip, stack, decode, acc, 0] - [clause] = - quote do - unquote(int) -> - string(unquote_splicing(args)) - end - clause - end - end - - defp escapeu(<>, original, skip, stack, decode, acc) do - require Unescape - last = escapeu_last(int2, original, skip) - Unescape.escapeu_first(int1, last, rest, original, skip, stack, decode, acc) - end - defp escapeu(<<_rest::bits>>, original, skip, _stack, _decode, _acc) do - empty_error(original, skip) - end - - # @compile {:inline, escapeu_last: 3} - - defp escapeu_last(int, original, skip) do - require Unescape - Unescape.escapeu_last(int, original, skip) - end - - defp escape_surrogate(<>, original, - skip, stack, decode, acc, hi) do - require Unescape - last = escapeu_last(int2, original, skip + 6) - Unescape.escapeu_surrogate(int1, last, rest, original, skip, stack, decode, acc, hi) - end - defp escape_surrogate(<<_rest::bits>>, original, skip, _stack, _decode, _acc, _hi) do - error(original, skip + 6) - end - - defp error(<<_rest::bits>>, _original, skip, _stack, _decode) do - throw {:position, skip - 1} - end - - defp empty_error(_original, skip) do - throw {:position, skip} - end - - @compile {:inline, error: 2, token_error: 2, token_error: 3} - defp error(_original, skip) do - throw {:position, skip} - end - - defp token_error(token, position) do - throw {:token, token, position} - end - - defp token_error(token, position, len) do - throw {:token, binary_part(token, position, len), position} - end - - @compile {:inline, continue: 6} - defp continue(rest, original, skip, stack, decode, value) do - case stack do - [@terminate | stack] -> - terminate(rest, original, skip, stack, decode, value) - [@array | stack] -> - array(rest, original, skip, stack, decode, value) - [@key | stack] -> - key(rest, original, skip, stack, decode, value) - [@object | stack] -> - object(rest, original, skip, stack, decode, value) - end - end - - defp terminate(<>, original, skip, stack, decode, value) - when byte in ~c'\s\n\r\t' do - terminate(rest, original, skip + 1, stack, decode, value) - end - defp terminate(<<>>, _original, _skip, _stack, _decode, value) do - value - end - defp terminate(<<_rest::bits>>, original, skip, _stack, _decode, _value) do - error(original, skip) - end -end diff --git a/plugins/mob_iap/deps/jason/lib/encode.ex b/plugins/mob_iap/deps/jason/lib/encode.ex deleted file mode 100644 index ffff65c..0000000 --- a/plugins/mob_iap/deps/jason/lib/encode.ex +++ /dev/null @@ -1,657 +0,0 @@ -defmodule Jason.EncodeError do - defexception [:message] - - @type t :: %__MODULE__{message: String.t} - - def new({:duplicate_key, key}) do - %__MODULE__{message: "duplicate key: #{key}"} - end - def new({:invalid_byte, byte, original}) do - %__MODULE__{message: "invalid byte #{inspect byte, base: :hex} in #{inspect original}"} - end -end - -defmodule Jason.Encode do - @moduledoc """ - Utilities for encoding elixir values to JSON. - """ - - import Bitwise - - alias Jason.{Codegen, EncodeError, Encoder, Fragment, OrderedObject} - - @typep escape :: (String.t -> iodata) - @typep encode_map :: (map, escape, encode_map -> iodata) - @opaque opts :: {escape, encode_map} - - @dialyzer :no_improper_lists - - # @compile :native - - @doc false - @spec encode(any, map) :: {:ok, iodata} | {:error, EncodeError.t | Exception.t} - def encode(value, opts) do - escape = escape_function(opts) - encode_map = encode_map_function(opts) - try do - {:ok, value(value, escape, encode_map)} - catch - :throw, %EncodeError{} = e -> - {:error, e} - :error, %Protocol.UndefinedError{protocol: Jason.Encoder} = e -> - {:error, e} - end - end - - defp encode_map_function(%{maps: maps}) do - case maps do - :naive -> &map_naive/3 - :strict -> &map_strict/3 - end - end - - defp escape_function(%{escape: escape}) do - case escape do - :json -> &escape_json/1 - :html_safe -> &escape_html/1 - :unicode_safe -> &escape_unicode/1 - :javascript_safe -> &escape_javascript/1 - # Keep for compatibility with Poison - :javascript -> &escape_javascript/1 - :unicode -> &escape_unicode/1 - end - end - - @doc """ - Equivalent to calling the `Jason.Encoder.encode/2` protocol function. - - Slightly more efficient for built-in types because of the internal dispatching. - """ - @spec value(term, opts) :: iodata - def value(value, {escape, encode_map}) do - value(value, escape, encode_map) - end - - @doc false - # We use this directly in the helpers and deriving for extra speed - def value(value, escape, _encode_map) when is_atom(value) do - encode_atom(value, escape) - end - - def value(value, escape, _encode_map) when is_binary(value) do - encode_string(value, escape) - end - - def value(value, _escape, _encode_map) when is_integer(value) do - integer(value) - end - - def value(value, _escape, _encode_map) when is_float(value) do - float(value) - end - - def value(value, escape, encode_map) when is_list(value) do - list(value, escape, encode_map) - end - - def value(%{__struct__: module} = value, escape, encode_map) do - struct(value, escape, encode_map, module) - end - - def value(value, escape, encode_map) when is_map(value) do - case Map.to_list(value) do - [] -> "{}" - keyword -> encode_map.(keyword, escape, encode_map) - end - end - - def value(value, escape, encode_map) do - Encoder.encode(value, {escape, encode_map}) - end - - @compile {:inline, integer: 1, float: 1} - - @spec atom(atom, opts) :: iodata - def atom(atom, {escape, _encode_map}) do - encode_atom(atom, escape) - end - - defp encode_atom(nil, _escape), do: "null" - defp encode_atom(true, _escape), do: "true" - defp encode_atom(false, _escape), do: "false" - defp encode_atom(atom, escape), - do: encode_string(Atom.to_string(atom), escape) - - @spec integer(integer) :: iodata - def integer(integer) do - Integer.to_string(integer) - end - - has_short_format = try do - :erlang.float_to_binary(1.0, [:short]) - catch - _, _ -> false - else - _ -> true - end - - @spec float(float) :: iodata - if has_short_format do - def float(float) do - :erlang.float_to_binary(float, [:short]) - end - else - def float(float) do - :io_lib_format.fwrite_g(float) - end - end - - @spec list(list, opts) :: iodata - def list(list, {escape, encode_map}) do - list(list, escape, encode_map) - end - - defp list([], _escape, _encode_map) do - "[]" - end - - defp list([head | tail], escape, encode_map) do - [?[, value(head, escape, encode_map) - | list_loop(tail, escape, encode_map)] - end - - defp list_loop([], _escape, _encode_map) do - ~c']' - end - - defp list_loop([head | tail], escape, encode_map) do - [?,, value(head, escape, encode_map) - | list_loop(tail, escape, encode_map)] - end - - @spec keyword(keyword, opts) :: iodata - def keyword(list, _) when list == [], do: "{}" - def keyword(list, {escape, encode_map}) when is_list(list) do - encode_map.(list, escape, encode_map) - end - - @spec map(map, opts) :: iodata - def map(value, {escape, encode_map}) do - case Map.to_list(value) do - [] -> "{}" - keyword -> encode_map.(keyword, escape, encode_map) - end - end - - defp map_naive([{key, value} | tail], escape, encode_map) do - ["{\"", key(key, escape), "\":", - value(value, escape, encode_map) - | map_naive_loop(tail, escape, encode_map)] - end - - defp map_naive_loop([], _escape, _encode_map) do - ~c'}' - end - - defp map_naive_loop([{key, value} | tail], escape, encode_map) do - [",\"", key(key, escape), "\":", - value(value, escape, encode_map) - | map_naive_loop(tail, escape, encode_map)] - end - - defp map_strict([{key, value} | tail], escape, encode_map) do - key = IO.iodata_to_binary(key(key, escape)) - visited = %{key => []} - ["{\"", key, "\":", - value(value, escape, encode_map) - | map_strict_loop(tail, escape, encode_map, visited)] - end - - defp map_strict_loop([], _encode_map, _escape, _visited) do - ~c'}' - end - - defp map_strict_loop([{key, value} | tail], escape, encode_map, visited) do - key = IO.iodata_to_binary(key(key, escape)) - case visited do - %{^key => _} -> - error({:duplicate_key, key}) - _ -> - visited = Map.put(visited, key, []) - [",\"", key, "\":", - value(value, escape, encode_map) - | map_strict_loop(tail, escape, encode_map, visited)] - end - end - - @spec struct(struct, opts) :: iodata - def struct(%module{} = value, {escape, encode_map}) do - struct(value, escape, encode_map, module) - end - - # TODO: benchmark the effect of inlining the to_iso8601 functions - for module <- [Date, Time, NaiveDateTime, DateTime] do - defp struct(value, _escape, _encode_map, unquote(module)) do - [?", unquote(module).to_iso8601(value), ?"] - end - end - - if Code.ensure_loaded?(Decimal) do - defp struct(value, _escape, _encode_map, Decimal) do - [?", Decimal.to_string(value, :normal), ?"] - end - end - - defp struct(value, escape, encode_map, Fragment) do - %{encode: encode} = value - encode.({escape, encode_map}) - end - - defp struct(value, escape, encode_map, OrderedObject) do - case value do - %{values: []} -> "{}" - %{values: values} -> encode_map.(values, escape, encode_map) - end - end - - defp struct(value, escape, encode_map, _module) do - Encoder.encode(value, {escape, encode_map}) - end - - @doc false - # This is used in the helpers and deriving implementation - def key(string, escape) when is_binary(string) do - escape.(string) - end - def key(atom, escape) when is_atom(atom) do - string = Atom.to_string(atom) - escape.(string) - end - def key(other, escape) do - string = String.Chars.to_string(other) - escape.(string) - end - - @spec string(String.t, opts) :: iodata - def string(string, {escape, _encode_map}) do - encode_string(string, escape) - end - - defp encode_string(string, escape) do - [?", escape.(string), ?"] - end - - slash_escapes = Enum.zip(~c'\b\t\n\f\r\"\\', ~c'btnfr"\\') - surogate_escapes = Enum.zip([0x2028, 0x2029], ["\\u2028", "\\u2029"]) - ranges = [{0x00..0x1F, :unicode} | slash_escapes] - html_ranges = [{0x00..0x1F, :unicode}, {?<, :unicode}, {?/, ?/} | slash_escapes] - escape_jt = Codegen.jump_table(html_ranges, :error) - - Enum.each(escape_jt, fn - {byte, :unicode} -> - sequence = List.to_string(:io_lib.format("\\u~4.16.0B", [byte])) - defp escape(unquote(byte)), do: unquote(sequence) - {byte, char} when is_integer(char) -> - defp escape(unquote(byte)), do: unquote(<>) - {byte, :error} -> - defp escape(unquote(byte)), do: throw(:error) - end) - - ## regular JSON escape - - json_jt = Codegen.jump_table(ranges, :chunk, 0x7F + 1) - - defp escape_json(data) do - escape_json(data, [], data, 0) - end - - Enum.map(json_jt, fn - {byte, :chunk} -> - defp escape_json(<>, acc, original, skip) - when byte === unquote(byte) do - escape_json_chunk(rest, acc, original, skip, 1) - end - {byte, _escape} -> - defp escape_json(<>, acc, original, skip) - when byte === unquote(byte) do - acc = [acc | escape(byte)] - escape_json(rest, acc, original, skip + 1) - end - end) - defp escape_json(<>, acc, original, skip) - when char <= 0x7FF do - escape_json_chunk(rest, acc, original, skip, 2) - end - defp escape_json(<>, acc, original, skip) - when char <= 0xFFFF do - escape_json_chunk(rest, acc, original, skip, 3) - end - defp escape_json(<<_char::utf8, rest::bits>>, acc, original, skip) do - escape_json_chunk(rest, acc, original, skip, 4) - end - defp escape_json(<<>>, acc, _original, _skip) do - acc - end - defp escape_json(<>, _acc, original, _skip) do - error({:invalid_byte, byte, original}) - end - - Enum.map(json_jt, fn - {byte, :chunk} -> - defp escape_json_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - escape_json_chunk(rest, acc, original, skip, len + 1) - end - {byte, _escape} -> - defp escape_json_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - part = binary_part(original, skip, len) - acc = [acc, part | escape(byte)] - escape_json(rest, acc, original, skip + len + 1) - end - end) - defp escape_json_chunk(<>, acc, original, skip, len) - when char <= 0x7FF do - escape_json_chunk(rest, acc, original, skip, len + 2) - end - defp escape_json_chunk(<>, acc, original, skip, len) - when char <= 0xFFFF do - escape_json_chunk(rest, acc, original, skip, len + 3) - end - defp escape_json_chunk(<<_char::utf8, rest::bits>>, acc, original, skip, len) do - escape_json_chunk(rest, acc, original, skip, len + 4) - end - defp escape_json_chunk(<<>>, acc, original, skip, len) do - part = binary_part(original, skip, len) - [acc | part] - end - defp escape_json_chunk(<>, _acc, original, _skip, _len) do - error({:invalid_byte, byte, original}) - end - - ## javascript safe JSON escape - - defp escape_javascript(data) do - escape_javascript(data, [], data, 0) - end - - Enum.map(json_jt, fn - {byte, :chunk} -> - defp escape_javascript(<>, acc, original, skip) - when byte === unquote(byte) do - escape_javascript_chunk(rest, acc, original, skip, 1) - end - {byte, _escape} -> - defp escape_javascript(<>, acc, original, skip) - when byte === unquote(byte) do - acc = [acc | escape(byte)] - escape_javascript(rest, acc, original, skip + 1) - end - end) - defp escape_javascript(<>, acc, original, skip) - when char <= 0x7FF do - escape_javascript_chunk(rest, acc, original, skip, 2) - end - Enum.map(surogate_escapes, fn {byte, escape} -> - defp escape_javascript(<>, acc, original, skip) do - acc = [acc | unquote(escape)] - escape_javascript(rest, acc, original, skip + 3) - end - end) - defp escape_javascript(<>, acc, original, skip) - when char <= 0xFFFF do - escape_javascript_chunk(rest, acc, original, skip, 3) - end - defp escape_javascript(<<_char::utf8, rest::bits>>, acc, original, skip) do - escape_javascript_chunk(rest, acc, original, skip, 4) - end - defp escape_javascript(<<>>, acc, _original, _skip) do - acc - end - defp escape_javascript(<>, _acc, original, _skip) do - error({:invalid_byte, byte, original}) - end - - Enum.map(json_jt, fn - {byte, :chunk} -> - defp escape_javascript_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - escape_javascript_chunk(rest, acc, original, skip, len + 1) - end - {byte, _escape} -> - defp escape_javascript_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - part = binary_part(original, skip, len) - acc = [acc, part | escape(byte)] - escape_javascript(rest, acc, original, skip + len + 1) - end - end) - defp escape_javascript_chunk(<>, acc, original, skip, len) - when char <= 0x7FF do - escape_javascript_chunk(rest, acc, original, skip, len + 2) - end - Enum.map(surogate_escapes, fn {byte, escape} -> - defp escape_javascript_chunk(<>, acc, original, skip, len) do - part = binary_part(original, skip, len) - acc = [acc, part | unquote(escape)] - escape_javascript(rest, acc, original, skip + len + 3) - end - end) - defp escape_javascript_chunk(<>, acc, original, skip, len) - when char <= 0xFFFF do - escape_javascript_chunk(rest, acc, original, skip, len + 3) - end - defp escape_javascript_chunk(<<_char::utf8, rest::bits>>, acc, original, skip, len) do - escape_javascript_chunk(rest, acc, original, skip, len + 4) - end - defp escape_javascript_chunk(<<>>, acc, original, skip, len) do - part = binary_part(original, skip, len) - [acc | part] - end - defp escape_javascript_chunk(<>, _acc, original, _skip, _len) do - error({:invalid_byte, byte, original}) - end - - ## HTML safe JSON escape - - html_jt = Codegen.jump_table(html_ranges, :chunk, 0x7F + 1) - - defp escape_html(data) do - escape_html(data, [], data, 0) - end - - Enum.map(html_jt, fn - {byte, :chunk} -> - defp escape_html(<>, acc, original, skip) - when byte === unquote(byte) do - escape_html_chunk(rest, acc, original, skip, 1) - end - {byte, _escape} -> - defp escape_html(<>, acc, original, skip) - when byte === unquote(byte) do - acc = [acc | escape(byte)] - escape_html(rest, acc, original, skip + 1) - end - end) - defp escape_html(<>, acc, original, skip) - when char <= 0x7FF do - escape_html_chunk(rest, acc, original, skip, 2) - end - Enum.map(surogate_escapes, fn {byte, escape} -> - defp escape_html(<>, acc, original, skip) do - acc = [acc | unquote(escape)] - escape_html(rest, acc, original, skip + 3) - end - end) - defp escape_html(<>, acc, original, skip) - when char <= 0xFFFF do - escape_html_chunk(rest, acc, original, skip, 3) - end - defp escape_html(<<_char::utf8, rest::bits>>, acc, original, skip) do - escape_html_chunk(rest, acc, original, skip, 4) - end - defp escape_html(<<>>, acc, _original, _skip) do - acc - end - defp escape_html(<>, _acc, original, _skip) do - error({:invalid_byte, byte, original}) - end - - Enum.map(html_jt, fn - {byte, :chunk} -> - defp escape_html_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - escape_html_chunk(rest, acc, original, skip, len + 1) - end - {byte, _escape} -> - defp escape_html_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - part = binary_part(original, skip, len) - acc = [acc, part | escape(byte)] - escape_html(rest, acc, original, skip + len + 1) - end - end) - defp escape_html_chunk(<>, acc, original, skip, len) - when char <= 0x7FF do - escape_html_chunk(rest, acc, original, skip, len + 2) - end - Enum.map(surogate_escapes, fn {byte, escape} -> - defp escape_html_chunk(<>, acc, original, skip, len) do - part = binary_part(original, skip, len) - acc = [acc, part | unquote(escape)] - escape_html(rest, acc, original, skip + len + 3) - end - end) - defp escape_html_chunk(<>, acc, original, skip, len) - when char <= 0xFFFF do - escape_html_chunk(rest, acc, original, skip, len + 3) - end - defp escape_html_chunk(<<_char::utf8, rest::bits>>, acc, original, skip, len) do - escape_html_chunk(rest, acc, original, skip, len + 4) - end - defp escape_html_chunk(<<>>, acc, original, skip, len) do - part = binary_part(original, skip, len) - [acc | part] - end - defp escape_html_chunk(<>, _acc, original, _skip, _len) do - error({:invalid_byte, byte, original}) - end - - ## unicode escape - - defp escape_unicode(data) do - escape_unicode(data, [], data, 0) - end - - Enum.map(json_jt, fn - {byte, :chunk} -> - defp escape_unicode(<>, acc, original, skip) - when byte === unquote(byte) do - escape_unicode_chunk(rest, acc, original, skip, 1) - end - {byte, _escape} -> - defp escape_unicode(<>, acc, original, skip) - when byte === unquote(byte) do - acc = [acc | escape(byte)] - escape_unicode(rest, acc, original, skip + 1) - end - end) - defp escape_unicode(<>, acc, original, skip) - when char <= 0xFF do - acc = [acc, "\\u00" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + 2) - end - defp escape_unicode(<>, acc, original, skip) - when char <= 0x7FF do - acc = [acc, "\\u0" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + 2) - end - defp escape_unicode(<>, acc, original, skip) - when char <= 0xFFF do - acc = [acc, "\\u0" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + 3) - end - defp escape_unicode(<>, acc, original, skip) - when char <= 0xFFFF do - acc = [acc, "\\u" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + 3) - end - defp escape_unicode(<>, acc, original, skip) do - char = char - 0x10000 - acc = - [ - acc, - "\\uD", Integer.to_string(0x800 ||| (char >>> 10), 16), - "\\uD" | Integer.to_string(0xC00 ||| (char &&& 0x3FF), 16) - ] - escape_unicode(rest, acc, original, skip + 4) - end - defp escape_unicode(<<>>, acc, _original, _skip) do - acc - end - defp escape_unicode(<>, _acc, original, _skip) do - error({:invalid_byte, byte, original}) - end - - Enum.map(json_jt, fn - {byte, :chunk} -> - defp escape_unicode_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - escape_unicode_chunk(rest, acc, original, skip, len + 1) - end - {byte, _escape} -> - defp escape_unicode_chunk(<>, acc, original, skip, len) - when byte === unquote(byte) do - part = binary_part(original, skip, len) - acc = [acc, part | escape(byte)] - escape_unicode(rest, acc, original, skip + len + 1) - end - end) - defp escape_unicode_chunk(<>, acc, original, skip, len) - when char <= 0xFF do - part = binary_part(original, skip, len) - acc = [acc, part, "\\u00" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + len + 2) - end - defp escape_unicode_chunk(<>, acc, original, skip, len) - when char <= 0x7FF do - part = binary_part(original, skip, len) - acc = [acc, part, "\\u0" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + len + 2) - end - defp escape_unicode_chunk(<>, acc, original, skip, len) - when char <= 0xFFF do - part = binary_part(original, skip, len) - acc = [acc, part, "\\u0" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + len + 3) - end - defp escape_unicode_chunk(<>, acc, original, skip, len) - when char <= 0xFFFF do - part = binary_part(original, skip, len) - acc = [acc, part, "\\u" | Integer.to_string(char, 16)] - escape_unicode(rest, acc, original, skip + len + 3) - end - defp escape_unicode_chunk(<>, acc, original, skip, len) do - char = char - 0x10000 - part = binary_part(original, skip, len) - acc = - [ - acc, part, - "\\uD", Integer.to_string(0x800 ||| (char >>> 10), 16), - "\\uD" | Integer.to_string(0xC00 ||| (char &&& 0x3FF), 16) - ] - escape_unicode(rest, acc, original, skip + len + 4) - end - defp escape_unicode_chunk(<<>>, acc, original, skip, len) do - part = binary_part(original, skip, len) - [acc | part] - end - defp escape_unicode_chunk(<>, _acc, original, _skip, _len) do - error({:invalid_byte, byte, original}) - end - - @compile {:inline, error: 1} - defp error(error) do - throw EncodeError.new(error) - end -end diff --git a/plugins/mob_iap/deps/jason/lib/encoder.ex b/plugins/mob_iap/deps/jason/lib/encoder.ex deleted file mode 100644 index 95fbf0c..0000000 --- a/plugins/mob_iap/deps/jason/lib/encoder.ex +++ /dev/null @@ -1,239 +0,0 @@ -defprotocol Jason.Encoder do - @moduledoc """ - Protocol controlling how a value is encoded to JSON. - - ## Deriving - - The protocol allows leveraging the Elixir's `@derive` feature - to simplify protocol implementation in trivial cases. Accepted - options are: - - * `:only` - encodes only values of specified keys. - * `:except` - encodes all struct fields except specified keys. - - By default all keys except the `:__struct__` key are encoded. - - ## Example - - Let's assume a presence of the following struct: - - defmodule Test do - defstruct [:foo, :bar, :baz] - end - - If we were to call `@derive Jason.Encoder` just before `defstruct`, - an implementation similar to the following implementation would be generated: - - defimpl Jason.Encoder, for: Test do - def encode(value, opts) do - Jason.Encode.map(Map.take(value, [:foo, :bar, :baz]), opts) - end - end - - If we called `@derive {Jason.Encoder, only: [:foo]}`, an implementation - similar to the following implementation would be generated: - - defimpl Jason.Encoder, for: Test do - def encode(value, opts) do - Jason.Encode.map(Map.take(value, [:foo]), opts) - end - end - - If we called `@derive {Jason.Encoder, except: [:foo]}`, an implementation - similar to the following implementation would be generated: - - defimpl Jason.Encoder, for: Test do - def encode(value, opts) do - Jason.Encode.map(Map.take(value, [:bar, :baz]), opts) - end - end - - The actually generated implementations are more efficient computing some data - during compilation similar to the macros from the `Jason.Helpers` module. - - ## Explicit implementation - - If you wish to implement the protocol fully yourself, it is advised to - use functions from the `Jason.Encode` module to do the actual iodata - generation - they are highly optimized and verified to always produce - valid JSON. - """ - - @type t :: term - @type opts :: Jason.Encode.opts() - - @fallback_to_any true - - @doc """ - Encodes `value` to JSON. - - The argument `opts` is opaque - it can be passed to various functions in - `Jason.Encode` (or to the protocol function itself) for encoding values to JSON. - """ - @spec encode(t, opts) :: iodata - def encode(value, opts) -end - -defimpl Jason.Encoder, for: Any do - defmacro __deriving__(module, struct, opts) do - fields = fields_to_encode(struct, opts) - kv = Enum.map(fields, &{&1, generated_var(&1)}) - escape = quote(do: escape) - encode_map = quote(do: encode_map) - encode_args = [escape, encode_map] - kv_iodata = Jason.Codegen.build_kv_iodata(kv, encode_args) - - quote do - defimpl Jason.Encoder, for: unquote(module) do - require Jason.Helpers - - def encode(%{unquote_splicing(kv)}, {unquote(escape), unquote(encode_map)}) do - unquote(kv_iodata) - end - end - end - end - - # The same as Macro.var/2 except it sets generated: true and handles _ key - defp generated_var(:_) do - {:__, [generated: true], __MODULE__.Underscore} - end - defp generated_var(name) do - {name, [generated: true], __MODULE__} - end - - def encode(%_{} = struct, _opts) do - raise Protocol.UndefinedError, - protocol: @protocol, - value: struct, - description: """ - Jason.Encoder protocol must always be explicitly implemented. - - If you own the struct, you can derive the implementation specifying \ - which fields should be encoded to JSON: - - @derive {Jason.Encoder, only: [....]} - defstruct ... - - It is also possible to encode all fields, although this should be \ - used carefully to avoid accidentally leaking private information \ - when new fields are added: - - @derive Jason.Encoder - defstruct ... - - Finally, if you don't own the struct you want to encode to JSON, \ - you may use Protocol.derive/3 placed outside of any module: - - Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) - Protocol.derive(Jason.Encoder, NameOfTheStruct) - """ - end - - def encode(value, _opts) do - raise Protocol.UndefinedError, - protocol: @protocol, - value: value, - description: "Jason.Encoder protocol must always be explicitly implemented" - end - - defp fields_to_encode(struct, opts) do - fields = Map.keys(struct) - - cond do - only = Keyword.get(opts, :only) -> - case only -- fields do - [] -> - only - - error_keys -> - raise ArgumentError, - "`:only` specified keys (#{inspect(error_keys)}) that are not defined in defstruct: " <> - "#{inspect(fields -- [:__struct__])}" - - end - - except = Keyword.get(opts, :except) -> - case except -- fields do - [] -> - fields -- [:__struct__ | except] - - error_keys -> - raise ArgumentError, - "`:except` specified keys (#{inspect(error_keys)}) that are not defined in defstruct: " <> - "#{inspect(fields -- [:__struct__])}" - - end - - true -> - fields -- [:__struct__] - end - end -end - -# The following implementations are formality - they are already covered -# by the main encoding mechanism in Jason.Encode, but exist mostly for -# documentation purposes and if anybody had the idea to call the protocol directly. - -defimpl Jason.Encoder, for: Atom do - def encode(atom, opts) do - Jason.Encode.atom(atom, opts) - end -end - -defimpl Jason.Encoder, for: Integer do - def encode(integer, _opts) do - Jason.Encode.integer(integer) - end -end - -defimpl Jason.Encoder, for: Float do - def encode(float, _opts) do - Jason.Encode.float(float) - end -end - -defimpl Jason.Encoder, for: List do - def encode(list, opts) do - Jason.Encode.list(list, opts) - end -end - -defimpl Jason.Encoder, for: Map do - def encode(map, opts) do - Jason.Encode.map(map, opts) - end -end - -defimpl Jason.Encoder, for: BitString do - def encode(binary, opts) when is_binary(binary) do - Jason.Encode.string(binary, opts) - end - - def encode(bitstring, _opts) do - raise Protocol.UndefinedError, - protocol: @protocol, - value: bitstring, - description: "cannot encode a bitstring to JSON" - end -end - -defimpl Jason.Encoder, for: [Date, Time, NaiveDateTime, DateTime] do - def encode(value, _opts) do - [?", @for.to_iso8601(value), ?"] - end -end - -if Code.ensure_loaded?(Decimal) do - defimpl Jason.Encoder, for: Decimal do - def encode(value, _opts) do - [?", Decimal.to_string(value), ?"] - end - end -end - -defimpl Jason.Encoder, for: Jason.Fragment do - def encode(%{encode: encode}, opts) do - encode.(opts) - end -end diff --git a/plugins/mob_iap/deps/jason/lib/formatter.ex b/plugins/mob_iap/deps/jason/lib/formatter.ex deleted file mode 100644 index 560a2f5..0000000 --- a/plugins/mob_iap/deps/jason/lib/formatter.ex +++ /dev/null @@ -1,255 +0,0 @@ -defmodule Jason.Formatter do - @moduledoc ~S""" - Pretty-printing and minimizing functions for JSON-encoded data. - - Input is required to be in an 8-bit-wide encoding such as UTF-8 or Latin-1 - in `t:iodata/0` format. Input must have valid JSON, invalid JSON may produce - unexpected results or errors. - """ - - @type opts :: [ - {:indent, iodata} - | {:line_separator, iodata} - | {:record_separator, iodata} - | {:after_colon, iodata} - ] - - import Record - defrecordp :opts, [:indent, :line, :record, :colon] - - @dialyzer :no_improper_lists - - @doc ~S""" - Pretty-prints JSON-encoded `input`. - - `input` may contain multiple JSON objects or arrays, optionally separated - by whitespace (e.g., one object per line). Objects in output will be - separated by newlines. No trailing newline is emitted. - - ## Options - - * `:indent` - used for nested objects and arrays (default: two spaces - `" "`); - * `:line_separator` - used in nested objects (default: `"\n"`); - * `:record_separator` - separates root-level objects and arrays - (default is the value for `:line_separator` option); - * `:after_colon` - printed after a colon inside objects (default: one space - `" "`). - - ## Examples - - iex> Jason.Formatter.pretty_print(~s|{"a":{"b": [1, 2]}}|) - ~s|{ - "a": { - "b": [ - 1, - 2 - ] - } - }| - - """ - @spec pretty_print(iodata, opts) :: binary - def pretty_print(input, opts \\ []) do - input - |> pretty_print_to_iodata(opts) - |> IO.iodata_to_binary() - end - - @doc ~S""" - Pretty-prints JSON-encoded `input` and returns iodata. - - This function should be preferred to `pretty_print/2`, if the pretty-printed - JSON will be handed over to one of the IO functions or sent - over the socket. The Erlang runtime is able to leverage vectorised - writes and avoid allocating a continuous buffer for the whole - resulting string, lowering memory use and increasing performance. - """ - @spec pretty_print_to_iodata(iodata, opts) :: iodata - def pretty_print_to_iodata(input, opts \\ []) do - opts = parse_opts(opts, " ", "\n", nil, " ") - - depth = :first - empty = false - - {output, _state} = pp_iodata(input, [], depth, empty, opts) - - output - end - - @doc ~S""" - Minimizes JSON-encoded `input`. - - `input` may contain multiple JSON objects or arrays, optionally - separated by whitespace (e.g., one object per line). Minimized - output will contain one object per line. No trailing newline is emitted. - - ## Options - - * `:record_separator` - controls the string used as newline (default: `"\n"`). - - ## Examples - - iex> Jason.Formatter.minimize(~s|{ "a" : "b" , "c": \n\n 2}|) - ~s|{"a":"b","c":2}| - - """ - @spec minimize(iodata, opts) :: binary - def minimize(input, opts \\ []) do - input - |> minimize_to_iodata(opts) - |> IO.iodata_to_binary() - end - - @doc ~S""" - Minimizes JSON-encoded `input` and returns iodata. - - This function should be preferred to `minimize/2`, if the minimized - JSON will be handed over to one of the IO functions or sent - over the socket. The Erlang runtime is able to leverage vectorised - writes and avoid allocating a continuous buffer for the whole - resulting string, lowering memory use and increasing performance. - """ - @spec minimize_to_iodata(iodata, opts) :: iodata - def minimize_to_iodata(input, opts) do - record = Keyword.get(opts, :record_separator, "\n") - opts = opts(indent: "", line: "", record: record, colon: "") - - depth = :first - empty = false - - {output, _state} = pp_iodata(input, [], depth, empty, opts) - - output - end - - defp parse_opts([{option, value} | opts], indent, line, record, colon) do - value = IO.iodata_to_binary(value) - case option do - :indent -> parse_opts(opts, value, line, record, colon) - :record_separator -> parse_opts(opts, indent, line, value, colon) - :after_colon -> parse_opts(opts, indent, line, record, value) - :line_separator -> parse_opts(opts, indent, value, record || value, colon) - end - end - - defp parse_opts([], indent, line, record, colon) do - opts(indent: indent, line: line, record: record || line, colon: colon) - end - - for depth <- 1..16 do - defp tab(" ", unquote(depth)), do: unquote(String.duplicate(" ", depth)) - end - - defp tab("", _), do: "" - defp tab(indent, depth), do: List.duplicate(indent, depth) - - defp pp_iodata(<<>>, output_acc, depth, empty, opts) do - {output_acc, &pp_iodata(&1, &2, depth, empty, opts)} - end - - defp pp_iodata(<>, output_acc, depth, empty, opts) do - pp_byte(byte, rest, output_acc, depth, empty, opts) - end - - defp pp_iodata([], output_acc, depth, empty, opts) do - {output_acc, &pp_iodata(&1, &2, depth, empty, opts)} - end - - defp pp_iodata([byte | rest], output_acc, depth, empty, opts) when is_integer(byte) do - pp_byte(byte, rest, output_acc, depth, empty, opts) - end - - defp pp_iodata([head | tail], output_acc, depth, empty, opts) do - {output_acc, cont} = pp_iodata(head, output_acc, depth, empty, opts) - cont.(tail, output_acc) - end - - defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ~c' \n\r\t' do - pp_iodata(rest, output, depth, empty, opts) - end - - defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ~c'{[' do - {out, depth} = - cond do - depth == :first -> {byte, 1} - depth == 0 -> {[opts(opts, :record), byte], 1} - empty -> {[opts(opts, :line), tab(opts(opts, :indent), depth), byte], depth + 1} - true -> {byte, depth + 1} - end - - empty = true - pp_iodata(rest, [output, out], depth, empty, opts) - end - - defp pp_byte(byte, rest, output, depth, true = _empty, opts) when byte in ~c'}]' do - empty = false - depth = depth - 1 - pp_iodata(rest, [output, byte], depth, empty, opts) - end - - defp pp_byte(byte, rest, output, depth, false = empty, opts) when byte in ~c'}]' do - depth = depth - 1 - out = [opts(opts, :line), tab(opts(opts, :indent), depth), byte] - pp_iodata(rest, [output, out], depth, empty, opts) - end - - defp pp_byte(byte, rest, output, depth, _empty, opts) when byte in ~c',' do - empty = false - out = [byte, opts(opts, :line), tab(opts(opts, :indent), depth)] - pp_iodata(rest, [output, out], depth, empty, opts) - end - - defp pp_byte(byte, rest, output, depth, empty, opts) when byte in ~c':' do - out = [byte, opts(opts, :colon)] - pp_iodata(rest, [output, out], depth, empty, opts) - end - - defp pp_byte(byte, rest, output, depth, empty, opts) do - out = if empty, do: [opts(opts, :line), tab(opts(opts, :indent), depth), byte], else: byte - empty = false - - if byte == ?" do - pp_string(rest, [output, out], _in_bs = false, &pp_iodata(&1, &2, depth, empty, opts)) - else - pp_iodata(rest, [output, out], depth, empty, opts) - end - end - - defp pp_string(<<>>, output_acc, in_bs, cont) do - {output_acc, &pp_string(&1, &2, in_bs, cont)} - end - - defp pp_string(binary, output_acc, true = _in_bs, cont) when is_binary(binary) do - <> = binary - pp_string(rest, [output_acc, byte], false, cont) - end - - defp pp_string(binary, output_acc, false = _in_bs, cont) when is_binary(binary) do - case :binary.match(binary, ["\"", "\\"]) do - :nomatch -> - {[output_acc | binary], &pp_string(&1, &2, false, cont)} - {pos, 1} -> - {head, tail} = :erlang.split_binary(binary, pos + 1) - case :binary.at(binary, pos) do - ?\\ -> pp_string(tail, [output_acc | head], true, cont) - ?" -> cont.(tail, [output_acc | head]) - end - end - end - - defp pp_string([], output_acc, in_bs, cont) do - {output_acc, &pp_string(&1, &2, in_bs, cont)} - end - - defp pp_string([byte | rest], output_acc, in_bs, cont) when is_integer(byte) do - cond do - in_bs -> pp_string(rest, [output_acc, byte], false, cont) - byte == ?" -> cont.(rest, [output_acc, byte]) - true -> pp_string(rest, [output_acc, byte], byte == ?\\, cont) - end - end - - defp pp_string([head | tail], output_acc, in_bs, cont) do - {output_acc, cont} = pp_string(head, output_acc, in_bs, cont) - cont.(tail, output_acc) - end -end diff --git a/plugins/mob_iap/deps/jason/lib/fragment.ex b/plugins/mob_iap/deps/jason/lib/fragment.ex deleted file mode 100644 index db26c7c..0000000 --- a/plugins/mob_iap/deps/jason/lib/fragment.ex +++ /dev/null @@ -1,21 +0,0 @@ -defmodule Jason.Fragment do - @moduledoc ~S""" - Provides a way to inject an already-encoded JSON structure into a - to-be-encoded structure in optimized fashion. - - This avoids a decoding/encoding round-trip for the subpart. - - This feature can be used for caching parts of the JSON, or delegating - the generation of the JSON to a third-party system (e.g. Postgres). - """ - - defstruct [:encode] - - def new(iodata) when is_list(iodata) or is_binary(iodata) do - %__MODULE__{encode: fn _ -> iodata end} - end - - def new(encode) when is_function(encode, 1) do - %__MODULE__{encode: encode} - end -end diff --git a/plugins/mob_iap/deps/jason/lib/helpers.ex b/plugins/mob_iap/deps/jason/lib/helpers.ex deleted file mode 100644 index f94678d..0000000 --- a/plugins/mob_iap/deps/jason/lib/helpers.ex +++ /dev/null @@ -1,98 +0,0 @@ -defmodule Jason.Helpers do - @moduledoc """ - Provides macro facilities for partial compile-time encoding of JSON. - """ - - alias Jason.{Codegen, Fragment} - - @doc ~S""" - Encodes a JSON map from a compile-time keyword. - - Encodes the keys at compile time and strives to create as flat iodata - structure as possible to achieve maximum efficiency. Does encoding - right at the call site, but returns an `%Jason.Fragment{}` struct - that needs to be passed to one of the "main" encoding functions - - for example `Jason.encode/2` for final encoding into JSON - this - makes it completely transparent for most uses. - - Only allows keys that do not require escaping in any of the supported - encoding modes. This means only ASCII characters from the range - 0x1F..0x7F excluding '\', '/' and '"' are allowed - this also excludes - all control characters like newlines. - - Preserves the order of the keys. - - ## Example - - iex> fragment = json_map(foo: 1, bar: 2) - iex> Jason.encode!(fragment) - "{\"foo\":1,\"bar\":2}" - - """ - defmacro json_map(kv) do - kv_values = Macro.expand(kv, __CALLER__) - kv_vars = Enum.map(kv_values, fn {key, _} -> {key, generated_var(key, Codegen)} end) - - values = Enum.map(kv_values, &elem(&1, 1)) - vars = Enum.map(kv_vars, &elem(&1, 1)) - - escape = quote(do: escape) - encode_map = quote(do: encode_map) - encode_args = [escape, encode_map] - kv_iodata = Codegen.build_kv_iodata(kv_vars, encode_args) - - quote do - {unquote_splicing(vars)} = {unquote_splicing(values)} - - %Fragment{ - encode: fn {unquote(escape), unquote(encode_map)} -> - unquote(kv_iodata) - end - } - end - end - - @doc ~S""" - Encodes a JSON map from a variable containing a map and a compile-time - list of keys. - - It is equivalent to calling `Map.take/2` before encoding. Otherwise works - similar to `json_map/2`. - - ## Example - - iex> map = %{a: 1, b: 2, c: 3} - iex> fragment = json_map_take(map, [:c, :b]) - iex> Jason.encode!(fragment) - "{\"c\":3,\"b\":2}" - - """ - defmacro json_map_take(map, take) do - take = Macro.expand(take, __CALLER__) - kv = Enum.map(take, &{&1, generated_var(&1, Codegen)}) - escape = quote(do: escape) - encode_map = quote(do: encode_map) - encode_args = [escape, encode_map] - kv_iodata = Codegen.build_kv_iodata(kv, encode_args) - - quote do - case unquote(map) do - %{unquote_splicing(kv)} -> - %Fragment{ - encode: fn {unquote(escape), unquote(encode_map)} -> - unquote(kv_iodata) - end - } - - other -> - raise ArgumentError, - "expected a map with keys: #{unquote(inspect(take))}, got: #{inspect(other)}" - end - end - end - - # The same as Macro.var/2 except it sets generated: true - defp generated_var(name, context) do - {name, [generated: true], context} - end -end diff --git a/plugins/mob_iap/deps/jason/lib/jason.ex b/plugins/mob_iap/deps/jason/lib/jason.ex deleted file mode 100644 index 2bfa013..0000000 --- a/plugins/mob_iap/deps/jason/lib/jason.ex +++ /dev/null @@ -1,242 +0,0 @@ -defmodule Jason do - @moduledoc """ - A blazing fast JSON parser and generator in pure Elixir. - """ - - alias Jason.{Encode, Decoder, DecodeError, EncodeError, Formatter} - - @type escape :: :json | :unicode_safe | :html_safe | :javascript_safe - @type maps :: :naive | :strict - - @type encode_opt :: {:escape, escape} | {:maps, maps} | {:pretty, boolean | Formatter.opts()} - - @type keys :: :atoms | :atoms! | :strings | :copy | (String.t() -> term) - - @type strings :: :reference | :copy - - @type floats :: :native | :decimals - - @type objects :: :maps | :ordered_objects - - @type decode_opt :: {:keys, keys} | {:strings, strings} | {:floats, floats} | {:objects, objects} - - @doc """ - Parses a JSON value from `input` iodata. - - ## Options - - * `:keys` - controls how keys in objects are decoded. Possible values are: - - * `:strings` (default) - decodes keys as binary strings, - * `:atoms` - keys are converted to atoms using `String.to_atom/1`, - * `:atoms!` - keys are converted to atoms using `String.to_existing_atom/1`, - * custom decoder - additionally a function accepting a string and returning a key - is accepted. - - * `:strings` - controls how strings (including keys) are decoded. Possible values are: - - * `:reference` (default) - when possible tries to create a sub-binary into the original - * `:copy` - always copies the strings. This option is especially useful when parts of the - decoded data will be stored for a long time (in ets or some process) to avoid keeping - the reference to the original data. - - * `:floats` - controls how floats are decoded. Possible values are: - - * `:native` (default) - Native conversion from binary to float using `:erlang.binary_to_float/1`, - * `:decimals` - uses `Decimal.new/1` to parse the binary into a Decimal struct with arbitrary precision. - - * `:objects` - controls how objects are decoded. Possible values are: - - * `:maps` (default) - objects are decoded as maps - * `:ordered_objects` - objects are decoded as `Jason.OrderedObject` structs - - ## Decoding keys to atoms - - The `:atoms` option uses the `String.to_atom/1` call that can create atoms at runtime. - Since the atoms are not garbage collected, this can pose a DoS attack vector when used - on user-controlled data. - - ## Examples - - iex> Jason.decode("{}") - {:ok, %{}} - - iex> Jason.decode("invalid") - {:error, %Jason.DecodeError{data: "invalid", position: 0, token: nil}} - """ - @spec decode(iodata, [decode_opt]) :: {:ok, term} | {:error, DecodeError.t()} - def decode(input, opts \\ []) do - input = IO.iodata_to_binary(input) - Decoder.parse(input, format_decode_opts(opts)) - end - - @doc """ - Parses a JSON value from `input` iodata. - - Similar to `decode/2` except it will unwrap the error tuple and raise - in case of errors. - - ## Examples - - iex> Jason.decode!("{}") - %{} - - iex> Jason.decode!("invalid") - ** (Jason.DecodeError) unexpected byte at position 0: 0x69 ("i") - - """ - @spec decode!(iodata, [decode_opt]) :: term | no_return - def decode!(input, opts \\ []) do - case decode(input, opts) do - {:ok, result} -> result - {:error, error} -> raise error - end - end - - @doc """ - Generates JSON corresponding to `input`. - - The generation is controlled by the `Jason.Encoder` protocol, - please refer to the module to read more on how to define the protocol - for custom data types. - - ## Options - - * `:escape` - controls how strings are encoded. Possible values are: - - * `:json` (default) - the regular JSON escaping as defined by RFC 7159. - * `:javascript_safe` - additionally escapes the LINE SEPARATOR (U+2028) - and PARAGRAPH SEPARATOR (U+2029) characters to make the produced JSON - valid JavaScript. - * `:html_safe` - similar to `:javascript_safe`, but also escapes the `/` - character to prevent XSS. - * `:unicode_safe` - escapes all non-ascii characters. - - * `:maps` - controls how maps are encoded. Possible values are: - - * `:strict` - checks the encoded map for duplicate keys and raises - if they appear. For example `%{:foo => 1, "foo" => 2}` would be - rejected, since both keys would be encoded to the string `"foo"`. - * `:naive` (default) - does not perform the check. - - * `:pretty` - controls pretty printing of the output. Possible values are: - - * `true` to pretty print with default configuration - * a keyword of options as specified by `Jason.Formatter.pretty_print/2`. - - ## Examples - - iex> Jason.encode(%{a: 1}) - {:ok, ~S|{"a":1}|} - - iex> Jason.encode("\\xFF") - {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}} - - """ - @spec encode(term, [encode_opt]) :: - {:ok, String.t()} | {:error, EncodeError.t() | Exception.t()} - def encode(input, opts \\ []) do - case do_encode(input, format_encode_opts(opts)) do - {:ok, result} -> {:ok, IO.iodata_to_binary(result)} - {:error, error} -> {:error, error} - end - end - - @doc """ - Generates JSON corresponding to `input`. - - Similar to `encode/1` except it will unwrap the error tuple and raise - in case of errors. - - ## Examples - - iex> Jason.encode!(%{a: 1}) - ~S|{"a":1}| - - iex> Jason.encode!("\\xFF") - ** (Jason.EncodeError) invalid byte 0xFF in <<255>> - - """ - @spec encode!(term, [encode_opt]) :: String.t() | no_return - def encode!(input, opts \\ []) do - case do_encode(input, format_encode_opts(opts)) do - {:ok, result} -> IO.iodata_to_binary(result) - {:error, error} -> raise error - end - end - - @doc """ - Generates JSON corresponding to `input` and returns iodata. - - This function should be preferred to `encode/2`, if the generated - JSON will be handed over to one of the IO functions or sent - over the socket. The Erlang runtime is able to leverage vectorised - writes and avoid allocating a continuous buffer for the whole - resulting string, lowering memory use and increasing performance. - - ## Examples - - iex> {:ok, iodata} = Jason.encode_to_iodata(%{a: 1}) - iex> IO.iodata_to_binary(iodata) - ~S|{"a":1}| - - iex> Jason.encode_to_iodata("\\xFF") - {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}} - - """ - @spec encode_to_iodata(term, [encode_opt]) :: - {:ok, iodata} | {:error, EncodeError.t() | Exception.t()} - def encode_to_iodata(input, opts \\ []) do - do_encode(input, format_encode_opts(opts)) - end - - @doc """ - Generates JSON corresponding to `input` and returns iodata. - - Similar to `encode_to_iodata/1` except it will unwrap the error tuple - and raise in case of errors. - - ## Examples - - iex> iodata = Jason.encode_to_iodata!(%{a: 1}) - iex> IO.iodata_to_binary(iodata) - ~S|{"a":1}| - - iex> Jason.encode_to_iodata!("\\xFF") - ** (Jason.EncodeError) invalid byte 0xFF in <<255>> - - """ - @spec encode_to_iodata!(term, [encode_opt]) :: iodata | no_return - def encode_to_iodata!(input, opts \\ []) do - case do_encode(input, format_encode_opts(opts)) do - {:ok, result} -> result - {:error, error} -> raise error - end - end - - defp do_encode(input, %{pretty: true} = opts) do - case Encode.encode(input, opts) do - {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded)} - other -> other - end - end - - defp do_encode(input, %{pretty: pretty} = opts) when pretty !== false do - case Encode.encode(input, opts) do - {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded, pretty)} - other -> other - end - end - - defp do_encode(input, opts) do - Encode.encode(input, opts) - end - - defp format_encode_opts(opts) do - Enum.into(opts, %{escape: :json, maps: :naive}) - end - - defp format_decode_opts(opts) do - Enum.into(opts, %{keys: :strings, strings: :reference, floats: :native, objects: :maps}) - end -end diff --git a/plugins/mob_iap/deps/jason/lib/ordered_object.ex b/plugins/mob_iap/deps/jason/lib/ordered_object.ex deleted file mode 100644 index 10d92f6..0000000 --- a/plugins/mob_iap/deps/jason/lib/ordered_object.ex +++ /dev/null @@ -1,106 +0,0 @@ -defmodule Jason.OrderedObject do - @doc """ - Struct implementing a JSON object retaining order of properties. - - A wrapper around a keyword (that supports non-atom keys) allowing for - proper protocol implementations. - - Implements the `Access` behaviour and `Enumerable` protocol with - complexity similar to keywords/lists. - """ - - @behaviour Access - - @type t :: %__MODULE__{values: [{String.Chars.t(), term()}]} - - defstruct values: [] - - @doc ~S""" - Creates a new ordered object from a list of key-value pairs. - - ## Example - - iex> %{a: 1, c: 3, b: 2} - ...> |> Enum.sort() - ...> |> Jason.OrderedObject.new() - ...> |> Jason.encode!() - "{\"a\":1,\"b\":2,\"c\":3}" - - """ - def new(values) when is_list(values) do - %__MODULE__{values: values} - end - - @impl Access - def fetch(%__MODULE__{values: values}, key) do - case :lists.keyfind(key, 1, values) do - {_, value} -> {:ok, value} - false -> :error - end - end - - @impl Access - def get_and_update(%__MODULE__{values: values} = obj, key, function) do - {result, new_values} = get_and_update(values, [], key, function) - {result, %{obj | values: new_values}} - end - - @impl Access - def pop(%__MODULE__{values: values} = obj, key, default \\ nil) do - case :lists.keyfind(key, 1, values) do - {_, value} -> {value, %{obj | values: delete_key(values, key)}} - false -> {default, obj} - end - end - - defp get_and_update([{key, current} | t], acc, key, fun) do - case fun.(current) do - {get, value} -> - {get, :lists.reverse(acc, [{key, value} | t])} - - :pop -> - {current, :lists.reverse(acc, t)} - - other -> - raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}" - end - end - - defp get_and_update([{_, _} = h | t], acc, key, fun), do: get_and_update(t, [h | acc], key, fun) - - defp get_and_update([], acc, key, fun) do - case fun.(nil) do - {get, update} -> - {get, [{key, update} | :lists.reverse(acc)]} - - :pop -> - {nil, :lists.reverse(acc)} - - other -> - raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}" - end - end - - defp delete_key([{key, _} | tail], key), do: delete_key(tail, key) - defp delete_key([{_, _} = pair | tail], key), do: [pair | delete_key(tail, key)] - defp delete_key([], _key), do: [] -end - -defimpl Enumerable, for: Jason.OrderedObject do - def count(%{values: []}), do: {:ok, 0} - def count(_obj), do: {:error, __MODULE__} - - def member?(%{values: []}, _value), do: {:ok, false} - def member?(_obj, _value), do: {:error, __MODULE__} - - def slice(%{values: []}), do: {:ok, 0, fn _, _ -> [] end} - def slice(_obj), do: {:error, __MODULE__} - - def reduce(%{values: values}, acc, fun), do: Enumerable.List.reduce(values, acc, fun) -end - -defimpl Jason.Encoder, for: Jason.OrderedObject do - def encode(%{values: values}, opts) do - Jason.Encode.keyword(values, opts) - end -end diff --git a/plugins/mob_iap/deps/jason/lib/sigil.ex b/plugins/mob_iap/deps/jason/lib/sigil.ex deleted file mode 100644 index 16aec39..0000000 --- a/plugins/mob_iap/deps/jason/lib/sigil.ex +++ /dev/null @@ -1,84 +0,0 @@ -defmodule Jason.Sigil do - @doc ~S""" - Handles the sigil `~j` for JSON strings. - - Calls `Jason.decode!/2` with modifiers mapped to options. - - Given a string literal without interpolations, decodes the - string at compile-time. - - ## Modifiers - - See `Jason.decode/2` for detailed descriptions. - - * `a` - equivalent to `{:keys, :atoms}` option - * `A` - equivalent to `{:keys, :atoms!}` option - * `r` - equivalent to `{:strings, :reference}` option - * `c` - equivalent to `{:strings, :copy}` option - - ## Examples - - iex> ~j"0" - 0 - - iex> ~j"[1, 2, 3]" - [1, 2, 3] - - iex> ~j'"string"'r - "string" - - iex> ~j"{}" - %{} - - iex> ~j'{"atom": "value"}'a - %{atom: "value"} - - iex> ~j'{"#{:j}": #{~c'"j"'}}'A - %{j: "j"} - - """ - defmacro sigil_j(term, modifiers) - - defmacro sigil_j({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do - Macro.escape(Jason.decode!(string, mods_to_opts(modifiers))) - end - - defmacro sigil_j(term, modifiers) do - quote(do: Jason.decode!(unquote(term), unquote(mods_to_opts(modifiers)))) - end - - @doc ~S""" - Handles the sigil `~J` for raw JSON strings. - - Decodes a raw string ignoring Elixir interpolations and - escape characters at compile-time. - - ## Examples - - iex> ~J'"#{string}"' - "\#{string}" - - iex> ~J'"\u0078\\y"' - "x\\y" - - iex> ~J'{"#{key}": "#{}"}'a - %{"\#{key}": "\#{}"} - """ - defmacro sigil_J(term, modifiers) - - defmacro sigil_J({:<<>>, _meta, [string]}, modifiers) when is_binary(string) do - Macro.escape(Jason.decode!(string, mods_to_opts(modifiers))) - end - - @spec mods_to_opts(charlist) :: [Jason.decode_opt()] - defp mods_to_opts(modifiers) do - modifiers - |> Enum.map(fn - ?a -> {:keys, :atoms} - ?A -> {:keys, :atoms!} - ?r -> {:strings, :reference} - ?c -> {:strings, :copy} - m -> raise ArgumentError, "unknown sigil modifier #{<>}" - end) - end -end diff --git a/plugins/mob_iap/deps/jason/mix.exs b/plugins/mob_iap/deps/jason/mix.exs deleted file mode 100644 index 37e51c8..0000000 --- a/plugins/mob_iap/deps/jason/mix.exs +++ /dev/null @@ -1,75 +0,0 @@ -defmodule Jason.Mixfile do - use Mix.Project - - @source_url "https://github.com/michalmuskala/jason" - @version "1.4.5" - - def project() do - [ - app: :jason, - version: @version, - elixir: "~> 1.4", - start_permanent: Mix.env() == :prod, - consolidate_protocols: Mix.env() != :test, - deps: deps(), - preferred_cli_env: [docs: :docs], - dialyzer: dialyzer(), - description: description(), - package: package(), - docs: docs() - ] - end - - def application() do - [ - extra_applications: [] - ] - end - - defp deps() do - [ - {:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", optional: true}, - {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false}, - {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, - ] ++ maybe_stream_data() - end - - defp maybe_stream_data() do - if Version.match?(System.version(), "~> 1.12") do - [{:stream_data, "~> 1.0", only: :test}] - else - [] - end - end - - defp dialyzer() do - [ - plt_add_apps: [:decimal] - ] - end - - defp description() do - """ - A blazing fast JSON parser and generator in pure Elixir. - """ - end - - defp package() do - [ - maintainers: ["Michał Muskała"], - licenses: ["Apache-2.0"], - links: %{"GitHub" => @source_url} - ] - end - - defp docs() do - [ - main: "readme", - name: "Jason", - source_ref: "v#{@version}", - canonical: "http://hexdocs.pm/jason", - source_url: @source_url, - extras: ["README.md", "CHANGELOG.md", "LICENSE"] - ] - end -end diff --git a/plugins/mob_iap/deps/makeup/.hex b/plugins/mob_iap/deps/makeup/.hex deleted file mode 100644 index 9749a3c924a3444b43f326a0de16a3071f333b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmZ9H%WlIU5JlZMeXQ~+5n~<$yZ%Wvz)(9W*mB~MeSf^T%kGYjj;?fn#>3dxe&h!o zsJnMv_fn6AGUpvLU3pvT>o>2rX)e2ahVyWS`j)BCXVYKJJ=H!>k4rsGrSSR@Mh@58 zmiPRfouB~0Xl)%Qsg*<<8+Za2l>kQRn2}kSS^BY|3{-V$|`Hn t3r{Q(lQuSj014obR2W#H(1zM3TjhqqC*uhrv 1.2.2` onwards to prepare for future deprecations. - -### 1.0.5 - 2020-10-02 - -#### Bugfix #1 - -Fix serious bug in the `Makeup.Lexer.Combinators.lexeme/1` when given a list of unicode characters. - -Before this fix, the following combinator: - -``` -import Makeup.Lexer.Combinators - -characters_lexeme = - utf8_char([]) - |> utf8_char([]) - |> lexeme() - |> token(:character_lexeme) -``` - -when given as input a string like `"àó"` would return the following invalid Unicode: - -``` -[{:character_lexeme, %{}, <<225, 242>>}] -``` - -instead of - -``` -[{:character_lexeme, %{}, "áò"}] -``` - -This was caused by the use of `IO.iodata_to_binary/1` instead of (the slower) `to_string()`. This was a problem because `IO.iodata_to_binary/1` would put any byte in a binary instead of (correctly) encoding bytes > 128 as a Unicode character. This is not a problem with `IO.iodata_to_binary/1` it's a problem with using that function when we want to encode characters as Unicode strings. - -#### Bugfix #2 - -Fixed an edge case in the HTML encoder in the formatter. - - -### 1.0.4 - 2020-09-25 - -Fix warnings and update NimbleParsec dependency. - - -### 1.0.3 - 2020-06-07 - -Allow styles to be given as atoms when generating stylesheets. - - -### 1.0.2 - 2020-05-28 - -Update NimbleParsec dependency. - - -### 1.0.1 - 2020-03-24 - -Remove warnings on recent Elixir versions. - - -### 1.0.0 - 2019-07-15 - -Upgrade the HTML formatter so that you can use different kinds of tags around the tokens. - -Previous versions always used the `` tag (e.g. `name`). -You can now use other HTML tags using the `:highlight_tag` option: - -```elixir -alias Makeup.Formatters.HTML.HTMLFormatter -HTMLFormatter.format_as_iolist(tokens, highlight_tag: "font") -``` - - -### 0.8.0 - 2019-01-01 - -This release adds a new file extension registry for lexers. -This means that it's now possible to lookup lexers by file extension. -Since the previous release it was already possible to lookup lexers by language name. -For example: - -```elixir -elixir_lexer = Makeup.Registry.fetch_lexer_by_extension!("elixir") -``` - -Now you can also do this this: - -```elixir -elixir_lexer = Makeup.Registry.get_lexer_by_extension!("ex") -``` - -Documentation of the registry functionality was also improved. - - -### 0.7.0 - 2018-12-30 - -Adds a register where lexer developers can register their lexers. -This allows one to pick a lexer based on the language name. - -The goal is for Makeup to be aware of the available lexers. -In a project such as ExDoc this allows Makeup to support an unbounded number of languages just by declaring the lexer as a dependency. - - -### 0.6.0 - 2018-12-22 - -Fixes the combinators affected by compatibility breaks in `nimble_parsec` from `0.4.x` to `0.5.x`. - -Pins `nimble_parsec` to version `0.5.0`. diff --git a/plugins/mob_iap/deps/makeup/LICENSE b/plugins/mob_iap/deps/makeup/LICENSE deleted file mode 100644 index e1b1566..0000000 --- a/plugins/mob_iap/deps/makeup/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2006-2021 by the respective authors (see AUTHORS file). -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/mob_iap/deps/makeup/README.md b/plugins/mob_iap/deps/makeup/README.md deleted file mode 100644 index 5847fdc..0000000 --- a/plugins/mob_iap/deps/makeup/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Makeup - -[![Module Version](https://img.shields.io/hexpm/v/makeup.svg)](https://hex.pm/packages/makeup) -[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/makeup/) - -## Introduction - -Makeup is a "generic syntax highlighter suitable for use in code hosting, forums, wikis or other applications that need to prettify source code" . This tagline was shamelessly stolen from the [Pygments website](http://pygments.org/). - -Pygments the major inspiration for this package, and the structure is basically the same. -It has **lexers**, **formatters** and **styles**. - -* **Lexers** turn the source code into a list of tokens. -* **Formatters** turn the list of tokens into something else (HTML, TeX, images, etc.). - Currently only an HTML formatter exists. -* **Styles** customize the output of the formatter. - Makeup supports all Pygments' styles (in fact, they were converted from the Python's source). - New custom styles can be added to makeup itself, or defined in the Project that uses it. - -## Demo - -To see a sample of Makeup's output, go check the [demo](https://elixir-makeup.github.io/makeup_demo/). -Please note that not all styles define all differences between tokens. -In a given style, strings and characters might be rendered in the same color while in others , the colors might be different. - -That is style-dependent. - -Some of the richer styles are -the Tango style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#tango)), -the Colorful style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#colorful)), -the Default style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#default)), and -the Friendly style ([elixir](https://elixir-makeup.github.io/makeup_demo/elixir.html#friendly)). - -## Supported Languages - -The supported source languages are: - - * [Elixir](https://github.com/elixir-makeup/makeup_elixir) - * [Erlang](https://github.com/elixir-makeup/makeup_erlang) - * [C](https://github.com/elixir-makeup/makeup_c) - * [HTML](https://github.com/elixir-makeup/makeup_html) - * [Diff](https://github.com/elixir-makeup/makeup_diff) - * [JSON](https://github.com/elixir-makeup/makeup_json) - * [(H)EEx](https://github.com/elixir-makeup/makeup_eex) - -## Installation - -The package can be installed by adding `makeup` and `makeup_elixir` (required -for the ElixirLexer) to your list of dependencies in `mix.exs`: - -```elixir -def deps do - [ - {:makeup, "x.y.z"}, - {:makeup_elixir, "x.y.z"} - ] -end -``` - -Documentation can be found at [https://hexdocs.pm/makeup](https://hexdocs.pm/makeup). - -## Changes - -Changes from previous versions are details in the [Changelog](CHANGELOG.md). - -## Quickstart - -To highlight some Elixir code (newlines added for clarity): - -```elixir -Makeup.highlight(source) -# "
    
    -#   x 
    -#   + 1
    -# 
    \n" -``` - -As you can see, the default HTML formatter uses CSS classes. -You'll need a CSS stylesheet to get the different colors and styles. - -To generate a stylesheet: - -``` -Makeup.stylesheet(style) # by default, the StyleMap.default style is used. -# ... output omitted -``` - -## Advantages over Pygments - -One of the greatest advantages is that it runs on the BEAM, so it can be used with Elixir projects without external dependencies. - -Another advantage is that the way lexers are written, we can be a lot smarter than Pygments in processing the output. - -For the developer, lexers are also easier to write than the Pygments lexers, because we use a PEG parser. -Most Pygments lexers use something like a state table that works based on regex matches, -and uses the results of those matches to switch to another state. -Using a PEG parser we can define the grammar in a more natural way. - - -The lexers are written using the excellent [NimbleParsec](https://github.com/dashbitco/nimble_parsec) parser. - -## Disadvantages over Pygments - -It supports fewer languages. - -## Documentation on how to write a new lexer - -Contributions are highly appreciated. The most direct way you can contribute to Makeup is by writing a new lexer. You can find some information here: [CONTRIBUTING.md](CONTRIBUTING.md) - -## LICENSE - -Makeup is licensed under the BSD license. -This is the same license as the Pygments Makeup uses and -it seems to be compatible with the licenses used by all the dependencies. diff --git a/plugins/mob_iap/deps/makeup/hex_metadata.config b/plugins/mob_iap/deps/makeup/hex_metadata.config deleted file mode 100644 index 30db0f9..0000000 --- a/plugins/mob_iap/deps/makeup/hex_metadata.config +++ /dev/null @@ -1,35 +0,0 @@ -{<<"links">>, - [{<<"Changelog">>,<<"https://hexdocs.pm/makeup/changelog.html">>}, - {<<"Contributing">>,<<"https://hexdocs.pm/makeup/contributing.html">>}, - {<<"GitHub">>,<<"https://github.com/elixir-makeup/makeup">>}]}. -{<<"name">>,<<"makeup">>}. -{<<"version">>,<<"1.2.1">>}. -{<<"description">>, - <<"Syntax highlighter for source code in the style of Pygments.">>}. -{<<"elixir">>,<<"~> 1.12">>}. -{<<"app">>,<<"makeup">>}. -{<<"licenses">>,[<<"BSD-2-Clause">>]}. -{<<"requirements">>, - [[{<<"name">>,<<"nimble_parsec">>}, - {<<"app">>,<<"nimble_parsec">>}, - {<<"optional">>,false}, - {<<"requirement">>,<<"~> 1.4">>}, - {<<"repository">>,<<"hexpm">>}]]}. -{<<"files">>, - [<<"lib">>,<<"lib/makeup">>,<<"lib/makeup/token">>, - <<"lib/makeup/token/utils">>,<<"lib/makeup/token/utils/hierarchy.ex">>, - <<"lib/makeup/token/utils.ex">>,<<"lib/makeup/registry.ex">>, - <<"lib/makeup/lexer">>,<<"lib/makeup/lexer/postprocess.ex">>, - <<"lib/makeup/lexer/types.ex">>,<<"lib/makeup/lexer/groups.ex">>, - <<"lib/makeup/lexer/combinators.ex">>,<<"lib/makeup/formatter.ex">>, - <<"lib/makeup/formatters">>,<<"lib/makeup/formatters/html">>, - <<"lib/makeup/formatters/html/html_formatter.ex">>, - <<"lib/makeup/formatters/html/scripts">>, - <<"lib/makeup/formatters/html/scripts/group_highlighter_javascript.js">>, - <<"lib/makeup/styles">>,<<"lib/makeup/styles/html">>, - <<"lib/makeup/styles/html/token_style.ex">>, - <<"lib/makeup/styles/html/style_map.ex">>, - <<"lib/makeup/styles/html/style.ex">>,<<"lib/makeup/application.ex">>, - <<"lib/makeup/lexer.ex">>,<<"lib/makeup.ex">>,<<"mix.exs">>,<<"README.md">>, - <<"LICENSE">>,<<"CHANGELOG.md">>]}. -{<<"build_tools">>,[<<"mix">>]}. diff --git a/plugins/mob_iap/deps/makeup/lib/makeup.ex b/plugins/mob_iap/deps/makeup/lib/makeup.ex deleted file mode 100644 index a8685a2..0000000 --- a/plugins/mob_iap/deps/makeup/lib/makeup.ex +++ /dev/null @@ -1,82 +0,0 @@ -defmodule Makeup do - @moduledoc """ - Syntax highlighting library for code, inspired by Pygments. - - By default, it doesn't include any lexers. You must import - them separately (even the Elixir lexer). - """ - alias Makeup.Formatters.HTML.HTMLFormatter - alias Makeup.Lexers.ElixirLexer - alias Makeup.Registry - - @doc """ - Highlights the given string using the given lexer and formatter. - - By default it highlight the Elixir language using - `Makeup.Formatters.HTML.HTMLFormatter`. - - ## Options: - - - `:lexer` - module name of the lexer to use (default: `Makeup.Lexers.ElixirLexer`) - - `:lexer_options` - list of options for the lexer - - `:formatter` - module name of the formatter to use (defult: `Makeup.Formatters.HTML.HTMLFormatter`) - - `:formatter_options` - list of options for the formatter. For the included HTMLFormatter, that's: - - `:css_class` - css class(es) of the main `
    ` element (default: `"highlight"`)
    -    - `:highlight_tag` - tag that wraps every token (default: `"span"`)
    -  """
    -  def highlight(source, options \\ []) do
    -    {lexer, lexer_options} = fetch_lexer(options)
    -
    -    formatter =
    -      case options[:formatter] do
    -        nil -> HTMLFormatter
    -        module when is_atom(module) -> module
    -      end
    -
    -    formatter_options = Keyword.get(options, :formatter_options, [])
    -    tokens = lexer.lex(source, lexer_options)
    -    formatter.format_as_binary(tokens, formatter_options)
    -  end
    -
    -  @doc """
    -  Convenience for formatting as the inner bits of HTML.
    -  """
    -  def highlight_inner_html(source, options \\ []) do
    -    {lexer, lexer_options} = fetch_lexer(options)
    -    formatter_options = Keyword.get(options, :formatter_options, [])
    -
    -    tokens = lexer.lex(source, lexer_options)
    -    HTMLFormatter.format_inner_as_binary(tokens, formatter_options)
    -  end
    -
    -  defp fetch_lexer(options) do
    -    {lexer, lexer_options} =
    -      case options[:lexer] do
    -        nil -> {ElixirLexer, []}
    -        module when is_atom(module) -> {module, []}
    -        name -> Registry.fetch_lexer_by_name!(name)
    -      end
    -
    -    {lexer, Keyword.merge(lexer_options, Keyword.get(options, :lexer_options, []))}
    -  end
    -
    -  @doc """
    -  Generates a CSS stylesheet for highlighted code for the given style.
    -
    -  It expects a `style`, either as an atom name or as "style map",
    -  and the `css_class` as the top level class for highlighted code.
    -
    -  For example, if the `css_class` is `"highlight"` (the default), the stylesheet
    -  has the form:
    -
    -  ```css
    -  .highlight .someclass {...}
    -  .highlight .anotherclass {...}
    -  ```
    -
    -  See `Makeup.Styles.HTML.StyleMap` for all style maps.
    -  """
    -  def stylesheet(style \\ :default_style, css_class \\ "highlight") do
    -    HTMLFormatter.stylesheet(style, css_class)
    -  end
    -end
    diff --git a/plugins/mob_iap/deps/makeup/lib/makeup/application.ex b/plugins/mob_iap/deps/makeup/lib/makeup/application.ex
    deleted file mode 100644
    index b730716..0000000
    --- a/plugins/mob_iap/deps/makeup/lib/makeup/application.ex
    +++ /dev/null
    @@ -1,12 +0,0 @@
    -defmodule Makeup.Application do
    -  @moduledoc false
    -  use Application
    -
    -  alias Makeup.Registry
    -
    -  def start(_type, _args) do
    -    Registry.create_name_registry()
    -    Registry.create_extension_registry()
    -    Supervisor.start_link([], strategy: :one_for_one)
    -  end
    -end
    diff --git a/plugins/mob_iap/deps/makeup/lib/makeup/formatter.ex b/plugins/mob_iap/deps/makeup/lib/makeup/formatter.ex
    deleted file mode 100644
    index c08e9c9..0000000
    --- a/plugins/mob_iap/deps/makeup/lib/makeup/formatter.ex
    +++ /dev/null
    @@ -1,3 +0,0 @@
    -defmodule Makeup.Formatter do
    -  @moduledoc false
    -end
    diff --git a/plugins/mob_iap/deps/makeup/lib/makeup/formatters/html/html_formatter.ex b/plugins/mob_iap/deps/makeup/lib/makeup/formatters/html/html_formatter.ex
    deleted file mode 100644
    index 27deabb..0000000
    --- a/plugins/mob_iap/deps/makeup/lib/makeup/formatters/html/html_formatter.ex
    +++ /dev/null
    @@ -1,146 +0,0 @@
    -defmodule Makeup.Formatters.HTML.HTMLFormatter do
    -  @moduledoc """
    -  Turns a list of tokens into HTML fragments.
    -  """
    -
    -  @group_highlight_js "lib/makeup/formatters/html/scripts/group_highlighter_javascript.js" |> File.read!
    -
    -  defp render_token(escaped_value, css_class, meta, highlight_tag) do
    -    group_id = meta[:group_id]
    -    selectable = Map.get(meta, :selectable, [])
    -
    -    classes = [
    -      css_class || [],
    -      if selectable == false do " unselectable" else [] end
    -    ]
    -
    -    [
    -      "<",
    -      highlight_tag,
    -      ~S( class="),
    -      classes,
    -      ~S("),
    -      if group_id do [~S( data-group-id="), group_id, ~S(")] else [] end,
    -      ">",
    -      escaped_value,
    -      "",
    -    ]
    -  end
    -
    -  @doc """
    -  Format a single token into an iolist.
    -  """
    -  def format_token({tag, meta, value}, highlight_tag) do
    -    escaped_value = escape(value)
    -    css_class = Makeup.Token.Utils.css_class_for_token_type(tag)
    -    render_token(escaped_value, css_class, meta, highlight_tag)
    -  end
    -
    -  defp escape_for(?&), do: "&"
    -
    -  defp escape_for(?<), do: "<"
    -
    -  defp escape_for(?>), do: ">"
    -
    -  defp escape_for(?"), do: """
    -
    -  defp escape_for(?'), do: "'"
    -
    -  defp escape_for(c) when is_integer(c) and c <= 127, do: c
    -
    -  defp escape_for(c) when is_integer(c) and c >= 128, do: << c :: utf8 >>
    -
    -  defp escape_for(string) when is_binary(string) do
    -    string
    -    |> to_charlist()
    -    |> Enum.map(&escape_for/1)
    -  end
    -
    -  defp escape(iodata) when is_list(iodata) do
    -    iodata
    -    |> :lists.flatten()
    -    |> Enum.map(&escape_for/1)
    -  end
    -
    -  defp escape(other) when is_binary(other) do
    -    escape_for(other)
    -  end
    -
    -  defp escape(c) when is_integer(c) do
    -    [escape_for(c)]
    -  end
    -
    -  defp escape(other) do
    -    raise "Found `#{inspect(other)}` inside what should be an iolist"
    -  end
    -
    -  @doc """
    -  Turns a list of tokens into an iolist which represents an HTML fragment.
    -  This fragment can be embedded directly into an HTML document.
    -  """
    -  def format_inner_as_iolist(tokens, opts) do
    -    highlight_tag = Keyword.get(opts, :highlight_tag, "span")
    -    Enum.map(tokens, &format_token(&1, highlight_tag))
    -  end
    -
    -  @doc """
    -  Turns a list of tokens into an HTML fragment.
    -  This fragment can be embedded directly into an HTML document.
    -  """
    -  def format_inner_as_binary(tokens, opts) do
    -    tokens
    -    |> format_inner_as_iolist(opts)
    -    |> IO.iodata_to_binary
    -  end
    -
    -  @doc """
    -  Turns a list of tokens into an iolist which represents an HTML fragment.
    -  This fragment can be embedded directly into an HTML document.
    -  """
    -  def format_as_iolist(tokens, opts \\ []) do
    -    css_class = Keyword.get(opts, :css_class, "highlight")
    -    inner = format_inner_as_iolist(tokens, opts)
    -
    -    [
    -      ~S(
    ),
    -      inner,
    -      ~S(
    ) - ] - end - - @doc """ - Turns a list of tokens into an HTML fragment. - This fragment can be embedded directly into an HTML document. - """ - def format_as_binary(tokens, opts \\ []) do - tokens - |> format_as_iolist(opts) - |> IO.iodata_to_binary - end - - @doc """ - Return the CSS stylesheet for a given style. - """ - def stylesheet(style \\ :default_style, css_class \\ "highlight") - - def stylesheet(style, css_class) when is_atom(style) do - stylesheet(apply(Makeup.Styles.HTML.StyleMap, style, []), css_class) - end - - def stylesheet(style, css_class) do - Makeup.Styles.HTML.Style.stylesheet(style, css_class) - end - - @doc """ - Return a JavaScript snippet to highlight code on mouseover. - This is "raw" javascript, and for inclusion in an HTML file - it must be wrapped in a `