XXXXXX-XXXXXX-XXXXXX f\xFCr jede RGB-Farbe.",
+ community: {
+ heading: "Community-Paletten",
+ description: "Hier findest du Paletten aus der GitHub-Diskussion. Klicke auf eine Karte, um sie sofort zu \xFCbernehmen oder {{communityLinkStart}}teile deine eigene{{communityLinkEnd}}.",
+ loading: "Paletten werden geladen\u2026",
+ error: "Paletten konnten nicht geladen werden. \xD6ffne die Diskussion, um sie manuell anzusehen.",
+ empty: "Noch keine Community-Paletten vorhanden. Sei der Erste, der eine teilt!",
+ applyHint: "Klicke, um diese Palette anzuwenden"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Barrierefreiheit",
+ description: "Barrierefreiheitsoptionen anzeigen",
+ highTextContrast: {
+ name: "Hoher Textkontrast",
+ description: "Nur wei\xDFe und schwarze Textfarben verwenden"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimentell",
+ description: "Gef\xE4hrliche Aktionen oder extrem instabile Optionen, die jederzeit ge\xE4ndert oder entfernt werden k\xF6nnen",
+ mixColors: {
+ name: "Farben mischen",
+ description: "Hilft dabei, den Text lesbar zu machen"
+ },
+ gradientTransition: {
+ name: "Gradienten\xFCbergang"
+ },
+ tagColors: {
+ name: "Farbzuweisungen",
+ description: "W\xE4hle eine Farbe aus der aktuellen Palette. Beim Wechsel der Palette passt sich der Tag automatisch an.",
+ placeholder: "Tag eingeben",
+ clear: "Farbe zur\xFCcksetzen",
+ empty: "Noch keine Farbzuweisungen",
+ applyHint: "Farbe anwenden"
+ },
+ reset: {
+ name: "Konfiguration zur\xFCcksetzen",
+ description: "\u{1F6A8} Alle Farben aller Tags werden neu berechnet, als w\xE4re es der erste Start des Plugins. Erfordert einen Neustart von Obsidian.",
+ button: "Zur\xFCcksetzen"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: Eine neue Version ist verf\xFCgbar",
+ resetDone: "\u2705 Zur\xFCcksetzen abgeschlossen\nBitte Obsidian neu starten",
+ communityPaletteApplied: "\u{1F3A8} Community-Palette von {{author}} erfolgreich angewendet"
+ }
+};
+
+// src/i18n/en.json
+var en_default = {
+ settings: {
+ palette: {
+ heading: "Palette",
+ description: "Select palette",
+ options: {
+ adaptiveSoft: "\u{1F338} Adaptive soft",
+ adaptiveBright: "\u{1F33A} Adaptive bright",
+ custom: "Custom"
+ },
+ shift: {
+ name: "Palette shift",
+ description: "If the colors of some tags don't fit, you can shift the palette."
+ },
+ custom: {
+ name: "Custom palette",
+ placeholder: "Paste palette",
+ description: "The format is XXXXXX-XXXXXX-XXXXXX for each RGB color.",
+ community: {
+ heading: "Community palettes",
+ description: "Below are palettes collected from the GitHub discussion. Click any card to apply it instantly or {{communityLinkStart}}share your own{{communityLinkEnd}}.",
+ loading: "Loading palettes\u2026",
+ error: "Unable to load palettes right now. Feel free to open the discussion to browse them manually.",
+ empty: "No shared palettes yet. Be the first to post one!",
+ applyHint: "Click to apply this palette"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accessibility",
+ description: "Show accessibility options",
+ highTextContrast: {
+ name: "High text contrast",
+ description: "Use only white and black colors for texts"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimental",
+ description: "Dangerous actions or insanely unstable options that could be changed or removed in any time",
+ mixColors: {
+ name: "Mix colors",
+ description: "It helps to make text readable"
+ },
+ gradientTransition: {
+ name: "Gradient transition"
+ },
+ tagColors: {
+ name: "Tag assignments",
+ description: "Choose a color from the current palette. If you change the palette, the tag adapts to the nearest color.",
+ placeholder: "Start typing a tag",
+ clear: "Reset color",
+ empty: "No tag overrides yet",
+ applyHint: "Apply color"
+ },
+ reset: {
+ name: "Reset config",
+ description: "\u{1F6A8} All colors of all tags will be recalculated as if it was the first launch of the plugin. Requires restart of Obsidian.",
+ button: "Reset"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: a new version is available",
+ resetDone: "\u2705 Reset is done\nPlease restart Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Applied community palette by {{author}}"
+ }
+};
+
+// src/i18n/es.json
+var es_default = {
+ settings: {
+ palette: {
+ heading: "Paleta",
+ description: "Seleccionar paleta",
+ options: {
+ adaptiveSoft: "\u{1F338} Suave adaptable",
+ adaptiveBright: "\u{1F33A} Brillante adaptable",
+ custom: "Personalizada"
+ },
+ shift: {
+ name: "Desplazamiento de paleta",
+ description: "Si los colores de algunas etiquetas no encajan, puedes desplazar la paleta."
+ },
+ custom: {
+ name: "Paleta personalizada",
+ placeholder: "Pegar paleta",
+ description: "El formato es XXXXXX-XXXXXX-XXXXXX para cada color RGB.",
+ community: {
+ heading: "Paletas de la comunidad",
+ description: "A continuaci\xF3n hay paletas recopiladas de la discusi\xF3n en GitHub. Haz clic en cualquier tarjeta para aplicarla al instante o {{communityLinkStart}}comparte la tuya{{communityLinkEnd}}.",
+ loading: "Cargando paletas\u2026",
+ error: "No se pueden cargar las paletas en este momento. Si\xE9ntete libre de abrir la discusi\xF3n para explorarlas manualmente.",
+ empty: "A\xFAn no hay paletas compartidas. \xA1S\xE9 el primero en publicar una!",
+ applyHint: "Haz clic para aplicar esta paleta"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accesibilidad",
+ description: "Mostrar opciones de accesibilidad",
+ highTextContrast: {
+ name: "Alto contraste de texto",
+ description: "Usar solo colores blanco y negro para los textos"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimental",
+ description: "Acciones peligrosas u opciones inestables que podr\xEDan cambiarse o eliminarse en cualquier momento",
+ mixColors: {
+ name: "Mezclar colores",
+ description: "Ayuda a hacer el texto legible"
+ },
+ gradientTransition: {
+ name: "Transici\xF3n de degradado"
+ },
+ tagColors: {
+ name: "Asignaciones de etiquetas",
+ description: "Elige un color de la paleta actual. Si cambias la paleta, la etiqueta se adapta al color m\xE1s cercano.",
+ placeholder: "Empieza a escribir una etiqueta",
+ clear: "Restablecer color",
+ empty: "A\xFAn no hay anulaciones de etiquetas",
+ applyHint: "Aplicar color"
+ },
+ reset: {
+ name: "Restablecer configuraci\xF3n",
+ description: "\u{1F6A8} Todos los colores de todas las etiquetas se recalcular\xE1n como si fuera el primer lanzamiento del plugin. Requiere reiniciar Obsidian.",
+ button: "Restablecer"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: hay una nueva versi\xF3n disponible",
+ resetDone: "\u2705 Restablecimiento completado\nPor favor, reinicia Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Paleta de la comunidad aplicada por {{author}}"
+ }
+};
+
+// src/i18n/fr.json
+var fr_default = {
+ settings: {
+ palette: {
+ heading: "Palette",
+ description: "S\xE9lectionner une palette",
+ options: {
+ adaptiveSoft: "\u{1F338} Doux adaptatif",
+ adaptiveBright: "\u{1F33A} Vif adaptatif",
+ custom: "Personnalis\xE9e"
+ },
+ shift: {
+ name: "D\xE9calage de palette",
+ description: "Si les couleurs de certaines \xE9tiquettes ne conviennent pas, vous pouvez d\xE9caler la palette."
+ },
+ custom: {
+ name: "Palette personnalis\xE9e",
+ placeholder: "Coller une palette",
+ description: "Le format est XXXXXX-XXXXXX-XXXXXX pour chaque couleur RVB.",
+ community: {
+ heading: "Palettes communautaires",
+ description: "Ci-dessous se trouvent des palettes collect\xE9es depuis la discussion GitHub. Cliquez sur une carte pour l'appliquer instantan\xE9ment ou {{communityLinkStart}}partagez la v\xF4tre{{communityLinkEnd}}.",
+ loading: "Chargement des palettes\u2026",
+ error: "Impossible de charger les palettes pour le moment. N'h\xE9sitez pas \xE0 ouvrir la discussion pour les parcourir manuellement.",
+ empty: "Aucune palette partag\xE9e pour l'instant. Soyez le premier \xE0 en publier une !",
+ applyHint: "Cliquez pour appliquer cette palette"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accessibilit\xE9",
+ description: "Afficher les options d'accessibilit\xE9",
+ highTextContrast: {
+ name: "Contraste de texte \xE9lev\xE9",
+ description: "Utiliser uniquement le blanc et le noir pour les textes"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Exp\xE9rimental",
+ description: "Actions dangereuses ou options instables qui pourraient \xEAtre modifi\xE9es ou supprim\xE9es \xE0 tout moment",
+ mixColors: {
+ name: "M\xE9langer les couleurs",
+ description: "Cela aide \xE0 rendre le texte lisible"
+ },
+ gradientTransition: {
+ name: "Transition de d\xE9grad\xE9"
+ },
+ tagColors: {
+ name: "Affectations d'\xE9tiquettes",
+ description: "Choisissez une couleur de la palette actuelle. Si vous changez la palette, l'\xE9tiquette s'adapte \xE0 la couleur la plus proche.",
+ placeholder: "Commencez \xE0 taper une \xE9tiquette",
+ clear: "R\xE9initialiser la couleur",
+ empty: "Aucune substitution d'\xE9tiquette pour l'instant",
+ applyHint: "Appliquer la couleur"
+ },
+ reset: {
+ name: "R\xE9initialiser la config",
+ description: "\u{1F6A8} Toutes les couleurs de toutes les \xE9tiquettes seront recalcul\xE9es comme lors du premier lancement du plugin. N\xE9cessite le red\xE9marrage d'Obsidian.",
+ button: "R\xE9initialiser"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}} : une nouvelle version est disponible",
+ resetDone: "\u2705 R\xE9initialisation termin\xE9e\nVeuillez red\xE9marrer Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Palette communautaire appliqu\xE9e par {{author}}"
+ }
+};
+
+// src/i18n/it.json
+var it_default = {
+ settings: {
+ palette: {
+ heading: "Tavolozza",
+ description: "Seleziona tavolozza",
+ options: {
+ adaptiveSoft: "\u{1F338} Adattiva tenue",
+ adaptiveBright: "\u{1F33A} Adattiva vivace",
+ custom: "Personalizzata"
+ },
+ shift: {
+ name: "Spostamento tavolozza",
+ description: "Se i colori di alcune etichette non vanno bene, puoi spostare la tavolozza."
+ },
+ custom: {
+ name: "Tavolozza personalizzata",
+ placeholder: "Incolla tavolozza",
+ description: "Il formato \xE8 XXXXXX-XXXXXX-XXXXXX per ogni colore RGB.",
+ community: {
+ heading: "Tavolozze della comunit\xE0",
+ description: "Di seguito sono riportate le tavolozze raccolte dalla discussione su GitHub. Clicca su qualsiasi scheda per applicarla istantaneamente o {{communityLinkStart}}condividi la tua{{communityLinkEnd}}.",
+ loading: "Caricamento tavolozze\u2026",
+ error: "Impossibile caricare le tavolozze al momento. Sentiti libero di aprire la discussione per sfogliarle manualmente.",
+ empty: "Nessuna tavolozza condivisa ancora. Sii il primo a pubblicarne una!",
+ applyHint: "Clicca per applicare questa tavolozza"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accessibilit\xE0",
+ description: "Mostra opzioni di accessibilit\xE0",
+ highTextContrast: {
+ name: "Alto contrasto del testo",
+ description: "Usa solo bianco e nero per i testi"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Sperimentale",
+ description: "Azioni pericolose o opzioni instabili che potrebbero essere modificate o rimosse in qualsiasi momento",
+ mixColors: {
+ name: "Mescola colori",
+ description: "Aiuta a rendere leggibile il testo"
+ },
+ gradientTransition: {
+ name: "Transizione gradiente"
+ },
+ tagColors: {
+ name: "Assegnazioni etichette",
+ description: "Scegli un colore dalla tavolozza corrente. Se cambi la tavolozza, l'etichetta si adatta al colore pi\xF9 vicino.",
+ placeholder: "Inizia a digitare un'etichetta",
+ clear: "Reimposta colore",
+ empty: "Nessuna sovrascrittura etichetta ancora",
+ applyHint: "Applica colore"
+ },
+ reset: {
+ name: "Reimposta configurazione",
+ description: "\u{1F6A8} Tutti i colori di tutte le etichette verranno ricalcolati come se fosse il primo avvio del plugin. Richiede il riavvio di Obsidian.",
+ button: "Reimposta"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: \xE8 disponibile una nuova versione",
+ resetDone: "\u2705 Reimpostazione completata\nSi prega di riavviare Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Tavolozza della comunit\xE0 applicata da {{author}}"
+ }
+};
+
+// src/i18n/ja.json
+var ja_default = {
+ settings: {
+ palette: {
+ heading: "\u30D1\u30EC\u30C3\u30C8",
+ description: "\u30D1\u30EC\u30C3\u30C8\u3092\u9078\u629E",
+ options: {
+ adaptiveSoft: "\u{1F338} \u30A2\u30C0\u30D7\u30C6\u30A3\u30D6\u30BD\u30D5\u30C8",
+ adaptiveBright: "\u{1F33A} \u30A2\u30C0\u30D7\u30C6\u30A3\u30D6\u30D6\u30E9\u30A4\u30C8",
+ custom: "\u30AB\u30B9\u30BF\u30E0"
+ },
+ shift: {
+ name: "\u30D1\u30EC\u30C3\u30C8\u30B7\u30D5\u30C8",
+ description: "\u4E00\u90E8\u306E\u30BF\u30B0\u306E\u8272\u304C\u5408\u308F\u306A\u3044\u5834\u5408\u306F\u3001\u30D1\u30EC\u30C3\u30C8\u3092\u30B7\u30D5\u30C8\u3067\u304D\u307E\u3059\u3002"
+ },
+ custom: {
+ name: "\u30AB\u30B9\u30BF\u30E0\u30D1\u30EC\u30C3\u30C8",
+ placeholder: "\u30D1\u30EC\u30C3\u30C8\u3092\u8CBC\u308A\u4ED8\u3051",
+ description: "\u5F62\u5F0F\u306F\u5404RGB\u30AB\u30E9\u30FC\u306B\u3064\u304D XXXXXX-XXXXXX-XXXXXX \u3067\u3059\u3002",
+ community: {
+ heading: "\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u30D1\u30EC\u30C3\u30C8",
+ description: "\u4EE5\u4E0B\u306FGitHub\u306E\u30C7\u30A3\u30B9\u30AB\u30C3\u30B7\u30E7\u30F3\u304B\u3089\u53CE\u96C6\u3055\u308C\u305F\u30D1\u30EC\u30C3\u30C8\u3067\u3059\u3002\u30AB\u30FC\u30C9\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u5373\u5EA7\u306B\u9069\u7528\u3059\u308B\u304B\u3001{{communityLinkStart}}\u81EA\u5206\u306E\u3082\u306E\u3092\u5171\u6709{{communityLinkEnd}}\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
+ loading: "\u30D1\u30EC\u30C3\u30C8\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D\u2026",
+ error: "\u73FE\u5728\u30D1\u30EC\u30C3\u30C8\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3002\u30C7\u30A3\u30B9\u30AB\u30C3\u30B7\u30E7\u30F3\u3092\u958B\u3044\u3066\u624B\u52D5\u3067\u95B2\u89A7\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
+ empty: "\u5171\u6709\u3055\u308C\u305F\u30D1\u30EC\u30C3\u30C8\u306F\u307E\u3060\u3042\u308A\u307E\u305B\u3093\u3002\u6700\u521D\u306B\u6295\u7A3F\u3057\u307E\u3057\u3087\u3046\uFF01",
+ applyHint: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u3053\u306E\u30D1\u30EC\u30C3\u30C8\u3092\u9069\u7528"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} \u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3",
+ description: "\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u8868\u793A",
+ highTextContrast: {
+ name: "\u9AD8\u30C6\u30AD\u30B9\u30C8\u30B3\u30F3\u30C8\u30E9\u30B9\u30C8",
+ description: "\u30C6\u30AD\u30B9\u30C8\u306B\u767D\u3068\u9ED2\u306E\u8272\u306E\u307F\u3092\u4F7F\u7528"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} \u5B9F\u9A13\u7684",
+ description: "\u3044\u3064\u3067\u3082\u5909\u66F4\u307E\u305F\u306F\u524A\u9664\u3055\u308C\u308B\u53EF\u80FD\u6027\u306E\u3042\u308B\u5371\u967A\u306A\u30A2\u30AF\u30B7\u30E7\u30F3\u307E\u305F\u306F\u4E0D\u5B89\u5B9A\u306A\u30AA\u30D7\u30B7\u30E7\u30F3",
+ mixColors: {
+ name: "\u8272\u3092\u6DF7\u305C\u308B",
+ description: "\u30C6\u30AD\u30B9\u30C8\u3092\u8AAD\u307F\u3084\u3059\u304F\u3059\u308B\u306E\u306B\u5F79\u7ACB\u3061\u307E\u3059"
+ },
+ gradientTransition: {
+ name: "\u30B0\u30E9\u30C7\u30FC\u30B7\u30E7\u30F3\u9077\u79FB"
+ },
+ tagColors: {
+ name: "\u30BF\u30B0\u306E\u5272\u308A\u5F53\u3066",
+ description: "\u73FE\u5728\u306E\u30D1\u30EC\u30C3\u30C8\u304B\u3089\u8272\u3092\u9078\u629E\u3057\u307E\u3059\u3002\u30D1\u30EC\u30C3\u30C8\u3092\u5909\u66F4\u3059\u308B\u3068\u3001\u30BF\u30B0\u306F\u6700\u3082\u8FD1\u3044\u8272\u306B\u9069\u5FDC\u3057\u307E\u3059\u3002",
+ placeholder: "\u30BF\u30B0\u306E\u5165\u529B\u3092\u958B\u59CB",
+ clear: "\u8272\u3092\u30EA\u30BB\u30C3\u30C8",
+ empty: "\u30BF\u30B0\u306E\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9\u306F\u307E\u3060\u3042\u308A\u307E\u305B\u3093",
+ applyHint: "\u8272\u3092\u9069\u7528"
+ },
+ reset: {
+ name: "\u8A2D\u5B9A\u3092\u30EA\u30BB\u30C3\u30C8",
+ description: "\u{1F6A8} \u3059\u3079\u3066\u306E\u30BF\u30B0\u306E\u3059\u3079\u3066\u306E\u8272\u304C\u3001\u30D7\u30E9\u30B0\u30A4\u30F3\u306E\u521D\u56DE\u8D77\u52D5\u6642\u306E\u3088\u3046\u306B\u518D\u8A08\u7B97\u3055\u308C\u307E\u3059\u3002Obsidian\u306E\u518D\u8D77\u52D5\u304C\u5FC5\u8981\u3067\u3059\u3002",
+ button: "\u30EA\u30BB\u30C3\u30C8"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: \u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5229\u7528\u53EF\u80FD\u3067\u3059",
+ resetDone: "\u2705 \u30EA\u30BB\u30C3\u30C8\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\nObsidian\u3092\u518D\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044",
+ communityPaletteApplied: "\u{1F3A8} {{author}} \u306B\u3088\u308B\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u30D1\u30EC\u30C3\u30C8\u304C\u9069\u7528\u3055\u308C\u307E\u3057\u305F"
+ }
+};
+
+// src/i18n/pt.json
+var pt_default = {
+ settings: {
+ palette: {
+ heading: "Paleta",
+ description: "Selecionar paleta",
+ options: {
+ adaptiveSoft: "\u{1F338} Suave adapt\xE1vel",
+ adaptiveBright: "\u{1F33A} Brilhante adapt\xE1vel",
+ custom: "Personalizada"
+ },
+ shift: {
+ name: "Deslocamento de paleta",
+ description: "Se as cores de algumas etiquetas n\xE3o se ajustarem, voc\xEA pode deslocar a paleta."
+ },
+ custom: {
+ name: "Paleta personalizada",
+ placeholder: "Colar paleta",
+ description: "O formato \xE9 XXXXXX-XXXXXX-XXXXXX para cada cor RGB.",
+ community: {
+ heading: "Paletas da comunidade",
+ description: "Abaixo est\xE3o paletas coletadas da discuss\xE3o no GitHub. Clique em qualquer cart\xE3o para aplic\xE1-la instantaneamente ou {{communityLinkStart}}compartilhe a sua{{communityLinkEnd}}.",
+ loading: "Carregando paletas\u2026",
+ error: "N\xE3o foi poss\xEDvel carregar as paletas agora. Sinta-se \xE0 vontade para abrir a discuss\xE3o para naveg\xE1-las manualmente.",
+ empty: "Nenhuma paleta compartilhada ainda. Seja o primeiro a postar uma!",
+ applyHint: "Clique para aplicar esta paleta"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Acessibilidade",
+ description: "Mostrar op\xE7\xF5es de acessibilidade",
+ highTextContrast: {
+ name: "Alto contraste de texto",
+ description: "Usar apenas cores branca e preta para textos"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimental",
+ description: "A\xE7\xF5es perigosas ou op\xE7\xF5es inst\xE1veis que podem ser alteradas ou removidas a qualquer momento",
+ mixColors: {
+ name: "Misturar cores",
+ description: "Ajuda a tornar o texto leg\xEDvel"
+ },
+ gradientTransition: {
+ name: "Transi\xE7\xE3o de gradiente"
+ },
+ tagColors: {
+ name: "Atribui\xE7\xF5es de etiquetas",
+ description: "Escolha uma cor da paleta atual. Se voc\xEA mudar a paleta, a etiqueta se adapta \xE0 cor mais pr\xF3xima.",
+ placeholder: "Comece a digitar uma etiqueta",
+ clear: "Redefinir cor",
+ empty: "Nenhuma substitui\xE7\xE3o de etiqueta ainda",
+ applyHint: "Aplicar cor"
+ },
+ reset: {
+ name: "Redefinir configura\xE7\xE3o",
+ description: "\u{1F6A8} Todas as cores de todas as etiquetas ser\xE3o recalculadas como se fosse o primeiro lan\xE7amento do plugin. Requer reinicializa\xE7\xE3o do Obsidian.",
+ button: "Redefinir"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: uma nova vers\xE3o est\xE1 dispon\xEDvel",
+ resetDone: "\u2705 Redefini\xE7\xE3o conclu\xEDda\nPor favor, reinicie o Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Paleta da comunidade aplicada por {{author}}"
+ }
+};
+
+// src/i18n/ru.json
+var ru_default = {
+ settings: {
+ palette: {
+ heading: "\u041F\u0430\u043B\u0438\u0442\u0440\u0430",
+ description: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0430\u043B\u0438\u0442\u0440\u0443",
+ options: {
+ adaptiveSoft: "\u{1F338} \u0410\u0434\u0430\u043F\u0442\u0438\u0432\u043D\u0430\u044F \u043C\u044F\u0433\u043A\u0430\u044F",
+ adaptiveBright: "\u{1F33A} \u0410\u0434\u0430\u043F\u0442\u0438\u0432\u043D\u0430\u044F \u044F\u0440\u043A\u0430\u044F",
+ custom: "\u0421\u0432\u043E\u044F"
+ },
+ shift: {
+ name: "\u0421\u0434\u0432\u0438\u0433 \u043F\u0430\u043B\u0438\u0442\u0440\u044B",
+ description: "\u0415\u0441\u043B\u0438 \u0446\u0432\u0435\u0442\u0430 \u043A\u0430\u043A\u0438\u0445-\u0442\u043E \u0442\u0435\u0433\u043E\u0432 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442, \u043C\u043E\u0436\u043D\u043E \u0441\u0434\u0432\u0438\u043D\u0443\u0442\u044C \u043F\u0430\u043B\u0438\u0442\u0440\u0443."
+ },
+ custom: {
+ name: "\u0421\u0432\u043E\u044F \u043F\u0430\u043B\u0438\u0442\u0440\u0430",
+ placeholder: "\u0412\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u043F\u0430\u043B\u0438\u0442\u0440\u0443",
+ description: "\u0424\u043E\u0440\u043C\u0430\u0442 XXXXXX-XXXXXX-XXXXXX \u0434\u043B\u044F \u043A\u0430\u0436\u0434\u043E\u0433\u043E RGB-\u0446\u0432\u0435\u0442\u0430.",
+ community: {
+ heading: "\u041F\u0430\u043B\u0438\u0442\u0440\u044B \u0441\u043E\u043E\u0431\u0449\u0435\u0441\u0442\u0432\u0430",
+ description: "\u041D\u0438\u0436\u0435 \u0441\u043E\u0431\u0440\u0430\u043D\u044B \u043F\u0430\u043B\u0438\u0442\u0440\u044B \u0438\u0437 \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u044F \u043D\u0430 GitHub. \u041A\u043B\u0438\u043A\u043D\u0438\u0442\u0435 \u043F\u043E \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0435\u0451 \u0438\u043B\u0438 {{communityLinkStart}}\u043F\u043E\u0434\u0435\u043B\u0438\u0442\u0435\u0441\u044C \u0441\u0432\u043E\u0435\u0439{{communityLinkEnd}}.",
+ loading: "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043C \u043F\u0430\u043B\u0438\u0442\u0440\u044B\u2026",
+ error: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0430\u043B\u0438\u0442\u0440\u044B. \u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0438\u0445 \u0432\u0440\u0443\u0447\u043D\u0443\u044E.",
+ empty: "\u041F\u043E\u043A\u0430 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u043F\u043E\u0434\u0435\u043B\u0438\u043B\u0441\u044F \u043F\u0430\u043B\u0438\u0442\u0440\u0430\u043C\u0438. \u0411\u0443\u0434\u044C\u0442\u0435 \u043F\u0435\u0440\u0432\u044B\u043C\u0438!",
+ applyHint: "\u041D\u0430\u0436\u043C\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u044D\u0442\u0443 \u043F\u0430\u043B\u0438\u0442\u0440\u0443"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} \u0414\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C",
+ description: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u0438",
+ highTextContrast: {
+ name: "\u0412\u044B\u0441\u043E\u043A\u0438\u0439 \u043A\u043E\u043D\u0442\u0440\u0430\u0441\u0442 \u0442\u0435\u043A\u0441\u0442\u0430",
+ description: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0431\u0435\u043B\u044B\u0439 \u0438 \u0447\u0435\u0440\u043D\u044B\u0439 \u0446\u0432\u0435\u0442\u0430 \u0442\u0435\u043A\u0441\u0442\u0430"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} \u042D\u043A\u0441\u043F\u0435\u0440\u0438\u043C\u0435\u043D\u0442\u0430\u043B\u044C\u043D\u044B\u0435",
+ description: "\u041E\u043F\u0430\u0441\u043D\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0438\u043B\u0438 \u043D\u0435\u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u044B\u0435 \u043E\u043F\u0446\u0438\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u044B \u0438\u043B\u0438 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u0432 \u043B\u044E\u0431\u043E\u0439 \u043C\u043E\u043C\u0435\u043D\u0442",
+ mixColors: {
+ name: "\u0421\u043C\u0435\u0448\u0438\u0432\u0430\u0442\u044C \u0446\u0432\u0435\u0442\u0430",
+ description: "\u041F\u043E\u043C\u043E\u0433\u0430\u0435\u0442 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0442\u0435\u043A\u0441\u0442 \u0447\u0438\u0442\u0430\u0435\u043C\u044B\u043C"
+ },
+ gradientTransition: {
+ name: "\u041F\u043B\u0430\u0432\u043D\u044B\u0439 \u0433\u0440\u0430\u0434\u0438\u0435\u043D\u0442"
+ },
+ tagColors: {
+ name: "\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0446\u0432\u0435\u0442\u043E\u0432",
+ description: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0446\u0432\u0435\u0442 \u0438\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u043F\u0430\u043B\u0438\u0442\u0440\u044B. \u041F\u0440\u0438 \u0441\u043C\u0435\u043D\u0435 \u043F\u0430\u043B\u0438\u0442\u0440\u044B \u0442\u0435\u0433 \u0430\u0434\u0430\u043F\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044F \u043A \u0431\u043B\u0438\u0436\u0430\u0439\u0448\u0435\u043C\u0443 \u0446\u0432\u0435\u0442\u0443.",
+ placeholder: "\u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0432\u0432\u043E\u0434\u0438\u0442\u044C \u0442\u0435\u0433",
+ clear: "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0446\u0432\u0435\u0442",
+ empty: "\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0445 \u0446\u0432\u0435\u0442\u043E\u0432",
+ applyHint: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0446\u0432\u0435\u0442"
+ },
+ reset: {
+ name: "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u043A\u043E\u043D\u0444\u0438\u0433",
+ description: "\u{1F6A8} \u0412\u0441\u0435 \u0446\u0432\u0435\u0442\u0430 \u0432\u0441\u0435\u0445 \u0442\u0435\u0433\u043E\u0432 \u0431\u0443\u0434\u0443\u0442 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u043D\u044B \u0442\u0430\u043A, \u0431\u0443\u0434\u0442\u043E \u043F\u043B\u0430\u0433\u0438\u043D \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u0432\u043F\u0435\u0440\u0432\u044B\u0435. \u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A Obsidian.",
+ button: "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F",
+ resetDone: "\u2705 \u0421\u0431\u0440\u043E\u0441 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\n\u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 Obsidian",
+ communityPaletteApplied: "\u{1F3A8} \u041F\u0430\u043B\u0438\u0442\u0440\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u0441\u0442\u0432\u0430 \u043E\u0442 {{author}} \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u0430"
+ }
+};
+
+// src/i18n/zh.json
+var zh_default = {
+ settings: {
+ palette: {
+ heading: "\u8C03\u8272\u677F",
+ description: "\u9009\u62E9\u8C03\u8272\u677F",
+ options: {
+ adaptiveSoft: "\u{1F338} \u81EA\u9002\u5E94\u67D4\u548C",
+ adaptiveBright: "\u{1F33A} \u81EA\u9002\u5E94\u9C9C\u8273",
+ custom: "\u81EA\u5B9A\u4E49"
+ },
+ shift: {
+ name: "\u8C03\u8272\u677F\u504F\u79FB",
+ description: "\u5982\u679C\u67D0\u4E9B\u6807\u7B7E\u7684\u989C\u8272\u4E0D\u5408\u9002\uFF0C\u53EF\u4EE5\u504F\u79FB\u8C03\u8272\u677F\u3002"
+ },
+ custom: {
+ name: "\u81EA\u5B9A\u4E49\u8C03\u8272\u677F",
+ placeholder: "\u7C98\u8D34\u8C03\u8272\u677F",
+ description: "\u6BCF\u79CD RGB \u989C\u8272\u7684\u683C\u5F0F\u4E3A XXXXXX-XXXXXX-XXXXXX\u3002",
+ community: {
+ heading: "\u793E\u533A\u8C03\u8272\u677F",
+ description: "\u4E0B\u65B9\u5C55\u793A\u6765\u81EA GitHub \u8BA8\u8BBA\u533A\u7684\u8C03\u8272\u677F\u3002\u70B9\u51FB\u4EFB\u610F\u5361\u7247\u5373\u53EF\u7ACB\u5373\u5E94\u7528\uFF0C\u6216{{communityLinkStart}}\u5206\u4EAB\u4F60\u7684\u914D\u8272{{communityLinkEnd}}\u3002",
+ loading: "\u6B63\u5728\u52A0\u8F7D\u8C03\u8272\u677F\u2026",
+ error: "\u6682\u65F6\u65E0\u6CD5\u52A0\u8F7D\u8C03\u8272\u677F\uFF0C\u8BF7\u6253\u5F00\u8BA8\u8BBA\u9875\u9762\u624B\u52A8\u67E5\u770B\u3002",
+ empty: "\u8FD8\u6CA1\u6709\u4EBA\u5206\u4EAB\u8C03\u8272\u677F\uFF0C\u6B22\u8FCE\u7387\u5148\u8D21\u732E\uFF01",
+ applyHint: "\u70B9\u51FB\u5373\u53EF\u5E94\u7528\u6B64\u8C03\u8272\u677F"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} \u65E0\u969C\u788D",
+ description: "\u663E\u793A\u65E0\u969C\u788D\u9009\u9879",
+ highTextContrast: {
+ name: "\u9AD8\u5BF9\u6BD4\u6587\u672C",
+ description: "\u4EC5\u5BF9\u6587\u672C\u4F7F\u7528\u9ED1\u8272\u548C\u767D\u8272"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} \u5B9E\u9A8C\u529F\u80FD",
+ description: "\u5371\u9669\u64CD\u4F5C\u6216\u6781\u5176\u4E0D\u7A33\u5B9A\u7684\u9009\u9879\uFF0C\u968F\u65F6\u53EF\u80FD\u4FEE\u6539\u6216\u79FB\u9664",
+ mixColors: {
+ name: "\u6DF7\u5408\u989C\u8272",
+ description: "\u6709\u52A9\u4E8E\u4FDD\u6301\u6587\u672C\u53EF\u8BFB"
+ },
+ gradientTransition: {
+ name: "\u6E10\u53D8\u8FC7\u6E21"
+ },
+ tagColors: {
+ name: "\u6807\u7B7E\u989C\u8272\u5206\u914D",
+ description: "\u8BF7\u4ECE\u5F53\u524D\u8C03\u8272\u677F\u4E2D\u9009\u62E9\u989C\u8272\u3002\u66F4\u6362\u8C03\u8272\u677F\u65F6\uFF0C\u6807\u7B7E\u4F1A\u81EA\u52A8\u9002\u914D\u6700\u63A5\u8FD1\u7684\u989C\u8272\u3002",
+ placeholder: "\u5F00\u59CB\u8F93\u5165\u6807\u7B7E",
+ clear: "\u91CD\u7F6E\u989C\u8272",
+ empty: "\u8FD8\u6CA1\u6709\u5206\u914D\u6807\u7B7E\u989C\u8272",
+ applyHint: "\u5E94\u7528\u989C\u8272"
+ },
+ reset: {
+ name: "\u91CD\u7F6E\u914D\u7F6E",
+ description: "\u{1F6A8} \u6240\u6709\u6807\u7B7E\u7684\u989C\u8272\u90FD\u4F1A\u91CD\u65B0\u8BA1\u7B97\uFF0C\u5C31\u50CF\u7B2C\u4E00\u6B21\u542F\u52A8\u63D2\u4EF6\u4E00\u6837\u3002\u9700\u8981\u91CD\u65B0\u542F\u52A8 Obsidian\u3002",
+ button: "\u91CD\u7F6E"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}\uFF1A\u6709\u53EF\u7528\u7684\u65B0\u7248\u672C",
+ resetDone: "\u2705 \u91CD\u7F6E\u5B8C\u6210\n\u8BF7\u91CD\u65B0\u542F\u52A8 Obsidian",
+ communityPaletteApplied: "\u{1F3A8} \u5DF2\u5E94\u7528\u6765\u81EA {{author}} \u7684\u793E\u533A\u8C03\u8272\u677F"
+ }
+};
+
+// src/logger.ts
+var logger = {
+ warn: (message) => {
+ console.warn(message);
+ },
+ error: (message) => {
+ console.error(message);
+ },
+ log: (message) => {
+ console.log(message);
+ }
+};
+
+// src/i18n/index.ts
+var locales = {
+ en: en_default,
+ ru: ru_default,
+ de: de_default,
+ zh: zh_default,
+ es: es_default,
+ fr: fr_default,
+ it: it_default,
+ pt: pt_default,
+ ja: ja_default
+};
+var I18n = class {
+ static t(key, params) {
+ const locale = window.localStorage.getItem("language") || "en";
+ const keys = key.split(".");
+ let translations = locales[locale] || locales["en"];
+ for (const k of keys) {
+ if (typeof translations !== "object" || translations === null) {
+ logger.warn(`Translation missing: ${key}`);
+ translations = locales["en"];
+ return this.extractEnglishValue(keys, key);
+ }
+ if (translations[k] === void 0) {
+ logger.warn(`Translation missing: ${key}`);
+ translations = locales["en"];
+ return this.extractEnglishValue(keys, key);
+ }
+ translations = translations[k];
+ }
+ if (typeof translations !== "string") {
+ return key;
+ }
+ let result = translations;
+ if (params) {
+ Object.entries(params).forEach(([key2, value]) => {
+ const placeholder = new RegExp(`{{${key2}}}`, "g");
+ result = result.replace(placeholder, value);
+ });
+ }
+ return result;
+ }
+ static extractEnglishValue(keys, originalKey) {
+ let engValue = locales["en"];
+ for (const ek of keys) {
+ if (typeof engValue !== "object" || engValue === null) {
+ engValue = void 0;
+ break;
+ }
+ if (engValue[ek] === void 0) {
+ engValue = void 0;
+ break;
+ }
+ engValue = engValue[ek];
+ }
+ if (typeof engValue === "string") {
+ return engValue;
+ }
+ return originalKey;
+ }
+};
+
+// src/tagUtils.ts
+function normalizeTagName(tagName) {
+ return tagName.replace(/#/g, "").trim().replace(/\s+/g, "").replace(/\/+$/, "").toLowerCase();
+}
+function normalizePaletteIndex(index, length) {
+ if (length <= 0) {
+ return 0;
+ }
+ const normalized = index % length;
+ return normalized < 0 ? normalized + length : normalized;
+}
+
+// src/CommunityPalettesService.ts
+var import_obsidian = require("obsidian");
+var DISCUSSION_COMMENTS_URL = "https://api.github.com/repos/pfrankov/obsidian-colored-tags/discussions/18/comments";
+var PER_PAGE = 100;
+var POSITIVE_REACTIONS = [
+ "+1",
+ "heart",
+ "hooray",
+ "rocket",
+ "eyes",
+ "laugh"
+];
+var NEGATIVE_REACTIONS = ["-1", "confused"];
+var CommunityPalettesService = class {
+ static async getCommunityPalettes() {
+ if (this.cache) {
+ return this.cache;
+ }
+ if (this.pendingRequest) {
+ return this.pendingRequest;
+ }
+ this.pendingRequest = this.fetchCommunityPalettes().then((palettes) => {
+ this.cache = palettes;
+ return palettes;
+ }).finally(() => {
+ this.pendingRequest = void 0;
+ });
+ return this.pendingRequest;
+ }
+ static async fetchCommunityPalettes() {
+ const palettes = await this.collectPalettes(
+ 1,
+ [],
+ /* @__PURE__ */ new Set(),
+ /* @__PURE__ */ new Set()
+ );
+ return palettes.sort((a2, b2) => {
+ if (a2.score === b2.score) {
+ return a2.order - b2.order;
+ }
+ return b2.score - a2.score;
+ }).map(({ order: _order, ...palette }) => palette);
+ }
+ static async collectPalettes(page, palettes, uniqueValues, authorsWithPalette) {
+ var _a;
+ const response = await this.requestPage(page);
+ if (!response) {
+ return palettes;
+ }
+ const comments = (_a = response.json) != null ? _a : [];
+ if (!comments.length) {
+ return palettes;
+ }
+ this.appendPalettesFromComments(
+ comments,
+ palettes,
+ uniqueValues,
+ authorsWithPalette
+ );
+ if (comments.length < PER_PAGE) {
+ return palettes;
+ }
+ return this.collectPalettes(
+ page + 1,
+ palettes,
+ uniqueValues,
+ authorsWithPalette
+ );
+ }
+ static async requestPage(page) {
+ try {
+ const response = await (0, import_obsidian.requestUrl)({
+ url: `${DISCUSSION_COMMENTS_URL}?per_page=${PER_PAGE}&page=${page}`,
+ headers: {
+ Accept: "application/vnd.github+json"
+ },
+ throw: false
+ });
+ if (response.status !== 200) {
+ console.error(
+ `GitHub API responded with ${response.status} on page ${page}`
+ );
+ return null;
+ }
+ return response;
+ } catch (error) {
+ console.error("Failed to fetch community palettes", error);
+ return null;
+ }
+ }
+ static appendPalettesFromComments(comments, palettes, uniqueValues, authorsWithPalette) {
+ var _a, _b;
+ const scoredComments = comments.map((comment) => ({
+ comment,
+ score: this.getReactionScore(comment.reactions)
+ })).filter(({ score }) => score >= 0);
+ for (const { comment, score } of scoredComments) {
+ const author = ((_a = comment.user) == null ? void 0 : _a.login) || "unknown";
+ if (authorsWithPalette.has(author)) {
+ continue;
+ }
+ const palettesFromComment = this.extractPalettesFromBody(
+ comment.body || ""
+ );
+ const [value] = palettesFromComment;
+ if (!value || uniqueValues.has(value)) {
+ continue;
+ }
+ uniqueValues.add(value);
+ authorsWithPalette.add(author);
+ palettes.push({
+ id: `${comment.id}-0`,
+ value,
+ colors: value.split("-").filter(Boolean).map((hex) => `#${hex}`),
+ author,
+ authorUrl: (_b = comment.user) == null ? void 0 : _b.html_url,
+ commentUrl: comment.html_url,
+ score,
+ order: palettes.length
+ });
+ }
+ }
+ static extractPalettesFromBody(body) {
+ const regex = /\b([0-9a-fA-F]{6}(?:-[0-9a-fA-F]{6})+)\b/g;
+ const results = /* @__PURE__ */ new Set();
+ let match;
+ while ((match = regex.exec(body)) !== null) {
+ results.add(match[1].toLowerCase());
+ }
+ return Array.from(results);
+ }
+ static getReactionScore(reactions) {
+ if (!reactions) {
+ return 0;
+ }
+ const positive = POSITIVE_REACTIONS.reduce((total, key) => {
+ var _a;
+ return total + ((_a = reactions[key]) != null ? _a : 0);
+ }, 0);
+ const negative = NEGATIVE_REACTIONS.reduce((total, key) => {
+ var _a;
+ return total + ((_a = reactions[key]) != null ? _a : 0);
+ }, 0);
+ return positive - negative;
+ }
+};
+CommunityPalettesService.cache = null;
+
+// src/ColoredTagsPluginSettingTab.ts
+var SELECTED_CLASS = "is-selected";
+var ColoredTagsPluginSettingTab = class extends import_obsidian2.PluginSettingTab {
+ constructor(app, plugin) {
+ super(app, plugin);
+ this.showExperimental = false;
+ this.showAccessibility = false;
+ this.communityPaletteDescriptionCounter = 0;
+ this.communityPaletteCards = /* @__PURE__ */ new Map();
+ this.paletteChangeSubscribers = [];
+ this.plugin = plugin;
+ }
+ renderPalette(paletteEl, animate = false) {
+ paletteEl.empty();
+ const palette = this.getActivePalette();
+ palette.forEach((paletteColor, index) => {
+ const firstElementStyles = "border-radius: var(--radius-m) 0 0 var(--radius-m)";
+ const lastElementStyles = "border-radius: 0 var(--radius-m) var(--radius-m) 0";
+ const styles = [
+ index === 0 && firstElementStyles,
+ "flex: 1",
+ "height: 2em",
+ `background-color: ${paletteColor}`,
+ index === palette.length - 1 && lastElementStyles
+ ];
+ paletteEl.createEl("div", {
+ attr: { style: styles.filter(Boolean).join(";") }
+ });
+ });
+ if (animate && paletteEl.animate) {
+ paletteEl.animate([{ opacity: 0.2 }, { opacity: 1 }], {
+ duration: 220,
+ easing: "ease-out"
+ });
+ }
+ }
+ renderTags(containerEl) {
+ const tagsObject = this.app.metadataCache.getTags();
+ const tagsArray = Object.keys(tagsObject);
+ if (!tagsArray.length) {
+ return;
+ }
+ const tagEl = containerEl.createEl("div", {
+ cls: "tagsExample"
+ });
+ tagsArray.sort((a2, b2) => {
+ return tagsObject[b2] - tagsObject[a2];
+ });
+ const mostPopularTags = tagsArray.splice(0, 2);
+ const mostPopularNestedTags = Object.values(
+ tagsArray.reduce((acc, tag) => {
+ const nestingLevel = tag.split("/").length;
+ if (!acc[nestingLevel]) {
+ acc[nestingLevel] = tag;
+ }
+ return acc;
+ }, {})
+ );
+ [...mostPopularTags, ...mostPopularNestedTags].forEach(
+ (tag) => {
+ const link = tagEl.createEl("a", { attr: { href: tag } });
+ link.classList.add("tag");
+ link.innerText = tag;
+ }
+ );
+ }
+ display() {
+ const { containerEl } = this;
+ containerEl.empty();
+ containerEl.classList.add("colored-tags-settings");
+ this.paletteChangeSubscribers = [];
+ this.renderTags(containerEl);
+ const paletteEl = containerEl.createEl("div", {
+ cls: "palette"
+ });
+ this.renderPalette(paletteEl);
+ this.renderPaletteSettings(containerEl, paletteEl);
+ this.renderAccessibilitySettings(containerEl);
+ this.renderExperimentalSettings(containerEl);
+ }
+ renderPaletteSettings(containerEl, paletteEl) {
+ new import_obsidian2.Setting(containerEl).setHeading().setName(I18n.t("settings.palette.heading")).setDesc(I18n.t("settings.palette.description")).addDropdown(
+ (dropdown) => dropdown.addOptions({
+ ["adaptive-soft" /* ADAPTIVE_SOFT */]: I18n.t(
+ "settings.palette.options.adaptiveSoft"
+ ),
+ ["adaptive-bright" /* ADAPTIVE_BRIGHT */]: I18n.t(
+ "settings.palette.options.adaptiveBright"
+ ),
+ ["custom" /* CUSTOM */]: I18n.t(
+ "settings.palette.options.custom"
+ )
+ }).setValue(String(this.plugin.settings.palette.selected)).onChange(async (value) => {
+ this.plugin.settings.palette.selected = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ if (this.plugin.settings.palette.selected === "custom") {
+ this.renderCustomPaletteField(containerEl, paletteEl);
+ }
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.palette.shift.name")).setDesc(I18n.t("settings.palette.shift.description")).addSlider(
+ (slider) => slider.setLimits(0, this.plugin.palettes.light.length - 1, 1).setValue(this.plugin.settings.palette.seed).onChange(async (value) => {
+ slider.showTooltip();
+ this.plugin.settings.palette.seed = value;
+ await this.plugin.saveSettings();
+ this.renderPalette(paletteEl);
+ this.notifyPaletteChange();
+ })
+ );
+ }
+ renderCustomPaletteField(containerEl, paletteEl) {
+ let customPaletteInput = null;
+ const customPaletteField = new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.palette.custom.name")).setDesc("").addText((text) => {
+ customPaletteInput = text;
+ text.inputEl.style.minWidth = "100%";
+ text.setValue(
+ this.plugin.settings.palette.custom
+ ).setPlaceholder(I18n.t("settings.palette.custom.placeholder"));
+ text.onChange(async (value) => {
+ if (/^([A-Fa-f0-9]{6}(-|$))+$/i.test(value)) {
+ this.plugin.settings.palette.custom = value;
+ await this.plugin.saveSettings();
+ this.renderPalette(paletteEl);
+ this.notifyPaletteChange();
+ this.updateCommunityPaletteSelection();
+ }
+ });
+ });
+ customPaletteField.descEl.innerHTML = I18n.t(
+ "settings.palette.custom.description"
+ );
+ if (customPaletteInput) {
+ this.renderCommunityPalettesSection(
+ containerEl,
+ paletteEl,
+ customPaletteInput
+ );
+ }
+ }
+ renderCommunityPalettesSection(containerEl, paletteEl, inputComponent) {
+ this.communityPaletteDescriptionCounter = 0;
+ this.communityPaletteCards = /* @__PURE__ */ new Map();
+ const sectionSetting = new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.palette.custom.community.heading")).setDesc("");
+ sectionSetting.settingEl.classList.add("community-palettes");
+ const descEl = sectionSetting.descEl;
+ descEl.empty();
+ const communityLinkStart = '';
+ const communityLinkEnd = "";
+ const descriptionEl = descEl.createDiv({
+ cls: "community-palettes__description"
+ });
+ descriptionEl.innerHTML = I18n.t(
+ "settings.palette.custom.community.description",
+ { communityLinkStart, communityLinkEnd }
+ );
+ const scrollContainer = descEl.createDiv({
+ cls: "community-palettes__scroll"
+ });
+ const gridEl = scrollContainer.createDiv({
+ cls: "community-palettes__grid"
+ });
+ const statusEl = scrollContainer.createDiv({
+ cls: "community-palettes__status",
+ text: I18n.t("settings.palette.custom.community.loading")
+ });
+ CommunityPalettesService.getCommunityPalettes().then((palettes) => {
+ if (!palettes.length) {
+ statusEl.textContent = I18n.t(
+ "settings.palette.custom.community.empty"
+ );
+ return;
+ }
+ statusEl.remove();
+ palettes.forEach((palette) => {
+ const card = this.renderCommunityPaletteCard(
+ gridEl,
+ palette,
+ paletteEl,
+ inputComponent
+ );
+ this.communityPaletteCards.set(
+ this.normalizePaletteValue(palette.value),
+ card
+ );
+ });
+ this.updateCommunityPaletteSelection();
+ }).catch((error) => {
+ console.error(error);
+ statusEl.textContent = I18n.t(
+ "settings.palette.custom.community.error"
+ );
+ });
+ }
+ renderCommunityPaletteCard(gridEl, palette, paletteEl, inputComponent) {
+ const descriptionId = `community-palette-desc-${this.communityPaletteDescriptionCounter++}`;
+ const card = gridEl.createDiv({
+ cls: "community-palette-card",
+ attr: {
+ role: "button",
+ tabindex: "0",
+ "aria-describedby": descriptionId,
+ "aria-pressed": "false"
+ }
+ });
+ card.createSpan({
+ cls: "visually-hidden",
+ attr: { id: descriptionId },
+ text: I18n.t("settings.palette.custom.community.applyHint")
+ });
+ const paletteRow = card.createDiv({
+ cls: "community-palette-card__preview"
+ });
+ palette.colors.forEach((color) => {
+ paletteRow.createDiv({
+ attr: {
+ style: `flex: 1; background-color: ${color};`
+ }
+ });
+ });
+ const metaEl = card.createDiv({
+ cls: "community-palette-card__meta"
+ });
+ metaEl.createSpan({
+ cls: "community-palette-card__author",
+ text: palette.author
+ });
+ metaEl.createSpan({
+ cls: "community-palette-card__upvotes",
+ text: `\u2764\uFE0F ${palette.score}`
+ });
+ const applyPalette = () => {
+ void this.applyCommunityPalette(palette, inputComponent, paletteEl);
+ };
+ card.addEventListener("click", applyPalette);
+ card.addEventListener("keydown", (event) => {
+ if (event.key === "Enter" || event.key === " ") {
+ event.preventDefault();
+ applyPalette();
+ }
+ });
+ return card;
+ }
+ async applyCommunityPalette(palette, inputComponent, paletteEl) {
+ this.plugin.settings.palette.custom = palette.value;
+ inputComponent.setValue(palette.value);
+ await this.plugin.saveSettings();
+ this.renderPalette(paletteEl, true);
+ this.notifyPaletteChange();
+ this.updateCommunityPaletteSelection();
+ new import_obsidian2.Notice(
+ I18n.t("notices.communityPaletteApplied", {
+ author: palette.author
+ }),
+ 4e3
+ );
+ }
+ normalizePaletteValue(value) {
+ return (value || "").replace(/#/g, "").replace(/\s+/g, "").trim().toLowerCase();
+ }
+ updateCommunityPaletteSelection() {
+ if (!this.communityPaletteCards.size) {
+ return;
+ }
+ const isCustomSelected = this.plugin.settings.palette.selected === "custom" /* CUSTOM */;
+ const normalizedCustom = isCustomSelected ? this.normalizePaletteValue(this.plugin.settings.palette.custom) : "";
+ const hasMatch = Boolean(normalizedCustom);
+ this.communityPaletteCards.forEach((card, value) => {
+ const isSelected = hasMatch && value === normalizedCustom;
+ if (isSelected) {
+ card.classList.add(SELECTED_CLASS);
+ } else {
+ card.classList.remove(SELECTED_CLASS);
+ }
+ card.setAttribute("aria-pressed", isSelected ? "true" : "false");
+ });
+ }
+ renderAccessibilitySettings(containerEl) {
+ new import_obsidian2.Setting(containerEl).setHeading().setName(I18n.t("settings.accessibility.heading")).setDesc(I18n.t("settings.accessibility.description")).addToggle(
+ (toggle) => toggle.setValue(this.showAccessibility).onChange(async (value) => {
+ this.showAccessibility = value;
+ this.display();
+ })
+ );
+ if (this.showAccessibility) {
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.accessibility.highTextContrast.name")).setDesc(
+ I18n.t(
+ "settings.accessibility.highTextContrast.description"
+ )
+ ).addToggle(
+ (toggle) => toggle.setValue(
+ this.plugin.settings.accessibility.highTextContrast
+ ).onChange(async (value) => {
+ this.plugin.settings.accessibility.highTextContrast = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ }
+ }
+ renderExperimentalSettings(containerEl) {
+ new import_obsidian2.Setting(containerEl).setHeading().setName(I18n.t("settings.experimental.heading")).setDesc(I18n.t("settings.experimental.description")).addToggle(
+ (toggle) => toggle.setValue(this.showExperimental).onChange(async (value) => {
+ this.showExperimental = value;
+ this.display();
+ })
+ );
+ if (!this.showExperimental) {
+ return;
+ }
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.mixColors.name")).setDesc(I18n.t("settings.experimental.mixColors.description")).addToggle(
+ (toggle) => toggle.setValue(this.plugin.settings.mixColors).onChange(async (value) => {
+ this.plugin.settings.mixColors = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.gradientTransition.name")).addToggle(
+ (toggle) => toggle.setValue(this.plugin.settings.transition).onChange(async (value) => {
+ this.plugin.settings.transition = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ this.renderTagPaletteOverrides(containerEl);
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.reset.name")).setDesc(I18n.t("settings.experimental.reset.description")).addButton(
+ (button) => button.setButtonText(I18n.t("settings.experimental.reset.button")).setClass("mod-warning").onClick(async () => {
+ new import_obsidian2.Notice(I18n.t("notices.resetDone"), 1e4);
+ button.setDisabled(true);
+ button.buttonEl.setAttribute("disabled", "true");
+ button.buttonEl.classList.remove("mod-warning");
+ this.plugin.settings = Object.assign(
+ {},
+ DEFAULT_SETTINGS
+ );
+ await this.plugin.saveSettings();
+ this.updateCommunityPaletteSelection();
+ })
+ );
+ }
+ renderTagPaletteOverrides(containerEl) {
+ const tagPaletteSetting = new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.tagColors.name")).setDesc(I18n.t("settings.experimental.tagColors.description"));
+ tagPaletteSetting.settingEl.classList.add("tag-color-setting-item");
+ tagPaletteSetting.controlEl.empty();
+ const wrapper = tagPaletteSetting.controlEl.createDiv({
+ cls: "tag-color-setting"
+ });
+ const controlsRow = wrapper.createDiv({
+ cls: "tag-color-setting__row"
+ });
+ const inputContainer = controlsRow.createDiv({
+ cls: "tag-color-setting__input"
+ });
+ const datalistId = `tag-color-list-${Date.now()}`;
+ const datalist = inputContainer.createEl("datalist", {
+ attr: { id: datalistId }
+ });
+ const refreshTagOptions = () => this.populateTagOptions(datalist);
+ refreshTagOptions();
+ let currentTag = "";
+ const tagInput = new import_obsidian2.TextComponent(inputContainer);
+ tagInput.setPlaceholder(
+ I18n.t("settings.experimental.tagColors.placeholder")
+ ).setValue("");
+ tagInput.inputEl.setAttr("list", datalistId);
+ tagInput.onChange((value) => {
+ currentTag = normalizeTagName(value);
+ updateSelectedSwatch();
+ });
+ const paletteEl = controlsRow.createDiv({
+ cls: "tag-color-setting__palette"
+ });
+ const listContainer = wrapper.createDiv({
+ cls: "tag-color-setting__chips"
+ });
+ const handleAssignmentsChange = () => {
+ updateSelectionState();
+ refreshTagOptions();
+ };
+ const updateSelectedSwatch = () => {
+ var _a;
+ const palette = this.getActivePalette();
+ const assignedIndex = (_a = this.plugin.settings.tagColors && this.plugin.settings.tagColors[currentTag]) != null ? _a : null;
+ Array.from(paletteEl.children).forEach((child, index) => {
+ child.classList.toggle(
+ SELECTED_CLASS,
+ assignedIndex !== null && assignedIndex !== void 0 && normalizePaletteIndex(assignedIndex, palette.length) === index
+ );
+ child.disabled = !currentTag;
+ });
+ };
+ const updateSelectionState = () => {
+ updateSelectedSwatch();
+ };
+ const applySelection = async (index) => {
+ if (!currentTag) {
+ return;
+ }
+ this.plugin.settings.tagColors[currentTag] = index;
+ await this.plugin.saveSettings();
+ this.plugin.colorizeTag(currentTag);
+ this.renderTagColorAssignments(
+ listContainer,
+ handleAssignmentsChange
+ );
+ };
+ this.renderPaletteSwatches(paletteEl, applySelection);
+ this.renderTagColorAssignments(listContainer, handleAssignmentsChange);
+ this.subscribeToPaletteChange(() => {
+ this.renderPaletteSwatches(paletteEl, applySelection);
+ this.renderTagColorAssignments(
+ listContainer,
+ handleAssignmentsChange
+ );
+ updateSelectionState();
+ });
+ updateSelectionState();
+ }
+ renderPaletteSwatches(paletteEl, onSelect) {
+ paletteEl.empty();
+ const palette = this.getActivePalette();
+ palette.forEach((color, index) => {
+ const swatch = paletteEl.createEl("button", {
+ cls: "tag-color-setting__swatch",
+ attr: {
+ type: "button",
+ style: `background-color: ${color}`,
+ "aria-label": `${I18n.t(
+ "settings.experimental.tagColors.applyHint"
+ )} ${index + 1}`
+ }
+ });
+ swatch.addEventListener("click", () => onSelect(index));
+ });
+ }
+ renderTagColorAssignments(listEl, onChange) {
+ listEl.empty();
+ const entries = Object.entries(this.plugin.settings.tagColors || {});
+ if (!entries.length) {
+ listEl.createDiv({
+ cls: "tag-color-setting__empty",
+ text: I18n.t("settings.experimental.tagColors.empty")
+ });
+ onChange == null ? void 0 : onChange();
+ return;
+ }
+ entries.sort(([tagA], [tagB]) => tagA.localeCompare(tagB)).forEach(([tag]) => {
+ const chipWrapper = listEl.createDiv({
+ cls: "tag-color-setting__chip"
+ });
+ const chip = chipWrapper.createEl("a", {
+ cls: "tag",
+ text: `#${tag}`,
+ attr: {
+ href: `#${tag}`
+ }
+ });
+ chip.addEventListener("click", (event) => {
+ var _a;
+ event.preventDefault();
+ const tagInputEl = (_a = listEl.closest(".tag-color-setting")) == null ? void 0 : _a.querySelector(
+ ".tag-color-setting__input input"
+ );
+ if (tagInputEl) {
+ tagInputEl.value = `#${tag}`;
+ tagInputEl.dispatchEvent(new Event("input"));
+ }
+ });
+ const removeButton = chipWrapper.createEl("button", {
+ cls: "tag-color-setting__chip-remove",
+ attr: {
+ type: "button",
+ "aria-label": I18n.t(
+ "settings.experimental.tagColors.clear"
+ ),
+ title: I18n.t("settings.experimental.tagColors.clear")
+ },
+ text: "\u2715"
+ });
+ removeButton.addEventListener("click", async (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ delete this.plugin.settings.tagColors[tag];
+ await this.plugin.saveSettings();
+ this.renderTagColorAssignments(listEl, onChange);
+ });
+ });
+ onChange == null ? void 0 : onChange();
+ }
+ populateTagOptions(datalist) {
+ var _a, _b;
+ datalist.empty();
+ const knownTags = new Set(
+ Object.keys(this.plugin.settings.knownTags || {})
+ );
+ const assignedTags = new Set(
+ Object.keys(this.plugin.settings.tagColors || {})
+ );
+ const metadataTags = Object.keys(
+ ((_b = (_a = this.app.metadataCache) == null ? void 0 : _a.getTags) == null ? void 0 : _b.call(_a)) || {}
+ ).map((tag) => normalizeTagName(tag)).filter((tag) => tag.length > 0);
+ metadataTags.forEach((tag) => knownTags.add(tag));
+ Array.from(knownTags).filter((tag) => !assignedTags.has(tag)).sort((a2, b2) => a2.localeCompare(b2)).forEach((tag) => {
+ datalist.createEl("option", {
+ attr: { value: `#${tag}` }
+ });
+ });
+ }
+ getActivePalette() {
+ let palette = this.plugin.palettes.light;
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+ palette = this.plugin.palettes.dark;
+ }
+ return palette.length ? palette : this.plugin.palettes.light;
+ }
+ subscribeToPaletteChange(callback) {
+ this.paletteChangeSubscribers.push(callback);
+ }
+ notifyPaletteChange() {
+ this.paletteChangeSubscribers.forEach((cb) => cb());
+ }
+};
+
+// node_modules/colorjs.io/dist/color.js
+function multiplyMatrices(A, B) {
+ let m3 = A.length;
+ if (!Array.isArray(A[0])) {
+ A = [A];
+ }
+ if (!Array.isArray(B[0])) {
+ B = B.map((x) => [x]);
+ }
+ let p2 = B[0].length;
+ let B_cols = B[0].map((_, i) => B.map((x) => x[i]));
+ let product = A.map((row) => B_cols.map((col) => {
+ let ret = 0;
+ if (!Array.isArray(row)) {
+ for (let c4 of col) {
+ ret += row * c4;
+ }
+ return ret;
+ }
+ for (let i = 0; i < row.length; i++) {
+ ret += row[i] * (col[i] || 0);
+ }
+ return ret;
+ }));
+ if (m3 === 1) {
+ product = product[0];
+ }
+ if (p2 === 1) {
+ return product.map((x) => x[0]);
+ }
+ return product;
+}
+function isString(str) {
+ return type(str) === "string";
+}
+function type(o) {
+ let str = Object.prototype.toString.call(o);
+ return (str.match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();
+}
+function toPrecision(n2, precision) {
+ n2 = +n2;
+ precision = +precision;
+ let integerLength = (Math.floor(n2) + "").length;
+ if (precision > integerLength) {
+ return +n2.toFixed(precision - integerLength);
+ } else {
+ let p10 = 10 ** (integerLength - precision);
+ return Math.round(n2 / p10) * p10;
+ }
+}
+function parseFunction(str) {
+ if (!str) {
+ return;
+ }
+ str = str.trim();
+ const isFunctionRegex = /^([a-z]+)\((.+?)\)$/i;
+ const isNumberRegex = /^-?[\d.]+$/;
+ let parts = str.match(isFunctionRegex);
+ if (parts) {
+ let args = [];
+ parts[2].replace(/\/?\s*([-\w.]+(?:%|deg)?)/g, ($0, arg) => {
+ if (/%$/.test(arg)) {
+ arg = new Number(arg.slice(0, -1) / 100);
+ arg.type = " blocks inside this element; for each one, look for things of the form `= ...`.
+ let codeblocks = el.querySelectorAll("code");
+ for (let index = 0; index < codeblocks.length; index++) {
+ let codeblock = codeblocks.item(index);
+ // Skip code inside of pre elements if not explicitly enabled.
+ if (codeblock.parentElement &&
+ codeblock.parentElement.nodeName.toLowerCase() == "pre" &&
+ !this.settings.inlineQueriesInCodeblocks)
+ continue;
+ let text = codeblock.innerText.trim();
+ if (this.settings.inlineJsQueryPrefix.length > 0 && text.startsWith(this.settings.inlineJsQueryPrefix)) {
+ let code = text.substring(this.settings.inlineJsQueryPrefix.length).trim();
+ if (code.length == 0)
+ continue;
+ component.addChild(new DataviewInlineJSRenderer(this.api, code, el, codeblock, sourcePath));
+ }
+ else if (this.settings.inlineQueryPrefix.length > 0 && text.startsWith(this.settings.inlineQueryPrefix)) {
+ let potentialField = text.substring(this.settings.inlineQueryPrefix.length).trim();
+ if (potentialField.length == 0)
+ continue;
+ let field = tryOrPropagate(() => parseField(potentialField));
+ if (!field.successful) {
+ let errorBlock = el.createEl("div");
+ renderErrorPre(errorBlock, `Dataview (inline field '${potentialField}'): ${field.error}`);
+ }
+ else {
+ let fieldValue = field.value;
+ component.addChild(new DataviewInlineRenderer(fieldValue, text, el, codeblock, this.index, sourcePath, this.settings, this.app));
+ }
+ }
+ }
+ }
+ /** Update plugin settings. */
+ async updateSettings(settings) {
+ Object.assign(this.settings, settings);
+ this.updateRefreshSettings();
+ await this.saveData(this.settings);
+ }
+ /** @deprecated Call the given callback when the dataview API has initialized. */
+ withApi(callback) {
+ callback(this.api);
+ }
+ /**
+ * Create an API element localized to the given path, with lifecycle management managed by the given component.
+ * The API will output results to the given HTML element.
+ */
+ localApi(path, component, el) {
+ return new DataviewInlineApi(this.api, component, el, path);
+ }
+}
+/** All of the dataview settings in a single, nice tab. */
+class GeneralSettingsTab extends obsidian.PluginSettingTab {
+ plugin;
+ constructor(app, plugin) {
+ super(app, plugin);
+ this.plugin = plugin;
+ }
+ display() {
+ this.containerEl.empty();
+ new obsidian.Setting(this.containerEl)
+ .setName("Enable inline queries")
+ .setDesc("Enable or disable executing regular inline Dataview queries.")
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.enableInlineDataview)
+ .onChange(async (value) => await this.plugin.updateSettings({ enableInlineDataview: value })));
+ new obsidian.Setting(this.containerEl)
+ .setName("Enable JavaScript queries")
+ .setDesc("Enable or disable executing DataviewJS queries.")
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.enableDataviewJs)
+ .onChange(async (value) => await this.plugin.updateSettings({ enableDataviewJs: value })));
+ new obsidian.Setting(this.containerEl)
+ .setName("Enable inline JavaScript queries")
+ .setDesc("Enable or disable executing inline DataviewJS queries. Requires that DataviewJS queries are enabled.")
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.enableInlineDataviewJs)
+ .onChange(async (value) => await this.plugin.updateSettings({ enableInlineDataviewJs: value })));
+ new obsidian.Setting(this.containerEl)
+ .setName("Enable inline field highlighting in reading view")
+ .setDesc("Enables or disables visual highlighting / pretty rendering for inline fields in reading view.")
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.prettyRenderInlineFields)
+ .onChange(async (value) => await this.plugin.updateSettings({ prettyRenderInlineFields: value })));
+ new obsidian.Setting(this.containerEl)
+ .setName("Enable inline field highlighting in Live Preview")
+ .setDesc("Enables or disables visual highlighting / pretty rendering for inline fields in Live Preview.")
+ .addToggle(toggle => toggle.setValue(this.plugin.settings.prettyRenderInlineFieldsInLivePreview).onChange(async (value) => {
+ await this.plugin.updateSettings({ prettyRenderInlineFieldsInLivePreview: value });
+ this.plugin.updateEditorExtensions();
+ }));
+ new obsidian.Setting(this.containerEl).setName("Codeblocks").setHeading();
+ new obsidian.Setting(this.containerEl)
+ .setName("DataviewJS keyword")
+ .setDesc("Keyword for DataviewJS blocks. Defaults to 'dataviewjs'. Reload required for changes to take effect.")
+ .addText(text => text
+ .setPlaceholder("dataviewjs")
+ .setValue(this.plugin.settings.dataviewJsKeyword)
+ .onChange(async (value) => {
+ if (value.length == 0)
+ return;
+ this.plugin.unregisterDataviewjsCodeHighlighting();
+ await this.plugin.updateSettings({ dataviewJsKeyword: value });
+ this.plugin.registerDataviewjsCodeHighlighting();
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Inline query prefix")
+ .setDesc("The prefix to inline queries (to mark them as Dataview queries). Defaults to '='.")
+ .addText(text => text
+ .setPlaceholder("=")
+ .setValue(this.plugin.settings.inlineQueryPrefix)
+ .onChange(async (value) => {
+ if (value.length == 0)
+ return;
+ await this.plugin.updateSettings({ inlineQueryPrefix: value });
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("JavaScript inline query prefix")
+ .setDesc("The prefix to JavaScript inline queries (to mark them as DataviewJS queries). Defaults to '$='.")
+ .addText(text => text
+ .setPlaceholder("$=")
+ .setValue(this.plugin.settings.inlineJsQueryPrefix)
+ .onChange(async (value) => {
+ if (value.length == 0)
+ return;
+ await this.plugin.updateSettings({ inlineJsQueryPrefix: value });
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Code block inline queries")
+ .setDesc("If enabled, inline queries will also be evaluated inside full code blocks.")
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.inlineQueriesInCodeblocks)
+ .onChange(async (value) => await this.plugin.updateSettings({ inlineQueriesInCodeblocks: value })));
+ new obsidian.Setting(this.containerEl).setName("View").setHeading();
+ new obsidian.Setting(this.containerEl)
+ .setName("Display result count")
+ .setDesc("If toggled off, the small number in the result header of TASK and TABLE queries will be hidden.")
+ .addToggle(toggle => toggle.setValue(this.plugin.settings.showResultCount).onChange(async (value) => {
+ await this.plugin.updateSettings({ showResultCount: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Warn on empty result")
+ .setDesc("If set, queries which return 0 results will render a warning message.")
+ .addToggle(toggle => toggle.setValue(this.plugin.settings.warnOnEmptyResult).onChange(async (value) => {
+ await this.plugin.updateSettings({ warnOnEmptyResult: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Render null as")
+ .setDesc("What null/non-existent should show up as in tables, by default. This supports Markdown notation.")
+ .addText(text => text
+ .setPlaceholder("-")
+ .setValue(this.plugin.settings.renderNullAs)
+ .onChange(async (value) => {
+ await this.plugin.updateSettings({ renderNullAs: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Automatic view refreshing")
+ .setDesc("If enabled, views will automatically refresh when files in your vault change; this can negatively affect" +
+ " some functionality like embeds in views, so turn it off if such functionality is not working.")
+ .addToggle(toggle => toggle.setValue(this.plugin.settings.refreshEnabled).onChange(async (value) => {
+ await this.plugin.updateSettings({ refreshEnabled: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Refresh interval")
+ .setDesc("How long to wait (in milliseconds) for files to stop changing before updating views.")
+ .addText(text => text
+ .setPlaceholder("500")
+ .setValue("" + this.plugin.settings.refreshInterval)
+ .onChange(async (value) => {
+ let parsed = parseInt(value);
+ if (isNaN(parsed))
+ return;
+ parsed = parsed < 100 ? 100 : parsed;
+ await this.plugin.updateSettings({ refreshInterval: parsed });
+ }));
+ let dformat = new obsidian.Setting(this.containerEl)
+ .setName("Date format")
+ .setDesc("The default date format (see Luxon date format options)." +
+ " Currently: " +
+ DateTime.now().toFormat(this.plugin.settings.defaultDateFormat, { locale: currentLocale() }))
+ .addText(text => text
+ .setPlaceholder(DEFAULT_QUERY_SETTINGS.defaultDateFormat)
+ .setValue(this.plugin.settings.defaultDateFormat)
+ .onChange(async (value) => {
+ dformat.setDesc("The default date format (see Luxon date format options)." +
+ " Currently: " +
+ DateTime.now().toFormat(value, { locale: currentLocale() }));
+ await this.plugin.updateSettings({ defaultDateFormat: value });
+ this.plugin.index.touch();
+ }));
+ let dtformat = new obsidian.Setting(this.containerEl)
+ .setName("Date + time format")
+ .setDesc("The default date and time format (see Luxon date format options)." +
+ " Currently: " +
+ DateTime.now().toFormat(this.plugin.settings.defaultDateTimeFormat, { locale: currentLocale() }))
+ .addText(text => text
+ .setPlaceholder(DEFAULT_QUERY_SETTINGS.defaultDateTimeFormat)
+ .setValue(this.plugin.settings.defaultDateTimeFormat)
+ .onChange(async (value) => {
+ dtformat.setDesc("The default date and time format (see Luxon date format options)." +
+ " Currently: " +
+ DateTime.now().toFormat(value, { locale: currentLocale() }));
+ await this.plugin.updateSettings({ defaultDateTimeFormat: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl).setName("Tables").setHeading();
+ new obsidian.Setting(this.containerEl)
+ .setName("Primary column name")
+ .setDesc("The name of the default ID column in tables; this is the auto-generated first column that links to the source file.")
+ .addText(text => text
+ .setPlaceholder("File")
+ .setValue(this.plugin.settings.tableIdColumnName)
+ .onChange(async (value) => {
+ await this.plugin.updateSettings({ tableIdColumnName: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl)
+ .setName("Grouped column name")
+ .setDesc("The name of the default ID column in tables, when the table is on grouped data; this is the auto-generated first column" +
+ "that links to the source file/group.")
+ .addText(text => text
+ .setPlaceholder("Group")
+ .setValue(this.plugin.settings.tableGroupColumnName)
+ .onChange(async (value) => {
+ await this.plugin.updateSettings({ tableGroupColumnName: value });
+ this.plugin.index.touch();
+ }));
+ new obsidian.Setting(this.containerEl).setName("Tasks").setHeading();
+ let taskCompletionSubsettingsEnabled = this.plugin.settings.taskCompletionTracking;
+ let taskCompletionInlineSubsettingsEnabled = taskCompletionSubsettingsEnabled && !this.plugin.settings.taskCompletionUseEmojiShorthand;
+ new obsidian.Setting(this.containerEl)
+ .setName("Automatic task completion tracking")
+ .setDesc(createFragment(el => {
+ el.appendText("If enabled, Dataview will automatically append tasks with their completion date when they are checked in Dataview views.");
+ el.createEl("br");
+ el.appendText("Example with default field name and date format: - [x] my task [completion:: 2022-01-01]");
+ }))
+ .addToggle(toggle => toggle.setValue(this.plugin.settings.taskCompletionTracking).onChange(async (value) => {
+ await this.plugin.updateSettings({ taskCompletionTracking: value });
+ taskCompletionSubsettingsEnabled = value;
+ this.display();
+ }));
+ let taskEmojiShorthand = new obsidian.Setting(this.containerEl)
+ .setName("Use emoji shorthand for completion")
+ .setDisabled(!taskCompletionSubsettingsEnabled);
+ if (taskCompletionSubsettingsEnabled)
+ taskEmojiShorthand
+ .setDesc(createFragment(el => {
+ el.appendText('If enabled, will use emoji shorthand instead of inline field formatting to fill out implicit task field "completion".');
+ el.createEl("br");
+ el.appendText("Example: - [x] my task ✅ 2022-01-01");
+ el.createEl("br");
+ el.appendText("Disable this to customize the completion date format or field name, or to use Dataview inline field formatting.");
+ el.createEl("br");
+ el.appendText('Only available when "automatic task completion tracking" is enabled.');
+ }))
+ .addToggle(toggle => toggle.setValue(this.plugin.settings.taskCompletionUseEmojiShorthand).onChange(async (value) => {
+ await this.plugin.updateSettings({ taskCompletionUseEmojiShorthand: value });
+ taskCompletionInlineSubsettingsEnabled = taskCompletionSubsettingsEnabled && !value;
+ this.display();
+ }));
+ else
+ taskEmojiShorthand.setDesc('Only available when "automatic task completion tracking" is enabled.');
+ let taskFieldName = new obsidian.Setting(this.containerEl)
+ .setName("Completion field name")
+ .setDisabled(!taskCompletionInlineSubsettingsEnabled);
+ if (taskCompletionInlineSubsettingsEnabled)
+ taskFieldName
+ .setDesc(createFragment(el => {
+ el.appendText("Text used as inline field key for task completion date when toggling a task's checkbox in a Dataview view.");
+ el.createEl("br");
+ el.appendText('Only available when "automatic task completion tracking" is enabled and "use emoji shorthand for completion" is disabled.');
+ }))
+ .addText(text => text.setValue(this.plugin.settings.taskCompletionText).onChange(async (value) => {
+ await this.plugin.updateSettings({ taskCompletionText: value.trim() });
+ }));
+ else
+ taskFieldName.setDesc('Only available when "automatic task completion tracking" is enabled and "use emoji shorthand for completion" is disabled.');
+ let taskDtFormat = new obsidian.Setting(this.containerEl)
+ .setName("Completion date format")
+ .setDisabled(!taskCompletionInlineSubsettingsEnabled);
+ if (taskCompletionInlineSubsettingsEnabled) {
+ let descTextLines = [
+ "Date-time format for task completion date when toggling a task's checkbox in a Dataview view (see Luxon date format options).",
+ 'Only available when "automatic task completion tracking" is enabled and "use emoji shorthand for completion" is disabled.',
+ "Currently: ",
+ ];
+ taskDtFormat
+ .setDesc(createFragment(el => {
+ el.appendText(descTextLines[0]);
+ el.createEl("br");
+ el.appendText(descTextLines[1]);
+ el.createEl("br");
+ el.appendText(descTextLines[2] +
+ DateTime.now().toFormat(this.plugin.settings.taskCompletionDateFormat, {
+ locale: currentLocale(),
+ }));
+ }))
+ .addText(text => text
+ .setPlaceholder(DEFAULT_SETTINGS.taskCompletionDateFormat)
+ .setValue(this.plugin.settings.taskCompletionDateFormat)
+ .onChange(async (value) => {
+ taskDtFormat.setDesc(createFragment(el => {
+ el.appendText(descTextLines[0]);
+ el.createEl("br");
+ el.appendText(descTextLines[1]);
+ el.createEl("br");
+ el.appendText(descTextLines[2] +
+ DateTime.now().toFormat(value.trim(), { locale: currentLocale() }));
+ }));
+ await this.plugin.updateSettings({ taskCompletionDateFormat: value.trim() });
+ this.plugin.index.touch();
+ }));
+ }
+ else {
+ taskDtFormat.setDesc('Only available when "automatic task completion tracking" is enabled and "use emoji shorthand for completion" is disabled.');
+ }
+ new obsidian.Setting(this.containerEl)
+ .setName("Recursive sub-task completion")
+ // I gotta word this better :/
+ .setDesc("If enabled, completing a task in a Dataview will automatically complete its subtasks too.")
+ .addToggle(toggle => toggle
+ .setValue(this.plugin.settings.recursiveSubTaskCompletion)
+ .onChange(async (value) => await this.plugin.updateSettings({ recursiveSubTaskCompletion: value })));
+ }
+}
+
+module.exports = DataviewPlugin;
+
+
+/* nosourcemap */
\ No newline at end of file
diff --git a/.stversions/liph/.obsidian/plugins/dataview/manifest.json b/.stversions/liph/.obsidian/plugins/dataview/manifest.json
new file mode 100755
index 0000000..926b2a6
--- /dev/null
+++ b/.stversions/liph/.obsidian/plugins/dataview/manifest.json
@@ -0,0 +1,11 @@
+{
+ "id": "dataview",
+ "name": "Dataview",
+ "version": "0.5.68",
+ "minAppVersion": "0.13.11",
+ "description": "Complex data views for the data-obsessed.",
+ "author": "Michael Brenan ",
+ "authorUrl": "https://github.com/blacksmithgu",
+ "helpUrl": "https://blacksmithgu.github.io/obsidian-dataview/",
+ "isDesktopOnly": false
+}
diff --git a/.stversions/liph/.obsidian/plugins/dataview/styles.css b/.stversions/liph/.obsidian/plugins/dataview/styles.css
new file mode 100755
index 0000000..618821a
--- /dev/null
+++ b/.stversions/liph/.obsidian/plugins/dataview/styles.css
@@ -0,0 +1,141 @@
+.block-language-dataview {
+ overflow-y: auto;
+}
+
+/*****************/
+/** Table Views **/
+/*****************/
+
+/* List View Default Styling; rendered internally as a table. */
+.table-view-table {
+ width: 100%;
+}
+
+.table-view-table > thead > tr, .table-view-table > tbody > tr {
+ margin-top: 1em;
+ margin-bottom: 1em;
+ text-align: left;
+}
+
+.table-view-table > tbody > tr:hover {
+ background-color: var(--table-row-background-hover);
+}
+
+.table-view-table > thead > tr > th {
+ font-weight: 700;
+ font-size: larger;
+ border-top: none;
+ border-left: none;
+ border-right: none;
+ border-bottom: solid;
+
+ max-width: 100%;
+}
+
+.table-view-table > tbody > tr > td {
+ text-align: left;
+ border: none;
+ font-weight: 400;
+ max-width: 100%;
+}
+
+.table-view-table ul, .table-view-table ol {
+ margin-block-start: 0.2em !important;
+ margin-block-end: 0.2em !important;
+}
+
+/** Rendered value styling for any view. */
+.dataview-result-list-root-ul {
+ padding: 0em !important;
+ margin: 0em !important;
+}
+
+.dataview-result-list-ul {
+ margin-block-start: 0.2em !important;
+ margin-block-end: 0.2em !important;
+}
+
+/** Generic grouping styling. */
+.dataview.result-group {
+ padding-left: 8px;
+}
+
+/*******************/
+/** Inline Fields **/
+/*******************/
+
+.dataview.inline-field-key {
+ padding-left: 8px;
+ padding-right: 8px;
+ font-family: var(--font-monospace);
+ background-color: var(--background-primary-alt);
+ color: var(--nav-item-color-selected);
+}
+
+.dataview.inline-field-value {
+ padding-left: 8px;
+ padding-right: 8px;
+ font-family: var(--font-monospace);
+ background-color: var(--background-secondary-alt);
+ color: var(--nav-item-color-selected);
+}
+
+.dataview.inline-field-standalone-value {
+ padding-left: 8px;
+ padding-right: 8px;
+ font-family: var(--font-monospace);
+ background-color: var(--background-secondary-alt);
+ color: var(--nav-item-color-selected);
+}
+
+/***************/
+/** Task View **/
+/***************/
+
+.dataview.task-list-item, .dataview.task-list-basic-item {
+ margin-top: 3px;
+ margin-bottom: 3px;
+ transition: 0.4s;
+}
+
+.dataview.task-list-item:hover, .dataview.task-list-basic-item:hover {
+ background-color: var(--text-selection);
+ box-shadow: -40px 0 0 var(--text-selection);
+ cursor: pointer;
+}
+
+/*****************/
+/** Error Views **/
+/*****************/
+
+div.dataview-error-box {
+ width: 100%;
+ min-height: 150px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 4px dashed var(--background-secondary);
+}
+
+.dataview-error-message {
+ color: var(--text-muted);
+ text-align: center;
+}
+
+/*************************/
+/** Additional Metadata **/
+/*************************/
+
+.dataview.small-text {
+ font-size: smaller;
+ color: var(--text-muted);
+ margin-left: 3px;
+}
+
+.dataview.small-text::before {
+ content: "(";
+}
+
+.dataview.small-text::after {
+ content: ")";
+}
diff --git a/.stversions/liph/.obsidian/plugins/editing-toolbar/data.json b/.stversions/liph/.obsidian/plugins/editing-toolbar/data.json
new file mode 100755
index 0000000..be79bc7
--- /dev/null
+++ b/.stversions/liph/.obsidian/plugins/editing-toolbar/data.json
@@ -0,0 +1,535 @@
+{
+ "lastVersion": "4.0.8",
+ "aestheticStyle": "default",
+ "positionStyle": "top",
+ "menuCommands": [
+ {
+ "id": "editing-toolbar:editor-undo",
+ "name": "Undo Edit",
+ "icon": "undo-glyph"
+ },
+ {
+ "id": "editing-toolbar:editor-redo",
+ "name": "Redo Edit",
+ "icon": "redo-glyph"
+ },
+ {
+ "id": "editing-toolbar:toggle-format-brush",
+ "name": "Format Brush",
+ "icon": "paintbrush"
+ },
+ {
+ "id": "editing-toolbar:format-eraser",
+ "name": "Clear Text Formatting",
+ "icon": "eraser"
+ },
+ {
+ "id": "editing-toolbar:header2-text",
+ "name": "Header 2",
+ "icon": "header-2"
+ },
+ {
+ "id": "editing-toolbar:header3-text",
+ "name": "Header 3",
+ "icon": "header-3"
+ },
+ {
+ "id": "SubmenuCommands-header",
+ "name": "submenu",
+ "icon": "header-n",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:header1-text",
+ "name": "Header 1",
+ "icon": "header-1"
+ },
+ {
+ "id": "editing-toolbar:header4-text",
+ "name": "Header 4",
+ "icon": "header-4"
+ },
+ {
+ "id": "editing-toolbar:header5-text",
+ "name": "Header 5",
+ "icon": "header-5"
+ },
+ {
+ "id": "editing-toolbar:header6-text",
+ "name": "Header 6",
+ "icon": "header-6"
+ }
+ ]
+ },
+ {
+ "id": "editing-toolbar:toggle-bold",
+ "name": "Bold",
+ "icon": "bold-glyph"
+ },
+ {
+ "id": "editing-toolbar:toggle-italics",
+ "name": "Italic",
+ "icon": "italic-glyph"
+ },
+ {
+ "id": "editing-toolbar:toggle-strikethrough",
+ "name": "Strikethrough",
+ "icon": "strikethrough-glyph"
+ },
+ {
+ "id": "editing-toolbar:underline",
+ "name": "Underline",
+ "icon": "underline-glyph"
+ },
+ {
+ "id": "editing-toolbar:toggle-highlight",
+ "name": "Highlight",
+ "icon": "highlight-glyph"
+ },
+ {
+ "id": "SubmenuCommands-text-tools",
+ "name": "Text Tools",
+ "icon": "box",
+ "menuType": "dropdown",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:get-plain-text",
+ "name": "Get Plain Text",
+ "icon": "lucide-file-text"
+ },
+ {
+ "id": "editing-toolbar:smart-symbols",
+ "name": "Full Half Converter",
+ "icon": "lucide-at-sign"
+ },
+ {
+ "id": "editingToolbar-Divider-Line",
+ "name": "Line Operations",
+ "icon": "vertical-split"
+ },
+ {
+ "id": "editing-toolbar:insert-blank-lines",
+ "name": "Insert Blank Lines",
+ "icon": "lucide-space"
+ },
+ {
+ "id": "editing-toolbar:remove-blank-lines",
+ "name": "Remove Blank Lines",
+ "icon": "lucide-minimize-2"
+ },
+ {
+ "id": "editing-toolbar:split-lines",
+ "name": "Split Lines",
+ "icon": "lucide-split"
+ },
+ {
+ "id": "editing-toolbar:merge-lines",
+ "name": "Merge Lines",
+ "icon": "lucide-merge"
+ },
+ {
+ "id": "editing-toolbar:dedupe-lines",
+ "name": "Dedupe Lines",
+ "icon": "lucide-filter"
+ },
+ {
+ "id": "editingToolbar-Divider-Line",
+ "name": "Text Processing",
+ "icon": "vertical-split"
+ },
+ {
+ "id": "editing-toolbar:add-wrap",
+ "name": "Add Prefix/Suffix",
+ "icon": "lucide-wrap-text"
+ },
+ {
+ "id": "editing-toolbar:number-lines",
+ "name": "Number Lines (Custom)",
+ "icon": "lucide-list-ordered"
+ },
+ {
+ "id": "editing-toolbar:remove-whitespace-trim",
+ "name": "Trim Line Ends",
+ "icon": "lucide-scissors"
+ },
+ {
+ "id": "editing-toolbar:remove-whitespace-compress",
+ "name": "Shrink Extra Spaces",
+ "icon": "lucide-minimize"
+ },
+ {
+ "id": "editing-toolbar:remove-whitespace-all",
+ "name": "Remove All Whitespace",
+ "icon": "lucide-eraser"
+ },
+ {
+ "id": "editingToolbar-Divider-Line",
+ "name": "Advanced Tools",
+ "icon": "vertical-split"
+ },
+ {
+ "id": "editing-toolbar:list-to-table",
+ "name": "List to Table",
+ "icon": "lucide-table"
+ },
+ {
+ "id": "editing-toolbar:table-to-list",
+ "name": "Table to List",
+ "icon": "lucide-list"
+ },
+ {
+ "id": "editing-toolbar:extract-between",
+ "name": "Extract Between Strings",
+ "icon": "lucide-brackets"
+ }
+ ]
+ },
+ {
+ "id": "SubmenuCommands-lucdf3en5",
+ "name": "submenu",
+ "icon": "edit",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:editor-cut",
+ "name": "Cut",
+ "icon": "lucide-scissors"
+ },
+ {
+ "id": "editing-toolbar:editor-copy",
+ "name": "Copy",
+ "icon": "lucide-copy"
+ },
+ {
+ "id": "editing-toolbar:editor-paste",
+ "name": "Paste",
+ "icon": "lucide-clipboard-type"
+ },
+ {
+ "id": "editing-toolbar:editor:swap-line-down",
+ "name": "Swap Line Down",
+ "icon": "lucide-corner-right-down"
+ },
+ {
+ "id": "editing-toolbar:editor:swap-line-up",
+ "name": "Swap Line Up",
+ "icon": "lucide-corner-right-up"
+ }
+ ]
+ },
+ {
+ "id": "editing-toolbar:editor:attach-file",
+ "name": "Attach File",
+ "icon": "lucide-paperclip"
+ },
+ {
+ "id": "editing-toolbar:editor:insert-table",
+ "name": "Insert Table",
+ "icon": "lucide-table"
+ },
+ {
+ "id": "editing-toolbar:editor:cycle-list-checklist",
+ "name": "Cycle List and Checklist",
+ "icon": "check-circle"
+ },
+ {
+ "id": "SubmenuCommands-luc8efull",
+ "name": "submenu",
+ "icon": "message-square",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:editor:toggle-blockquote",
+ "name": "Blockquote",
+ "icon": "lucide-text-quote"
+ },
+ {
+ "id": "editing-toolbar:insert-callout",
+ "name": "Callout",
+ "icon": "lucide-quote"
+ }
+ ]
+ },
+ {
+ "id": "SubmenuCommands-mdcmder",
+ "name": "submenu",
+ "icon": "",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:superscript",
+ "name": "Superscript",
+ "icon": "superscript-glyph"
+ },
+ {
+ "id": "editing-toolbar:subscript",
+ "name": "Subscript",
+ "icon": "subscript-glyph"
+ },
+ {
+ "id": "editing-toolbar:editor:toggle-code",
+ "name": "Inline Code",
+ "icon": "code-glyph"
+ },
+ {
+ "id": "editing-toolbar:codeblock",
+ "name": "Code Block",
+ "icon": "codeblock-glyph"
+ },
+ {
+ "id": "editing-toolbar:editor:insert-wikilink",
+ "name": "Wikilink",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:editor:insert-embed",
+ "name": "Embed",
+ "icon": "note-glyph"
+ },
+ {
+ "id": "editing-toolbar:insert-link",
+ "name": "Link",
+ "icon": "link-glyph"
+ },
+ {
+ "id": "editing-toolbar:hrline",
+ "name": "Horizontal Divider",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:toggle-inline-math",
+ "name": "Inline Math",
+ "icon": "lucide-sigma"
+ },
+ {
+ "id": "editing-toolbar:editor:insert-mathblock",
+ "name": "MathBlock",
+ "icon": "lucide-sigma-square"
+ }
+ ]
+ },
+ {
+ "id": "SubmenuCommands-list",
+ "name": "submenu-list",
+ "icon": "bullet-list-glyph",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:editor:toggle-checklist-status",
+ "name": "Checklist",
+ "icon": "checkbox-glyph"
+ },
+ {
+ "id": "editing-toolbar:renumber-ordered-list",
+ "name": "Renumber Ordered List",
+ "icon": "list-restart"
+ },
+ {
+ "id": "editing-toolbar:toggle-numbered-list",
+ "name": "Ordered List",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:toggle-bullet-list",
+ "name": "Unordered List",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:undent-list",
+ "name": "Unindent List",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:indent-list",
+ "name": "Indent list",
+ "icon": ""
+ }
+ ]
+ },
+ {
+ "id": "SubmenuCommands-aligin",
+ "name": "submenu-aligin",
+ "icon": "",
+ "SubmenuCommands": [
+ {
+ "id": "editing-toolbar:justify",
+ "name": "Justify Text",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:left",
+ "name": "Align Text Left",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:center",
+ "name": "Center Text",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:right",
+ "name": "Align Text Right",
+ "icon": ""
+ }
+ ]
+ },
+ {
+ "id": "editing-toolbar:change-font-color",
+ "name": "Change Font Color",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:change-background-color",
+ "name": "Change Background Color",
+ "icon": ""
+ },
+ {
+ "id": "editing-toolbar:fullscreen-focus",
+ "name": "Fullscreen Focus Mode",
+ "icon": "fullscreen"
+ },
+ {
+ "id": "editing-toolbar:workplace-fullscreen-focus",
+ "name": "Workplace Fullscreen",
+ "icon": "exit-fullscreen"
+ }
+ ],
+ "followingCommands": [],
+ "topCommands": [],
+ "fixedCommands": [],
+ "mobileCommands": [],
+ "enableMultipleConfig": false,
+ "enableTopToolbar": true,
+ "enableFollowingToolbar": false,
+ "enableFixedToolbar": false,
+ "appendMethod": "workspace",
+ "shouldShowMenuOnSelect": false,
+ "cMenuVisibility": true,
+ "cMenuBottomValue": 4.25,
+ "cMenuNumRows": 12,
+ "cMenuWidth": 610,
+ "cMenuFontColor": "#2DC26B",
+ "cMenuBackgroundColor": "#d3f8b6",
+ "autohide": false,
+ "Iscentered": false,
+ "custom_bg1": "#FFB78B8C",
+ "custom_bg2": "#CDF4698C",
+ "custom_bg3": "#A0CCF68C",
+ "custom_bg4": "#F0A7D88C",
+ "custom_bg5": "#ADEFEF8C",
+ "custom_fc1": "#D83931",
+ "custom_fc2": "#DE7802",
+ "custom_fc3": "#245BDB",
+ "custom_fc4": "#6425D0",
+ "custom_fc5": "#646A73",
+ "isLoadOnMobile": false,
+ "horizontalPosition": 0,
+ "verticalPosition": 0,
+ "formatBrushes": {},
+ "customCommands": [],
+ "viewTypeSettings": {},
+ "appearanceByStyle": {
+ "top": {
+ "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
+ "toolbarIconColor": "var(--text-normal)",
+ "toolbarIconSize": 18,
+ "aestheticStyle": "default"
+ },
+ "following": {
+ "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
+ "toolbarIconColor": "var(--text-normal)",
+ "toolbarIconSize": 18,
+ "aestheticStyle": "default"
+ },
+ "fixed": {
+ "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
+ "toolbarIconColor": "var(--text-normal)",
+ "toolbarIconSize": 18,
+ "aestheticStyle": "default"
+ },
+ "mobile": {
+ "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
+ "toolbarIconColor": "var(--text-normal)",
+ "toolbarIconSize": 18,
+ "aestheticStyle": "default"
+ }
+ },
+ "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
+ "toolbarIconColor": "var(--text-normal)",
+ "toolbarIconSize": 18,
+ "useCurrentLineForRegex": false,
+ "ai": {
+ "enabled": false,
+ "consentAccepted": false,
+ "onboardingShown": true,
+ "providerMode": "pkmer-first",
+ "enableInlineCompletion": true,
+ "inlineCompletionHintLearned": false,
+ "completionTrigger": "manual",
+ "completionDelay": 500,
+ "enableRewrite": true,
+ "showRewriteToolbarOnSelection": false,
+ "rewriteMinSelectionLength": 1,
+ "pkmerApiBaseUrl": "https://newapi.pkmer.cn",
+ "pkmerModel": "04-fast",
+ "pkmerModelRouting": {
+ "mode": "smart",
+ "completion": "04-fast",
+ "rewrite": "04-fast",
+ "reasoning": "03-agent",
+ "artifact": "03-agent"
+ },
+ "pkmer": {
+ "tokenExpiresAt": 0,
+ "userInfo": null
+ },
+ "enableCustomModel": false,
+ "customModel": {
+ "apiFormat": "openai-compatible",
+ "baseUrl": "",
+ "apiKey": "",
+ "model": "",
+ "temperature": 0.2
+ },
+ "customPromptHistory": [],
+ "customPromptTemplates": [
+ {
+ "id": "template-demo-variables",
+ "name": "📝 Summarize Key Points",
+ "prompt": "Analyze the structure of {{file:content}} and focus on:\n{{selection}}\n\nPlease provide:\n1. A concise overview\n2. The key points\n3. Suggestions for improvement",
+ "icon": "lucide-sparkles"
+ },
+ {
+ "id": "template-task-variables",
+ "name": "Extract Tasks",
+ "prompt": "Today's date is {{date}}. Extract all actionable tasks from {{file:content}}, with special attention to:\n{{selection}}\n\nPlease output strictly in Obsidian Tasks plugin format, one task per line:\n\n- [ ] Task description ⏫/🔼/🔽/⏬ priority\n- [ ] Task description 📅 YYYY-MM-DD due date\n- [ ] Task description ⏰ YYYY-MM-DD HH:mm reminder\n- [ ] Task description 🛫 YYYY-MM-DD start date\n- [ ] Task description 🔁 every day/week/month recurrence\n- [ ] Task description #tag #project\n\nExtraction rules:\n1. If the text implies urgency such as urgent, ASAP, immediately, or today, add ⏫.\n2. If it implies near-term timing such as tomorrow, this week, or soon, add 🔼.\n3. Convert explicit dates to 📅 YYYY-MM-DD.\n4. Convert explicit times to ⏰ YYYY-MM-DD HH:mm when possible.\n5. Add recurrence for repeated work such as 🔁 every week/month.\n6. Add practical tags for each task.\n\nExample output:\n- [ ] Finish project report ⏫ 📅 2026-04-25 #work\n- [ ] Weekly team sync 🔁 every week on Monday ⏰ 09:00 #meeting\n- [ ] Follow up on client request 🔼 📅 2026-04-23 #follow-up",
+ "icon": "lucide-sparkles"
+ },
+ {
+ "id": "template-dataview",
+ "name": "Generate Dataview",
+ "prompt": "Help me generate an Obsidian Dataview query block based on my requirement. Requirements:\n1. Use DataviewJS or DQL syntax.\n2. Include the necessary filters and sorting.\n3. Add short comments explaining each part.\n4. Use DataviewJS if the logic is complex.\n\nMy requirement:",
+ "icon": "lucide-database"
+ },
+ {
+ "id": "template-templater",
+ "name": "Design Templater Template",
+ "prompt": "Help me design an Obsidian Templater template. Requirements:\n1. Use Templater syntax (<% %>).\n2. Include dynamic date, time, and similar variables.\n3. Support user input prompts.\n4. Add necessary conditionals and loops.\n5. Comment the purpose of each section.\n\nTemplate purpose:",
+ "icon": "lucide-file-code"
+ },
+ {
+ "id": "template-mermaid",
+ "name": "Create Mermaid Diagram",
+ "prompt": "Based on my selected text {{selection}}, generate Mermaid diagram code. Requirements:\n1. Choose an appropriate diagram type such as flowchart, sequence, class, or gantt.\n2. Use clear node names.\n3. Add useful styling and comments when needed.\n4. Ensure the syntax is valid and renderable.\n",
+ "icon": "lucide-workflow"
+ },
+ {
+ "id": "template-metadata",
+ "name": "Design YAML",
+ "prompt": "Based on the current note content {{file:content}}, help me design a suitable YAML Frontmatter structure for this note. Requirements:\n1. Recommend fields that fit the note content.\n2. Include common fields such as tags, aliases, and date.\n3. Suggest useful custom fields.\n4. Briefly explain the purpose of each field.\n\nNote type:",
+ "icon": "lucide-file-json"
+ },
+ {
+ "id": "template-callout",
+ "name": "Wrap with Callout",
+ "prompt": "Based on my selected text {{selection}}, wrap it using an Obsidian Callout block. Requirements:\n1. Choose an appropriate callout type such as note, tip, warning, or danger.\n2. Support nesting and folding when helpful.\n3. Include a title and content.\n4. Allow code blocks or lists when needed.\n\nContent requirement:",
+ "icon": "lucide-message-square"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/.stversions/liph/.obsidian/plugins/editing-toolbar/main.js b/.stversions/liph/.obsidian/plugins/editing-toolbar/main.js
new file mode 100755
index 0000000..2db0963
--- /dev/null
+++ b/.stversions/liph/.obsidian/plugins/editing-toolbar/main.js
@@ -0,0 +1,12 @@
+"use strict";var e=require("obsidian"),t=require("@codemirror/language"),o=require("@codemirror/state"),n=require("@codemirror/view");const i=["Custom","editingToolbar","editingToolbarSub","editingToolbarAdd","editingToolbarDelete","editingToolbarReload","codeblock-glyph","underline-glyph","superscript-glyph","subscript-glyph","bot-glyph","header-1","header-2","header-3","header-4","header-5","header-6","header-n","obsidian","obsidian-new","accessibility","activity","air-vent","airplay","alarm-check","alarm-clock-off","alarm-clock","alarm-minus","alarm-plus","album","alert-circle","alert-octagon","alert-triangle","align-center-horizontal","align-center-vertical","align-center","align-end-horizontal","align-end-vertical","align-horizontal-distribute-center","align-horizontal-distribute-end","align-horizontal-distribute-start","align-horizontal-justify-center","align-horizontal-justify-end","align-horizontal-justify-start","align-horizontal-space-around","align-horizontal-space-between","align-justify","align-left","align-right","align-start-horizontal","align-start-vertical","align-vertical-distribute-center","align-vertical-distribute-end","align-vertical-distribute-start","align-vertical-justify-center","align-vertical-justify-end","align-vertical-justify-start","align-vertical-space-around","align-vertical-space-between","anchor","angry","annoyed","aperture","apple","archive-restore","archive","armchair","arrow-big-down","arrow-big-left","arrow-big-right","arrow-big-up","arrow-down-circle","arrow-down-left","arrow-down-right","arrow-down","arrow-left-circle","arrow-left-right","arrow-left","arrow-right-circle","arrow-right","arrow-up-circle","arrow-up-left","arrow-up-right","arrow-up","asterisk","at-sign","award","axe","axis-3d","baby","backpack","baggage-claim","banana","banknote","bar-chart-2","bar-chart-3","bar-chart-4","bar-chart-horizontal","bar-chart","baseline","bath","battery-charging","battery-full","battery-low","battery-medium","battery","beaker","bed-double","bed-single","bed","beer","bell-minus","bell-off","bell-plus","bell-ring","bell","bike","binary","bitcoin","bluetooth-connected","bluetooth-off","bluetooth-searching","bluetooth","bold","bomb","bone","book-open","book","bookmark-minus","bookmark-plus","bookmark","bot","box-select","box","boxes","briefcase","brush","bug","building-2","building","bus","cake","calculator","calendar-check-2","calendar-check","calendar-clock","calendar-days","calendar-heart","calendar-minus","calendar-off","calendar-plus","calendar-range","calendar-search","calendar-x2","calendar-x","calendar","camera-off","camera","car","carrot","cast","check-circle-2","check-circle","check-square","check","chef-hat","cherry","chevron-down","chevron-first","chevron-last","chevron-left","chevron-right","chevron-up","chevrons-down-up","chevrons-down","chevrons-left-right","chevrons-left","chevrons-right-left","chevrons-right","chevrons-up-down","chevrons-up","chrome","cigarette-off","cigarette","circle-dot","circle-ellipsis","circle-slashed","circle","citrus","clapperboard","clipboard-check","clipboard-copy","clipboard-edit","clipboard-list","clipboard-signature","clipboard-type","clipboard-x","clipboard","clock-1","clock-10","clock-11","clock-12","clock-2","clock-3","clock-4","clock-5","clock-6","clock-7","clock-8","clock-9","clock","cloud-cog","cloud-drizzle","cloud-fog","cloud-hail","cloud-lightning","cloud-moon-rain","cloud-moon","cloud-off","cloud-rain-wind","cloud-rain","cloud-snow","cloud-sun-rain","cloud-sun","cloud","cloudy","clover","code-2","code","codepen","codesandbox","coffee","cog","coins","columns","command","compass","component","contact","contrast","cookie","copy","copyleft","copyright","corner-down-left","corner-down-right","corner-left-down","corner-left-up","corner-right-down","corner-right-up","corner-up-left","corner-up-right","cpu","credit-card","croissant","crop","cross","crosshair","crown","cup-soda","curly-braces","currency","database","delete","diamond","dice-1","dice-2","dice-3","dice-4","dice-5","dice-6","dices","diff","disc","divide-circle","divide-square","divide","dollar-sign","download-cloud","download","dribbble","droplet","droplets","drumstick","edit-2","edit-3","edit","egg-fried","egg","equal-not","equal","eraser","euro","expand","external-link","eye-off","eye","facebook","factory","fast-forward","feather","figma","file-archive","file-audio-2","file-audio","file-axis-3d","file-badge-2","file-badge","file-bar-chart-2","file-bar-chart","file-box","file-check-2","file-check","file-clock","file-code","file-cog-2","file-cog","file-diff","file-digit","file-down","file-edit","file-heart","file-image","file-input","file-json-2","file-json","file-key-2","file-key","file-line-chart","file-lock-2","file-lock","file-minus-2","file-minus","file-output","file-pie-chart","file-plus-2","file-plus","file-question","file-scan","file-search-2","file-search","file-signature","file-spreadsheet","file-symlink","file-terminal","file-text","file-type-2","file-type","file-up","file-video-2","file-video","file-volume-2","file-volume","file-warning","file-x2","file-x","file","files","film","filter","fingerprint","flag-off","flag-triangle-left","flag-triangle-right","flag","flame","flashlight-off","flashlight","flask-conical","flask-round","flip-horizontal-2","flip-horizontal","flip-vertical-2","flip-vertical","flower-2","flower","focus","folder-archive","folder-check","folder-clock","folder-closed","folder-cog-2","folder-cog","folder-down","folder-edit","folder-heart","folder-input","folder-key","folder-lock","folder-minus","folder-open","folder-output","folder-plus","folder-search-2","folder-search","folder-symlink","folder-tree","folder-up","folder-x","folder","folders","form-input","forward","frame","framer","frown","fuel","function-square","gamepad-2","gamepad","gauge","gavel","gem","ghost","gift","git-branch-plus","git-branch","git-commit","git-compare","git-fork","git-merge","git-pull-request-closed","git-pull-request-draft","git-pull-request","github","gitlab","glass-water","glasses","globe-2","globe","grab","graduation-cap","grape","grid","grip-horizontal","grip-vertical","hammer","hand-metal","hand","hard-drive","hard-hat","hash","haze","headphones","heart-crack","heart-handshake","heart-off","heart-pulse","heart","help-circle","hexagon","highlighter","history","home","hourglass","ice-cream","image-minus","image-off","image-plus","image","import","inbox","indent","indian-rupee","infinity","info","inspect","instagram","italic","japanese-yen","joystick","key","keyboard","lamp-ceiling","lamp-desk","lamp-floor","lamp-wall-down","lamp-wall-up","lamp","landmark","languages","laptop-2","laptop","lasso-select","lasso","laugh","layers","layout-dashboard","layout-grid","layout-list","layout-template","layout","leaf","library","life-buoy","lightbulb-off","lightbulb","line-chart","link-2off","link-2","link","linkedin","list-checks","list-end","list-minus","list-music","list-ordered","list-plus","list-start","list-video","list-x","list","loader-2","loader","locate-fixed","locate-off","locate","lock","log-in","log-out","luggage","magnet","mail-check","mail-minus","mail-open","mail-plus","mail-question","mail-search","mail-warning","mail-x","mail","mails","map-pin-off","map-pin","map","martini","maximize-2","maximize","medal","megaphone-off","megaphone","meh","menu","message-circle","message-square","mic-2","mic-off","mic","microscope","milestone","minimize-2","minimize","minus-circle","minus-square","minus","monitor-off","monitor-speaker","monitor","moon","more-horizontal","more-vertical","mountain-snow","mountain","mouse-pointer-2","mouse-pointer-click","mouse-pointer","mouse","move-3d","move-diagonal-2","move-diagonal","move-horizontal","move-vertical","move","music-2","music-3","music-4","music","navigation-2off","navigation-2","navigation-off","navigation","network","newspaper","octagon","option","outdent","package-2","package-check","package-minus","package-open","package-plus","package-search","package-x","package","paint-bucket","paintbrush-2","paintbrush","palette","palmtree","paperclip","party-popper","pause-circle","pause-octagon","pause","pen-tool","pencil","percent","person-standing","phone-call","phone-forwarded","phone-incoming","phone-missed","phone-off","phone-outgoing","phone","pie-chart","piggy-bank","pin-off","pin","pipette","pizza","plane","play-circle","play","plug-zap","plus-circle","plus-square","plus","pocket","podcast","pointer","pound-sterling","power-off","power","printer","puzzle","qr-code","quote","radio-receiver","radio","recycle","redo-2","redo","refresh-ccw","refresh-cw","regex","repeat-1","repeat","reply-all","reply","rewind","rocket","rocking-chair","rotate-3d","rotate-ccw","rotate-cw","rss","ruler","russian-ruble","save","scale-3d","scale","scaling","scan-face","scan-line","scan","scissors","screen-share-off","screen-share","scroll","search","send","separator-horizontal","separator-vertical","server-cog","server-crash","server-off","server","settings-2","settings","share-2","share","sheet","shield-alert","shield-check","shield-close","shield-off","shield","shirt","shopping-bag","shopping-cart","shovel","shrink","shrub","shuffle","sidebar-close","sidebar-open","sidebar","sigma","signal-high","signal-low","signal-medium","signal-zero","signal","siren","skip-back","skip-forward","skull","slack","slash","slice","sliders-horizontal","sliders","smartphone-charging","smartphone","smile-plus","smile","snowflake","sofa","sort-asc","sort-desc","speaker","sprout","square","star-half","star-off","star","stethoscope","sticker","sticky-note","stop-circle","stretch-horizontal","stretch-vertical","strikethrough","subscript","sun-dim","sun-medium","sun-moon","sun-snow","sun","sunrise","sunset","superscript","swiss-franc","switch-camera","sword","swords","syringe","table-2","table","tablet","tag","tags","target","tent","terminal-square","terminal","text-cursor-input","text-cursor","thermometer-snowflake","thermometer-sun","thermometer","thumbs-down","thumbs-up","ticket","timer-off","timer-reset","timer","toggle-left","toggle-right","tornado","toy-brick","train","trash-2","trash","tree-deciduous","tree-pine","trees","trello","trending-down","trending-up","triangle","trophy","truck","tv-2","tv","twitch","twitter","type","umbrella","underline","undo-2","undo","unlink-2","unlink","unlock","upload-cloud","upload","usb","user-check","user-cog","user-minus","user-plus","user-x","user","users","utensils-crossed","utensils","venetian-mask","verified","vibrate-off","vibrate","video-off","video","view","voicemail","volume-1","volume-2","volume-x","volume","wallet","wand-2","wand","watch","waves","webcam","webhook","wifi-off","wifi","wind","wine","wrap-text","wrench","x-circle","x-octagon","x-square","x","youtube","zap-off","zap","zoom-in","zoom-out","create-new","trash","search","right-triangle","document","folder","pencil","left-arrow","right-arrow","three-horizontal-bars","dot-network","audio-file","image-file","pdf-file","gear","documents","blocks","go-to-file","presentation","cross-in-box","microphone","microphone-filled","two-columns","link","popup-open","checkmark","hashtag","left-arrow-with-tail","right-arrow-with-tail","up-arrow-with-tail","down-arrow-with-tail","lines-of-text","vertical-three-dots","pin","magnifying-glass","info","horizontal-split","vertical-split","calendar-with-checkmark","folder-minus","sheets-in-box","up-and-down-arrows","broken-link","cross","any-key","reset","star","crossed-star","dice","filled-pin","enter","help","vault","open-vault","paper-plane","bullet-list","uppercase-lowercase-a","star-list","expand-vertically","languages","switch","pane-layout","install","sync","check-in-circle","sync-small","check-small","paused","forward-arrow","stacked-levels","bracket-glyph","note-glyph","tag-glyph","price-tag-glyph","heading-glyph","bold-glyph","italic-glyph","strikethrough-glyph","highlight-glyph","code-glyph","quote-glyph","link-glyph","bullet-list-glyph","number-list-glyph","checkbox-glyph","undo-glyph","redo-glyph","up-chevron-glyph","down-chevron-glyph","left-chevron-glyph","right-chevron-glyph","percent-sign-glyph","keyboard-glyph","double-up-arrow-glyph","double-down-arrow-glyph","image-glyph","wrench-screwdriver-glyph","clock","plus-with-circle","minus-with-circle","indent-glyph","unindent-glyph","fullscreen","exit-fullscreen","cloud","run-command","compress-glyph","enlarge-glyph","scissors-glyph","up-curly-arrow-glyph","down-curly-arrow-glyph","plus-minus-glyph","links-going-out","links-coming-in","add-note-glyph","duplicate-glyph","clock-glyph","calendar-glyph","command-glyph","dice-glyph","file-explorer-glyph","graph-glyph","import-glyph","navigate-glyph","open-elsewhere-glyph","presentation-glyph","paper-plane-glyph","question-mark-glyph","restore-file-glyph","search-glyph","star-glyph","play-audio-glyph","stop-audio-glyph","tomorrow-glyph","wand-glyph","workspace-glyph","yesterday-glyph","box-glyph","merge-files-glyph","merge-files","two-blank-pages","scissors","paste","paste-text","split","select-all-text","wand","github-glyph","reading-glasses","user-manual-filled","discord-filled","chat-bubbles-filled","experiment-filled","bracket-glyph","box-glyph","check-small","dice-glyph","dice","discord","right-triangle","heading-glyph","help","keyboard-toggle","broken-link","experiment","left-arrow","link","link-glyph","links-coming-in","links-going-out","open-vault","paused","question-mark-glyph","right-arrow","sidebar-left","sidebar-right","sheets-in-box","star-list","sync-small","tabs","uppercase-lowercase-a","vault","stack-horizontal","stack-vertical","stretch-horizontal","stretch-vertical","distribute-space-horizontal","distribute-space-vertical"];function a(e){let t=Date.now().toString(36);return t+=Math.random().toString(36).substr(3,e),t}function s(e,t,o,n){let i,a={index:-1,subindex:-1},s=n;return o?s.forEach((e,o)=>{if("SubmenuCommands"in e&&(i=e.SubmenuCommands.findIndex(e=>e.id==t.id),i>=0))return a={index:o,subindex:i},a}):(i=s.findIndex(e=>e.id==t.id),a={index:i,subindex:-1}),a}function r(e,t){const o=t.getLine(t.getCursor().line);let n,i="";const a=/^(\s*(?:>\s*)*(?:\[[!\w]+\]\s*)?)#{1,6}\s+/,s=/^(?:\s*(?:>\s*)*(?:\[[!\w]+\]\s*)?)?(?:(?:#{1,6}\s+)|(?:[-+*]\s+)|(?:\d+\.\s+)|(?:\[[ xX]\]\s+))+/,r=o.match(a),l=r?.[0]?.trim();e===l||""===e?n=o.replace(a,"$1"):(n=o.replace(s,"").trimStart(),n=`${e} ${n}`),i=""!==n?t.getRange(t.getCursor(),{line:t.getCursor().line,ch:o.length}):t.getRange(t.getCursor(),{line:t.getCursor().line,ch:0}),t.setLine(t.getCursor().line,n),t.setCursor({line:t.getCursor().line,ch:Number(n.length-i.length)})}function l(e,t){if(!t)return;const o=t.getSelection();if(!o||""===o.trim())return void this.plugin.setLastExecutedCommand("editing-toolbar:change-font-color");const n=/]+["']?>(.*?)<\/font>/gms,i=n.test(o);if(a=o,new RegExp(`^(.+)<\\/font>$`,"ms").test(a.trim()))return;var a;const s=o.replace(n,(t,o)=>o.split("\n").map(t=>t.trim()?`${t}`:t).join("\n")),r=s===o?o.split("\n").map(t=>t.trim()?`${t}`:t).join("\n"):s,l=t.listSelections().map(t=>{const o=i?0:``.length;return t.anchor.line([\s\S]*?)<\/mark>/g.test(o);if(((e,t)=>{const o=t.replace(/([()[{*+.$^\\|?])/g,"\\$1");return new RegExp(`^([sS]+)<\\/mark>$`).test(e.trim())})(o,e))return;let i;i=n?o.replace(/(background:)(?:#[0-9a-fA-F]{3,6}|rgba?\([^)]+\))/gi,`$1${e}`):o.split("\n").map(t=>t.trim()?`${t}`:t).join("\n");const a=t.listSelections().map(t=>{const o=n?0:``.length;return t.anchor.line{"OrderedList"===e.name&&(d=e.to)}}),d>=0){const e=i.offsetToPos(d).line+1;e=0?i.getLine(g).trim():"";if(f&&!/^\s*$/.test(f)&&!f.includes("ㅤ")&&(h.push(""),h.push("ㅤ")),u)h.push(...o);else{let e={},t=-1;for(const n of o){const o=n.trim(),i=/^\d+\.\s/.test(o),a=n.match(/^\s*/)?.[0]||"";if(i){const n=a.length;e[n]=n!==t?1:(e[n]||1)+1,h.push(`${a}${e[n]}. ${o.replace(/^\d+\.\s/,"")}`),t=n}else h.push(n),t=-1}}i.replaceRange(h.join("\n"),{line:n,ch:0},{line:n+o.length-1,ch:i.getLine(n+o.length-1).length})}function u(e,t,o){const n=[];for(let i=t;i<=o;i++)n.push(e.getLine(i));d(n,t,e)}let m;const p=t=>{m=e.requireApiVersion("0.15.0")?activeWindow.document:window.document;["top","following","fixed"].forEach(e=>{m.querySelectorAll(`.editingToolbarModalBar[data-toolbar-style="${e}"]`).forEach(e=>{t?(e.style.display="",e.style.visibility="visible"):e.style.display="none"})});const o=m.getElementById("editingToolbarModalBar");o&&(t?(o.style.display="",o.style.visibility="visible"):o.style.display="none")},h=t=>{m=e.requireApiVersion("0.15.0")?activeWindow.document:window.document,m.documentElement.style.setProperty("--toolbar-vertical-offset",`${t.verticalPosition}px`)},g=t=>{m=e.requireApiVersion("0.15.0")?activeWindow.document:window.document,m.documentElement.style.setProperty("--toolbar-horizontal-offset",`${t.horizontalPosition}px`)};var f={"Editing Toolbar Append Method":"Editing Toolbar Append Method","Choose where Editing Toolbar will append upon regeneration. To see the change, hit the refresh button below, or in the status bar menu.":"Choose where Editing Toolbar will append upon regeneration. To see the change, hit the refresh button below, or in the status bar menu.","Editing Toolbar aesthetic":"Editing Toolbar aesthetic","Choose between a glass morphism, tiny and default style for Editing Toolbar. To see the change, hit the refresh button below, or in the status bar menu.":"Choose between a glass morphism, tiny and default style for Editing Toolbar. To see the change, hit the refresh button below, or in the status bar menu.","Editing Toolbar position":"Editing Toolbar position","Choose between fixed position or cursor following mode.":"Choose between fixed position, cursor following or Top mode.","Editing Toolbar Columns":"Editing Toolbar Columns","Choose the number of columns per row to display on Editing Toolbar.":"Choose the number of columns per row to display on Editing Toolbar.","Editing Toolbar refresh":"Editing Toolbar refresh","Editing Toolbar Commands":"Editing Toolbar Commands","Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.":"Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.","Format Brush Off!":"Format Brush Off!","Hide & Show":"Hide & Show","Editing Toolbar will only refresh automatically after you have either added or deleted a command from it. To see UI changes to editingToolbar (above settings changes) use the refresh button. If you forget to refresh in settings, no worries. There is also a refresh button in the editingToolbar status bar menu.":"Editing Toolbar will only refresh automatically after you have either added or deleted a command from it. To see UI changes to editingToolbar (above settings changes) use the refresh button. If you forget to refresh in settings, no worries. There is also a refresh button in the editingToolbar status bar menu.","Font-Color formatting brush ON!":"Font-Color formatting brush ON!",More:"More","Copy commands from selected style.":"Copy commands from selected style.","Font Colors":"Font Colors","Format Brush":"Format Brush","Background Color":"Background color",Refresh:"Refresh",Add:"Add",Delete:"Delete","Change Command Name":"Change Command Name","Change Submenu Name":"Change Submenu Name","Button Submenu":"Button Submenu","Dropdown Menu":"Dropdown Menu","Menu type changed to":"Menu type changed to","Add Submenu":"Add Submenu","Add Separator":"Add Separator","Enter the icon code, it looks like format":"Enter the icon code, it looks like format","Please enter a new name: ":"Please enter a new name: ","Drag the slider to move the position":"Drag the slider to move the position","Plugin Settings":"Plugin Settings","Background-color formatting brush ON!":"Background-color formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush","Clear formatting brush ON!":"Clear formatting brush ON!","Clear formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush":"Clear formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush","The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden":"The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden","Editing Toolbar Auto-hide":"Editing Toolbar Auto-hide","Editing Toolbar Centred Display":"Editing Toolbar Centred Display","Whether the toolbar is centred or full-width, the default is full-width.":"Whether the toolbar is centred or full-width, the default is full-width.","Custom Backgroud Color":"Custom Backgroud Color","Custom Font Color":"Custom Font Color","🎨 Set Custom Background":"🎨 Set Custom Background","🖌️ Set Custom Font Color":"🖌️ Set Custom Font Color","Click on the picker to adjust the color":"Click on the picker to adjust the color","Mobile Enabled or Not":"Mobile Enabled or Not","Whether to enable the plugin for the mobile client, the default is enabled.":"Whether to enable the plugin for the mobile client, the default is enabled.","Whether to enable on mobile devices with device width less than 768px.":"Whether to enable on mobile devices with device width less than 768px, the default is disable.",Reset:"Reset",Fix:"Fix","Fix Editing Toolbar":"Fix Editing Toolbar",General:"General",Appearance:"Appearance",Commands:"Commands","Choose between fixed position or cursor following mode":"Choose between fixed position, cursor following or Top mode.","Add and manage commands":"Add and manage commands","Choose where Editing Toolbar will append upon regeneration.":"Choose where Editing Toolbar will append upon regeneration.","Whether to enable on mobile devices with device width less than 768px":"Whether to enable on mobile devices with device width less than 768px","Choose between a glass morphism, tiny and default style.":"Choose between a glass morphism, tiny and default style.","Refresh Toolbar":"Refresh Toolbar","Add Command":"Add Command",Settings:"Settings","Position Style":"Position Style",Columns:"Columns","Drag to Adjust Position":"Drag to Adjust Position","Vertical Position":"Vertical Position","Horizontal Position":"Horizontal Position","Toolbar Position":"Toolbar Position","Choose an icon":"Choose an icon","Search for an icon...":"Search for an icon...",All:"All",Obsidian:"Obsidian",Glyph:"Glyph",Custom:"Custom","Choose a command":"Choose a command","The command":"The command","already exists":"already exists","Enter the icon code, format as ":"Enter the icon code, format as ","No matching icons found":"No matching icons found","Custom Commands":"Custom Commands","Toolbar Commands":"Toolbar Commands",ID:"ID",Prefix:"Prefix",Suffix:"Suffix",Pattern:"Pattern","Custom Format Commands":"Custom Format Commands","Add, edit or delete custom format commands.":"Add, edit or delete custom format commands.",Edit:"Edit","Command ID":"Command ID",'Unique identifier, no spaces, e.g.: "my-custom-format"':'Unique identifier, no spaces, e.g.: "my-custom-format"',"Displayed name in toolbar and menu":"Displayed name in toolbar and menu","Add content before selected text":"Add content before selected text","Add content after selected text":"Add content after selected text","Character offset of cursor after formatting":"Character offset of cursor after formatting","Line offset of cursor after formatting":"Line offset of cursor after formatting","Whether to insert at the beginning of the next line":"Whether to insert at the beginning of the next line","Command icon (click to select)":"Command icon (click to select)","Choose Icon":"Choose Icon",Save:"Save",Cancel:"Cancel","Edit Custom Command":"Edit Custom Command","Add Custom Command":"Add Custom Command","Command ID and command name cannot be empty":"Command ID and command name cannot be empty","Command ID cannot contain spaces":"Command ID cannot contain spaces",'Command ID "${this.commandId}" already exists':'Command ID "${this.commandId}" already exists',"Cursor Position Offset":"Cursor Position Offset","Line Offset":"Line Offset","Line Head Format":"Line Head Format",Icon:"Icon","Command Name":"Command Name","Are you sure you want to restore all settings to default? This will lose all your custom configurations.":"Are you sure you want to restore all settings to default? This will lose all your custom configurations.","Restore default":"Restore default","Restore default settings":"Restore default settings","🔄Restore default settings":"🔄Restore default settings","🔧Data repair":"🔧Data repair","Command IDs have been successfully repaired!":"Command IDs have been successfully repaired!","No command IDs need to be repaired":"No command IDs need to be repaired","Error repairing command IDs, please check the console for details":"Error repairing command IDs, please check the console for details","Error restoring default settings, please check the console for details":"Error restoring default settings, please check the console for details","Successfully restored default settings!":"Successfully restored default settings!",Close:"Close",Tips:"Tips","This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly":"This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly","Repair command ID":"Repair command ID","This will reset all your custom configurations":"This will reset all your custom configurations","Notice:":"Notice:","This update rebuilds the entire code, reducing resource consumption":"This update rebuilds the entire code, reducing resource consumption","Optimized mobile usage, added canvas support, and added custom commands":"Optimized mobile usage, added canvas support, and added custom commands","⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible":"⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible","⚠️If you want to restore the default settings, please click [Restore default settings]":"⚠️If you want to restore the default settings, please click [Restore default settings]","Please execute a editingToolbar format command first, then enable the format brush":"Please execute a editingToolbar format command first, then enable the format brush","Format brush ON! Select text to apply【":"Format brush ON! Select text to apply【","】format":"】format\nClick the mouse right key to close the formatting-brush","Add to Toolbar":"Add to Toolbar","This command is already in the toolbar.":"This command is already in the toolbar.","Command added to toolbar":"Command added to toolbar","Add this command to the toolbar.":"Add this command to the toolbar.","Callout Type":"Callout Type",Title:"Title","Optional, leave blank for default title":"Optional, leave blank for default title","Input title":"Input title","Collapse State":"Collapse State",Open:"Open",Closed:"Closed",Default:"Default",Content:"Content",Insert:"Insert","Input content":"Input content","Link Text":"Link Text","Link Alias":"Link Alias","Link URL":"Link URL","Embed Content":"Embed Content","Image Size":"Image Size","Insert New Line":"Insert New Line","Paste and Parse":"Paste and Parse","URL Format Error":"URL Format Error","Image Width":"Image Width","Image Height":"Image Height","If it is an image, turn on":"If it is an image, turn on","Insert a link on the next line":"Insert a link on the next line","Link Title(optional)":"Link Title(optional)",Alias:"Alias",Optional:"Optional","Default 0, format will keep the text selected":"Default 0, format will keep the text selected","to insert":"to insert","Latest Changes":"Latest Changes","📋View full changelog":"📋View full changelog","Open changelog":"Open changelog","Loading changelog...":"Loading changelog...","Open the complete changelog in your browser":"Open the complete changelog in your browser","Enable Multiple Configurations":"Enable Multiple Configurations","Enable different command configurations for each position style (following, top, fixed).":"Enable different command configurations for each position style (following, top, fixed).","Currently editing commands for":"Currently editing commands for","position style":"position style","Current Configuration":"Current Configuration","Switch between different command configurations.":"Switch between different command configurations.","Following Style":"Following Style","Top Style":"Top Style","Fixed Style":"Fixed Style","Mobile Style":"Mobile Style",configuration:"configuration","Deploy command to configurations":"Deploy command to configurations","All Configurations":"All Configurations",Deploy:"Deploy","Command deployed to selected configurations":"Command deployed to selected configurations","No configuration selected for deployment":"No configuration selected for deployment","Command already exists in selected configurations":"Command already exists in selected configurations","Command deployed to: ":"Command deployed to: ","Command Deleted":"Command Deleted","Confirm Delete?":"Confirm Delete?",Confirm:"Confirm","Are you sure you want to restore all settings to default? But custom commands will be preserved.":"Are you sure you want to restore all settings to default? But custom commands will be preserved.","Successfully restored default settings! (Custom commands preserved)":"Successfully restored default settings! (Custom commands preserved)","This will reset all your custom configurations, but custom commands will be preserved":"This will reset all your custom configurations, but custom commands will be preserved","Import/Export":"Import/Export","Export Configuration":"Export Configuration","Export your toolbar configuration to share with others.":"Export your toolbar configuration to share with others.",Export:"Export","Import Configuration":"Import Configuration","Import toolbar configuration from JSON.":"Import toolbar configuration from JSON.",Import:"Import","Usage Instructions":"Usage Instructions","Export: Generate a JSON configuration that you can save or share.":"Export: Generate a JSON configuration that you can save or share.","Import: Paste a previously exported JSON configuration.":"Import: Paste a previously exported JSON configuration.","You can choose to export all settings, only toolbar commands, or only custom commands":"You can choose to export all settings, only toolbar commands, or only custom commands","When importing, the plugin will only update the settings included in the import data":"When importing, the plugin will only update the settings included in the import data","Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup.":"Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup.","Export Type":"Export Type","Choose what to export":"Choose what to export","All Settings":"All Settings","Toolbar Commands Only":"Toolbar Commands Only","Custom Commands Only":"Custom Commands Only","Export Content":"Export Content","Copy this content to share with others":"Copy this content to share with others","Loading...":"Loading...","Copy to Clipboard":"Copy","Configuration copied to clipboard":"Configuration copied to clipboard","Failed to copy configuration":"Failed to copy configuration","Paste the configuration JSON here":"Paste the configuration JSON here","Paste configuration here...":"Paste configuration here...","Invalid import data":"Invalid import data","Configuration imported successfully":"Configuration imported successfully","No valid configuration found in import data":"No valid configuration found in import data","Failed to import configuration. Invalid format.":"Failed to import configuration. Invalid format.","Import Mode":"Import Mode","Choose how to import the configuration":"Choose how to import the configuration","Update Mode (Add new items and update existing ones)":"Update Mode (Add new items and update existing ones)","Overwrite Mode (Replace all settings with imported ones)":"Overwrite Mode (Replace all settings with imported ones)","Configuration imported successfully (Overwrite mode)":"Configuration imported successfully (Overwrite mode)","Configuration imported successfully (Update mode)":"Configuration imported successfully (Update mode)","Warning: Overwrite mode will completely replace your current settings with the imported ones. Consider exporting your current configuration first as a backup.":"Warning: Overwrite mode will completely replace your current settings with the imported ones. Consider exporting your current configuration first as a backup.","Warning: Update mode will add new items and update existing ones based on the imported configuration.":"Warning: Update mode will add new items and update existing ones based on the imported configuration.","Add Format Command":"Add Format Command",Regex:"Regex","Prefix/Suffix":"Prefix/Suffix","Insert Special Char":"Insert Special Char","Add Regex Command":"Add Regex Command","Switch Regex Command Window":"Switch Regex Command Window","Please select text first":"Please select text first","Place the cursor in a paragraph or select text first.":"Place the cursor in a paragraph or select text first.","Current line is empty, please select text or move to a non-empty line":"Current line is empty, please select text or move to a non-empty line","Use current line for regex commands":"Use current line for regex commands","When no text is selected, regex commands will use the current line instead of clipboard content":"When no text is selected, regex commands will use the current line instead of clipboard content","The selected text does not meet the condition requirements":"The selected text does not meet the condition requirements","Regex command execution error: ":"Regex command execution error: ","Copy code":"Copy code","Copied!":"Copied!","Explain the syntax of JavaScript regular expressions":"Explain the syntax of JavaScript regular expressions","Apply regular expression replacement":"Apply regular expression replacement","Conditional matching":"Conditional matching","Complete regular expression code (copy to AI for explanation)":"Complete regular expression code (copy to AI for explanation)","Error: ":"Error: ","Regex pattern cannot be empty":"Regex pattern cannot be empty","Command already exists":"Command already exists","Choose icon":"Choose icon","URL to Markdown link":"URL to Markdown link","Convert MM/DD/YYYY to YYYY-MM-DD":"Convert MM/DD/YYYY to YYYY-MM-DD","Add bold to keywords":"Add bold to keywords","Format phone number":"Format phone number","Remove extra spaces":"Remove extra spaces","Convert HTML bold tags to Markdown format":"Convert HTML bold tags to Markdown format","Convert quoted text to quote block":"Convert quoted text to quote block","Convert CSV to Markdown table row":"Convert CSV to Markdown table row","Add uniform alias to Markdown links":"Add uniform alias to Markdown links","Delete empty lines (multiline mode)":"Delete empty lines (multiline mode)","Add list symbol to each line (multiline mode)":"Add list symbol to each line (multiline mode)","If the text contains important, set the text highlight (conditional format)":"If the text contains important, set the text highlight (conditional format)","Matching pattern":"Matching pattern","Regex pattern to match":"Regex pattern to match","Replacement pattern (use $1, $2, etc. to reference capture groups)":"Replacement pattern (use $1, $2, etc. to reference capture groups)","Ignore case":"Ignore case","Global replace":"Global replace","Multiline mode":"Multiline mode","Use condition":"Use condition","Condition pattern":"Condition pattern","Only apply custom command when text matches the condition":"Only apply custom command when text matches the condition","Must exist regular expression or text":"Must exist regular expression or text","Replacement pattern":"Replacement pattern","Match case-insensitive":"Match case-insensitive","^ and $ match the start and end of each line":"^ and $ match the start and end of each line","Replace all matches":"Replace all matches",Command:"Command","Input example text to view the formatting effect of the command...":"Input example text to view the formatting effect of the command...",Description:"Description","[Example]":"[Example]","[Requirements]":"[Requirements]","[Output]":"[Output]","AI question template:":"AI question template:","I need to convert the url to a markdown format link":"I need to convert the url to a markdown format link","For example, convert https://example.com to [https://example.com](https://example.com)":"For example, convert https://example.com to [https://example.com](https://example.com)","Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)":"Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)","[Description]":"[Description]","How to use AI to get regular expressions?":"How to use AI to get regular expressions?","Regular expression examples":"Regular expression examples","Edit regular expression command":"Edit regular expression command","Add regular expression command":"Add regular expression command","Result: ":"Result: ","Example text:":"Example text:",Preview:"Preview",Result:"Result","Please select text or copy text to clipboard first":"Please select text or copy text to clipboard first","Overwrite Import":"Overwrite Import","Update Import":"Update Import","Importing configuration...":"Importing configuration...","Following Style Only":"Following Style Only","Top Style Only":"Top Style Only","Fixed Style Only":"Fixed Style Only","Mobile Style Only":"Mobile Style Only","Unknown import type":"Unknown import type","All Toolbar Commands":"All Toolbar Commands","Initialize Commands":"Initialize Commands","Copy commands from the main menu configuration":"Copy commands from the main menu configuration","Initialize commands to default settings":"Initialize commands to default settings","Reset Commands":"Reset Commands","Commands reset successfully":"Commands reset successfully","Following style commands initialized from current menu commands":"Following style commands initialized from current menu commands","Commands initialized successfully":"Commands initialized successfully","Reset to Menu Commands":"Reset to Menu Commands","Are you sure you want to reset the current configuration?":"Are you sure you want to reset the current configuration?","Following style commands successfully initialized":"Following style commands successfully initialized","Top style commands successfully initialized":"Top style commands successfully initialized","Fixed style commands successfully initialized":"Fixed style commands successfully initialized","Mobile style commands successfully initialized":"Mobile style commands successfully initialized","Reset commands to default settings":"Reset commands to default settings",Clear:"Clear","Remove all commands from this configuration.":"Remove all commands from this configuration.","Are you sure you want to clear all commands under the current style?":"Are you sure you want to clear all commands under the current style?","Current style commands have been cleared":"Current style commands have been cleared","Manage Commands":"Manage Commands","Reset or clear all commands in this configuration":"Reset or clear all commands in this configuration","One-click Clear":"One-click Clear","Import Commands from Other Styles":"Import Commands from Other Styles","Copy commands from another style configuration.":"Copy commands from another style configuration.","Main menu only":"Main menu only","This import will update:":"This import will update:","Custom commands":"Custom commands","Toolbar commands":"Toolbar commands","General settings":"General settings","Please paste configuration data first":"Please paste configuration data first","Invalid import data format":"Invalid import data format","Import From":"Import From","This import will:":"This import will:","Update general settings":"Update general settings","Update Main Menu Commands":"Update Main Menu Commands","Update Custom Commands":"Update Custom Commands","Update Following Style Commands":"Update Following Style Commands","Update Top Style Commands":"Update Top Style Commands","Update Fixed Style Commands":"Update Fixed Style Commands","Update Mobile Style Commands":"Update Mobile Style Commands","Clear all Main Menu Commands":"Clear all Main Menu Commands","Clear all Custom Commands":"Clear all Custom Commands","Clear all Following Style Commands":"Clear all Following Style Commands","Clear all Top Style Commands":"Clear all Top Style Commands","Clear all Fixed Style Commands":"Clear all Fixed Style Commands","Clear all Mobile Style Commands":"Clear all Mobile Style Commands","Overwrite Mode (Replace settings with imported ones)":"Overwrite Mode (Replace settings with imported ones)","Warning: Overwrite mode will replace existing settings with imported ones.":"Warning: Overwrite mode will replace existing settings with imported ones.","Warning: Update mode will add new items and update existing ones.":"Warning: Update mode will add new items and update existing ones.","Enable Multiple Config":"Enable Multiple Config","Set Multiple Config to:":"Set Multiple Config to:",Enable:"Enable",Disable:"Disable","Set Position Style to:":"Set Position Style to:",Following:"Following",Top:"Top",Fixed:"Fixed",Mobile:"Mobile","All commands":"All commands","⚠️ Overwrite mode will replace existing settings with imported ones.":"⚠️ Overwrite mode will replace existing settings with imported ones.","ℹ️ Update mode will merge imported settings with existing ones.":"ℹ️ Update mode will merge imported settings with existing ones.","Do you want to continue?":"Do you want to continue?","Imported settings:":"Imported settings:","Imported commands:":"Imported commands:","Disable toolbar for this view":"Disable toolbar for this view","Enable toolbar for this view":"Enable toolbar for this view","Manage all view types":"Manage all view types","Current View: ":"Current View: ","Appearance Style":"Appearance Style","Position Settings":"Position Settings","All commands have been removed.":"All commands have been removed.","Join the Community":"Join the Community","Share your toolbar settings and styles in our":"Share your toolbar settings and styles in our","Get inspired by what others have created or showcase your own customizations.":"Get inspired by what others have created or showcase your own customizations.","Toolbar Preview (With a hypothetical command configuration.)":"Toolbar Preview (With a hypothetical command configuration.)","Toolbar Theme":"Toolbar Theme","Select a preset toolbar theme, automatically setting the background color, icon color, and size for the selected style.":"Select a preset toolbar theme, automatically setting the background color, icon color, and size for the selected style.","Toolbar Background Color":"Toolbar Background Color","Set the background color of the toolbar.":"Set the background color of the toolbar.","Toolbar Icon Color":"Toolbar Icon Color","Set the color of the toolbar icon.":"Set the color of the toolbar icon.","Toolbar Icon Size":"Toolbar Icon Size","Set the size of the toolbar icon (px); default: 18px":"Set the size of the toolbar icon (px); default: 18px","Custom Theme":"Custom Theme","Fixed Position Offset":"Fixed Position Offset","Choose the offset of the Editing Toolbar in the fixed position.":"Choose the offset of the Editing Toolbar in the fixed position.","Renumber List":"Renumber List","Fetch Remote Title":"Fetch Remote Title","Please enter a URL first":"Please enter a URL first","Failed to fetch title for":"Failed to fetch title for","Link Title (optional)":"Link Title (optional)","Unable to detect editor width":"Unable to detect editor width","Fit Editor Width":"Fit Editor Width","Please execute a format command or select format text first, then enable the format brush":"Please execute a format command or select format text first, then enable the format brush","Use \\n to represent line breaks":"Use \\n to represent line breaks","Use ↵ to represent line breaks":"Use ↵ to represent line breaks","Top Toolbar":"Top Toolbar","Enable the toolbar positioned at the top.":"Enable the toolbar positioned at the top.","Following Toolbar":"Following Toolbar","Enable the toolbar that appears upon text selection.":"Enable the toolbar that appears upon text selection.","Fixed Toolbar":"Fixed Toolbar","Enable the toolbar whose position may be fixed where you please.":"Enable the toolbar whose position may be fixed where you please.","Toolbar Settings":"Toolbar Settings","Choose which toolbar style's appearance you want to edit.":"Choose which toolbar style's appearance you want to edit.","Vertical Split":"Vertical Split","Text Enhancement Tools":"Text Enhancement Tools","Get Plain Text":"Get Plain Text","Full Half Converter":"Full Half Converter","Insert Blank Lines":"Insert Blank Lines","Remove Blank Lines":"Remove Blank Lines","Split Lines":"Split Lines","Dedupe Lines":"Dedupe Lines","Add Prefix/Suffix":"Add Prefix/Suffix","Number Lines (Custom)":"Number Lines (Custom)","Trim Line Ends":"Trim Line Ends","Shrink Extra Spaces":"Shrink Extra Spaces","Remove All Whitespace":"Remove All Whitespace","Extract Between Strings":"Extract Between Strings","Merge Lines":"Merge Lines","List to Table":"List to Table","Table to List":"Table to List","Line Operations":"Line Operations","Text Processing":"Text Processing","Advanced Tools":"Advanced Tools","Align Text Right":"Align Text Right",Bold:"Bold",Checklist:"Checklist","Clear Text Formatting":"Clear Text Formatting",Copy:"Copy",Cut:"Cut",Embed:"Embed","Header 1":"Header 1","Header 2":"Header 2","Header 3":"Header 3","Header 4":"Header 4","Header 5":"Header 5","Header 6":"Header 6",Highlight:"Highlight","Horizontal Divider":"Horizontal Divider","Indent list":"Indent list","Inline Math":"Inline Math",Italic:"Italic",Link:"Link",MathBlock:"MathBlock","Ordered List":"Ordered List",Paste:"Paste","Redo Edit":"Redo Edit",Strikethrough:"Strikethrough",Underline:"Underline","Undo Edit":"Undo Edit","Unindent List":"Unindent List","Unordered List":"Unordered List","Insert Embed":"Insert Embed","Insert Link":"Insert Link","Insert Tag":"Insert Tag","Insert Internal link":"Insert Internal link","Insert Code":"Insert Code","Insert Blockquote":"Insert Blockquote","Insert Comment":"Insert Comment","Insert Callout":"Insert Callout","Insert MathBlock":"Insert MathBlock","Insert Table":"Insert Table","Swap Line Up":"Swap Line Up","Swap Line Down":"Swap Line Down","Attach File":"Attach File","Clear Formatting":"Clear Formatting","Cycle List and Checklist":"Cycle List and Checklist",Blockquote:"Blockquote",Callout:"Callout",Superscript:"Superscript",Subscript:"Subscript","Inline Code":"Inline Code","Code Block":"Code Block",Wikilink:"Wikilink","Justify Text":"Justify Text","Align Text Left":"Align Text Left","Center Text":"Center Text","Change Font Color":"Change Font Color","Change Background Color":"Change Background Color","Fullscreen Focus Mode":"Fullscreen Focus Mode","Workplace Fullscreen":"Workplace Fullscreen","Renumber Ordered List":"Renumber Ordered List","Toggle Format Brush":"Toggle Format Brush","Toggle Fullscreen Focus Mode":"Toggle Fullscreen Focus Mode","Toggle Workplace Fullscreen Focus":"Toggle Workplace Fullscreen Focus","Enter prefix":"Enter prefix","Enter suffix":"Enter suffix",Delimiter:"Delimiter","Enter delimiter (e.g., comma, tab)":"Enter delimiter (e.g., comma, tab)","Column Number":"Column Number","Enter column number (starting from 1)":"Enter column number (starting from 1)","Start String":"Start String","Enter start string":"Enter start string","End String":"End String","Enter end string":"Enter end string","Number Lines Configuration":"Number Lines Configuration","Start Number":"Start Number",Step:"Step",Separator:"Separator","Merge Lines Settings":"Merge Lines Settings","Separator (leave empty for smart spacing)":"Separator (leave empty for smart spacing)","e.g., comma, pipe, arrow":"e.g., comma, pipe, arrow","Plain text copied to clipboard":"Plain text copied to clipboard","Whitespace cleaning completed":"Whitespace cleaning completed","List pattern detected, auto-split":"List pattern detected, auto-split","No obvious separator or list pattern detected":"No obvious separator or list pattern detected","Paste failed":"Paste failed","Detected Chinese context: converted to full-width symbols":"Detected Chinese context: converted to full-width symbols","Detected code/English context: converted to half-width symbols":"Detected code/English context: converted to half-width symbols","Please select text to dedupe first":"Please select text to dedupe first","Deduplication completed, remaining":"Deduplication completed, remaining",lines:"lines","Prefix/suffix added":"Prefix/suffix added","Please select text to number first":"Please select text to number first","Numbering completed: starting from":"Numbering completed: starting from","Please specify start or end string":"Please specify start or end string",Extracted:"Extracted",matches:"matches","No matches found":"No matches found","Extraction failed":"Extraction failed","Please select lines to merge first":"Please select lines to merge first","Merged with":"Merged with","Merge completed":"Merge completed",Item:"Item","Super conversion completed: context preserved and layout optimized":"Super conversion completed: context preserved and layout optimized","Please select a valid Markdown table":"Please select a valid Markdown table","Table converted to multi-level list":"Table converted to multi-level list","AI Editor":"AI Editor","Enable AI Editor":"Enable AI Editor","Enable AI editor features such as inline completion and selection rewrite.":"Enable AI editor features such as inline completion and selection rewrite.","Try AI Editing":"Try AI Editing","Unlock inline completion, rewrite, frontmatter, and canvas generation right inside your toolbar.":"Unlock inline completion, rewrite, frontmatter, and canvas generation right inside your toolbar.","Sign in to PKMer AI for free managed AI, or use your own compatible model.":"Sign in to PKMer AI for free managed AI, or use your own compatible model.","What you get":"What you get","Inline completion while you write":"Inline completion while you write","Rewrite, summarize, and continue text in place":"Rewrite, summarize, and continue text in place","Generate frontmatter, lists, tables, and canvas drafts":"Generate frontmatter, lists, tables, and canvas drafts","Before you enable AI":"Before you enable AI","The plugin itself does not intentionally store your note content.":"The plugin itself does not intentionally store your note content.","AI requests are sent only to the provider you choose, such as PKMer AI or your custom model.":"AI requests are sent only to the provider you choose, such as PKMer AI or your custom model.","Those requests remain subject to the provider privacy policy, terms, and model rules.":"Those requests remain subject to the provider privacy policy, terms, and model rules.","You can turn AI off at any time in settings.":"You can turn AI off at any time in settings.","Agree & Enable AI":"Agree & Enable AI","Not now":"Not now","AI editing is now enabled. The plugin does not intentionally store your note content, and requests follow your chosen provider policies.":"AI editing is now enabled. The plugin does not intentionally store your note content, and requests follow your chosen provider policies.",Enabled:"Enabled",Disabled:"Disabled","Completion Mode":"Completion Mode","Choose whether completion is triggered manually or automatically after a short pause.":"Choose whether completion is triggered manually or automatically after a short pause.","Enable Inline Completion":"Enable Inline Completion","Show ghost text suggestions inside the editor.":"Show ghost text suggestions inside the editor.","Completion Delay (ms)":"Completion Delay (ms)","Delay before auto-triggering inline completion.":"Delay before auto-triggering inline completion.","Completion Max Tokens":"Completion Max Tokens","Upper bound for one inline completion response. Increase it if completions feel cut off.":"Upper bound for one inline completion response. Increase it if completions feel cut off.",Manual:"Manual",Auto:"Auto","Text Tools":"Text Tools","AI Tools":"AI Tools","Inline Completion":"Inline Completion","Select text to see more tools":"Select text to see more tools","AI Assistant":"AI Assistant","AI module is ready":"AI module is ready","Use AI enhancements directly from the toolbar.":"Use AI enhancements directly from the toolbar.","Place the cursor for inline completion, or select text to unlock rewrite actions.":"Place the cursor for inline completion, or select text to unlock rewrite actions.","Selected text detected. Rewrite actions are ready.":"Selected text detected. Rewrite actions are ready.","AI Continue Writing":"AI Continue Writing","AI Summarize Selection":"AI Summarize Selection","AI Explain Selection":"AI Explain Selection","At Cursor":"At Cursor","On Selection":"On Selection","Continue writing at the current cursor.":"Continue writing at the current cursor.","Polish tone, wording, and structure.":"Polish tone, wording, and structure.","Extend from the selected passage.":"Extend from the selected passage.","Turn long text into concise points.":"Turn long text into concise points.","Explain the meaning and key ideas.":"Explain the meaning and key ideas.","Use your own prompt on the selection.":"Use your own prompt on the selection.","Custom Model":"Custom Model",Unavailable:"Unavailable","Checking...":"Checking...",AI:"AI","AI Complete":"AI Complete","AI Rewrite":"AI Rewrite","AI Continue":"AI Continue","AI Summarize":"AI Summarize","AI Explain":"AI Explain","AI Translate":"AI Translate","AI Tone":"AI Tone","AI Fix":"AI Fix","AI Shorten":"AI Shorten","AI Expand":"AI Expand","AI Simplify":"AI Simplify","AI Professional":"AI Professional","AI Casual":"AI Casual","AI Custom":"AI Custom",Tone:"Tone",Translate:"Translate",Generate:"Generate","Improve writing":"Improve writing","Fix spelling & grammar":"Fix spelling & grammar","Make shorter":"Make shorter","Make longer":"Make longer","Simplify language":"Simplify language","Professional tone":"Professional tone","Casual tone":"Casual tone",English:"English",Chinese:"Chinese",Japanese:"Japanese",German:"German",French:"French",Spanish:"Spanish","Explain this":"Explain this",Summarize:"Summarize","Continue writing":"Continue writing","Press Enter to send, Shift+Enter for newline, Esc to close.":"Press Enter to send, Shift+Enter for newline, Esc to close.Type [[]] to reference document content.","Type [[]] to reference document content.":"Type [[]] to reference document content.","Enter inserts a newline. Tap Send to submit.":"Enter inserts a newline. Tap Send to submit.","Open AI Settings":"Open AI Settings","Trigger AI Inline Completion":"Trigger AI Inline Completion","AI Improve Selection":"AI Improve Selection","AI Custom Rewrite":"AI Custom Rewrite",Send:"Send","Selected text":"Selected text","Current document":"Current document",characters:"characters","Login to PKMer AI":"Login to PKMer AI","PKMer AI":"PKMer AI","Sign in to PKMer to use managed AI without manual model setup.":"Sign in to PKMer to use managed AI without manual model setup.","Log in to PKMer AI to get free AI features without manual model setup.":"Log in to PKMer AI to get free AI features without manual model setup.","Log in to PKMer AI and you can start using free AI features right away.":"Log in to PKMer AI and you can start using free AI features right away.","Need a PKMer AI account?":"Need a PKMer AI account?","Open PKMer AI":"Open PKMer AI","PKMer Account":"PKMer Account",Login:"Login",Logout:"Logout","Refresh Quota":"Refresh Quota","Check Quota":"Check Quota","More Quota":"More Quota","PKMer AI is ready. You can use AI now and check your quota here.":"PKMer AI is ready. You can use AI now and check your quota here.","PKMer managed defaults are used automatically after sign-in.":"PKMer managed defaults are used automatically after sign-in.","Checking current AI route...":"Checking current AI route...","Current route: PKMer AI. If unavailable, it falls back to your custom model.":"Current route: PKMer AI. If unavailable, it falls back to your custom model.","Current route: PKMer AI.":"Current route: PKMer AI.","Current route: Custom model.":"Current route: Custom model.","No provider available. Log in to PKMer or complete the custom model settings.":"No provider available. Log in to PKMer or complete the custom model settings.","No provider available. Log in to PKMer to enable AI.":"No provider available. Log in to PKMer to enable AI.","Unable to determine current AI route.":"Unable to determine current AI route.",Account:"Account",Complete:"Complete","Quick Trigger":"Quick Trigger","Improve Selection":"Improve Selection","Custom Rewrite":"Custom Rewrite","Logout from PKMer":"Logout from PKMer","Editor Features":"Editor Features","PKMer Model":"PKMer Model","Choose models by task.":"Choose models by task.",Mode:"Mode","Light tasks use the light model. Complex tasks use the reasoning model.":"Light tasks use the light model. Complex tasks use the reasoning model.",Completion:"Completion",Rewrite:"Rewrite",Reasoning:"Reasoning",Structured:"Structured","Used for inline completion.":"Used for inline completion.","Used for normal rewrite.":"Used for normal rewrite.","Used for explain, summarize, and custom prompts.":"Used for explain, summarize, and custom prompts.","Used for frontmatter and canvas.":"Used for frontmatter and canvas.","PKMer route only.":"PKMer route only.","Light model":"Light model","Reasoning model":"Reasoning model","Inline completion":"Inline completion","Configure inline completion and rewrite after your AI provider is ready.":"Configure inline completion and rewrite after your AI provider is ready.","Custom Model (Optional)":"Custom Model (Optional)","Custom model is used automatically when PKMer AI is unavailable.":"Custom model is used automatically when PKMer AI is unavailable.","Enable Custom Model":"Enable Custom Model","Use your own OpenAI-compatible provider as a fallback.":"Use your own OpenAI-compatible provider as a fallback.","Custom API Format":"Custom API Format","Choose whether the custom model uses an OpenAI-compatible endpoint or the native Ollama API.":"Choose whether the custom model uses an OpenAI-compatible endpoint or the native Ollama API.","OpenAI-compatible":"OpenAI-compatible",Ollama:"Ollama","Custom API Base URL":"Custom API Base URL","OpenAI-compatible endpoint for your own provider.":"OpenAI-compatible endpoint for your own provider.","Native Ollama endpoint. The root URL, /api, /api/chat, or /api/generate are all supported.":"Native Ollama endpoint. The root URL, /api, /api/chat, or /api/generate are all supported.","Custom Model Name":"Custom Model Name","Model identifier used for inline completion and rewrite requests.":"Model identifier used for inline completion and rewrite requests.","Detected Ollama Models":"Detected Ollama Models","Fetch available models from your Ollama service.":"Fetch available models from your Ollama service.","Choose a detected Ollama model to fill the model field.":"Choose a detected Ollama model to fill the model field.","Select a detected model":"Select a detected model","Custom API Key":"Custom API Key","Optional for Ollama. Leave empty unless your gateway requires authentication.":"Optional for Ollama. Leave empty unless your gateway requires authentication.","Stored securely in Obsidian secret storage.":"Stored securely in Obsidian secret storage.","Will be stored securely in Obsidian secret storage.":"Will be stored securely in Obsidian secret storage.","Current Obsidian version does not support secure secret storage.":"Current Obsidian version does not support secure secret storage.","Stored securely":"Stored securely","Enter API key":"Enter API key",Temperature:"Temperature","Lower values are more stable; higher values are more creative.":"Lower values are more stable; higher values are more creative.","Test Connection":"Test Connection","Send a lightweight request to verify your custom model settings.":"Send a lightweight request to verify your custom model settings.","Please fill in the required custom model settings first.":"Please fill in the required custom model settings first.","No Ollama models found at this endpoint.":"No Ollama models found at this endpoint.","Failed to load Ollama models:":"Failed to load Ollama models:","Please fill in Custom API Base URL, Custom Model Name, and Custom API Key first.":"Please fill in Custom API Base URL, Custom Model Name, and Custom API Key first.","Testing...":"Testing...","Testing custom model connection...":"Testing custom model connection...","Custom model connection succeeded.":"Custom model connection succeeded.","Custom model connection failed:":"Custom model connection failed:","Unknown connection error.":"Unknown connection error.","PKMer AI request failed because your quota is insufficient. Please get more quota in PKMer and try again.":"PKMer AI request failed because your quota is insufficient. Please get more quota in PKMer and try again.","Available Variables":"Available Variables","Document path":"Document path","Full document content":"Full document content",Date:"Date",Time:"Time","Date and time":"Date and time","Vault name":"Vault name","Linked note references":"Linked note references","Use [[note name]] to reference the content of other notes.":"Use [[note name]] to reference the content of other notes.","Manual Completion Shortcut":"Manual Completion Shortcut","Default shortcut is Ctrl+J. You can customize it in Obsidian Hotkeys.":"Default shortcut is Ctrl+J. You can customize it in Obsidian Hotkeys.","Open Hotkey Settings":"Open Hotkey Settings","More Options":"More Options",Advanced:"Advanced","Advanced AI settings are usually not needed.":"Advanced AI settings are usually not needed.","Not logged in":"Not logged in","Logged in":"Logged in",Quota:"Quota","Unable to refresh PKMer quota.":"Unable to refresh PKMer quota.","PKMer quota refreshed:":"PKMer quota refreshed:","AI features are disabled in settings.":"AI features are disabled in settings.","Inline completion is disabled in settings.":"Inline completion is disabled in settings.","Current editor does not support AI inline completion.":"Current editor does not support AI inline completion.","Current editor does not support AI rewrite.":"Current editor does not support AI rewrite.","AI Custom Instruction":"AI Custom Instruction",Instruction:"Instruction",Suggestions:"Suggestions","Referenced notes":"Referenced notes","Please enter your custom AI instruction":"Please enter your custom AI instruction","Current Obsidian version does not support secure token storage.":"Current Obsidian version does not support secure token storage.","Login cancelled or timed out.":"Login cancelled or timed out.","PKMer login is already in progress. Please continue in the opened browser window.":"PKMer login is already in progress. Please continue in the opened browser window.","Successfully logged in to PKMer!":"Successfully logged in to PKMer!","Failed to complete login. Please try again.":"Failed to complete login. Please try again.","Login failed. Please try again.":"Login failed. Please try again.","OAuth state mismatch. Please try logging in again.":"OAuth state mismatch. Another Obsidian window may have intercepted the callback. Please close other vaults and try again.","Logged out from PKMer.":"Logged out from PKMer.","AI thinking":"AI thinking","AI generating":"AI generating","Ask AI to edit or generate...":"Ask AI to edit or generate...","Submit custom instruction":"Submit custom instruction","AI is writing...":"AI is writing...","AI is generating":"AI is generating","AI suggestion":"AI suggestion","Press Tab to accept":"Press Tab to accept",Replace:"Replace","Insert below":"Insert below","Try again":"Try again",Discard:"Discard","AI List":"AI List","AI Table":"AI Table","AI Base":"AI Base","AI Canvas":"AI Canvas","AI Canvas Prompt":"AI Canvas Prompt","AI Frontmatter":"AI Frontmatter",Toolbox:"Toolbox","AI Toolbox":"AI Toolbox","Convert to list":"Convert to list","Convert to table":"Convert to table","Convert to base":"Convert to base","Convert to canvas":"Convert to canvas","Generate frontmatter":"Generate frontmatter","Insert at cursor":"Insert at cursor",Create:"Create","Create & Embed":"Create & Embed","AI file suggestion":"AI file suggestion","AI frontmatter suggestion":"AI frontmatter suggestion","AI Base File":"AI Base","AI Canvas File":"AI Canvas","Insert frontmatter":"Insert frontmatter","Replace frontmatter":"Replace frontmatter","Failed to apply frontmatter.":"Failed to apply frontmatter.","Created AI file:":"Created AI file:","Failed to create AI file.":"Failed to create AI file.","AI file creation is unavailable.":"AI file creation is unavailable.","AI generated content is empty.":"AI generated content is empty.","AI returned invalid Canvas JSON.":"AI returned invalid Canvas JSON.","AI Canvas Expand":"AI Canvas Expand",Canvas:"Canvas","Expand Current Node":"Expand Current Node","Global Prompt":"Global Prompt","Expand current canvas node":"Expand current node prompt","Canvas Tools":"Canvas Tools","Canvas global prompt":"Canvas global prompt","Connect related nodes":"Connect related nodes","Add missing branches":"Add missing branches","Clarify structure":"Clarify structure","Generate next steps":"Generate next steps","Add risks and dependencies":"Add risks and dependencies","Selected nodes":"Selected nodes",Focused:"Focused",Selected:"Selected","Tidy selected layout":"Tidy selected layout","Reorganize the selected nodes into a clearer local hierarchy with tidy spacing and fewer edge crossings. Reuse current nodes instead of adding new ones.":"Reorganize the selected nodes into a clearer local hierarchy with tidy spacing and fewer edge crossings. Reuse current nodes instead of adding new ones.","Connect selected nodes":"Connect selected nodes","Connect the selected nodes with the most meaningful relationships.":"Connect the selected nodes with the most meaningful relationships.","Add the missing branches around the selected nodes.":"Add the missing branches around the selected nodes.","Clarify the structure around the selected nodes and remove ambiguity.":"Clarify the structure around the selected nodes and remove ambiguity.","Generate the next actionable steps from the selected nodes.":"Generate the next actionable steps from the selected nodes.","Add the risks, dependencies, and constraints related to the selected nodes.":"Add the risks, dependencies, and constraints related to the selected nodes.","Generate the next actionable steps from the current canvas node.":"Generate the next actionable steps from the current canvas node.","Add the risks, dependencies, and constraints related to the current canvas node.":"Add the risks, dependencies, and constraints related to the current canvas node.","Add the missing branches around the current canvas node.":"Add the missing branches around the current canvas node.","Clarify the structure around the current canvas node and remove ambiguity.":"Clarify the structure around the current canvas node and remove ambiguity.","Organize canvas layout":"Organize canvas layout","Reorganize this canvas into a clean hierarchy with tidy spacing, aligned sibling nodes, and fewer edge crossings. Reuse current nodes instead of adding new ones.":"Reorganize this canvas into a clean hierarchy with tidy spacing, aligned sibling nodes, and fewer edge crossings. Reuse current nodes instead of adding new ones.","Reduce edge crossings":"Reduce edge crossings","Reorganize the canvas to reduce edge crossings, keep related nodes close, and make the reading path clearer. Reuse current nodes instead of adding new ones.":"Reorganize the canvas to reduce edge crossings, keep related nodes close, and make the reading path clearer. Reuse current nodes instead of adding new ones.","Canvas to article":"Canvas to article","Convert this canvas into a polished Markdown article draft.":"Convert this canvas into a polished Markdown article draft.","Canvas to slides":"Canvas to slides","Convert this canvas into Obsidian Slides Markdown.":"Convert this canvas into Obsidian Slides Markdown.","Convert the referenced content into a structured canvas with one central topic, main branches, supporting details, and explicit relations.":"Convert the referenced content [[]] into a structured canvas with one central topic, main branches, supporting details, and explicit relations.","Reorganize board":"Reorganize board","Reorganize the whole canvas into a clearer hierarchy with better grouping.":"Reorganize the whole canvas into a clearer hierarchy with better grouping.","Reorganize the existing canvas nodes into a clearer hierarchy and grouping. Reuse current nodes instead of adding new ones.":"Reorganize the existing canvas nodes into a clearer hierarchy and grouping. Reuse current nodes instead of adding new ones.","Connect board clusters":"Connect board clusters","Connect the related clusters across the whole canvas and add missing bridge nodes.":"Connect the related clusters across the whole canvas and add missing bridge nodes.","Summarize main narrative":"Summarize main narrative","Identify the main narrative of this canvas and improve the section flow.":"Identify the main narrative of this canvas and improve the section flow.","e.g. split into next steps, risks, dependencies":"e.g. split into next steps, risks, dependencies","e.g. connect related nodes, add missing branches, clarify structure":"e.g. connect related nodes, add missing branches, clarify structure","e.g. tidy selected layout, connect selected nodes, add missing branches":"e.g. tidy selected layout, connect selected nodes, add missing branches","e.g. tidy selected layout, connect selected nodes, add missing branches, convert [[a note]] to canvas":"e.g. tidy selected layout, connect selected nodes, add missing branches, convert [[a note]] to canvas","e.g. reorganize the board, turn this canvas into an article, turn this canvas into Obsidian Slides":"e.g. reorganize the board, turn this canvas into an article, turn this canvas into Obsidian Slides","e.g. reorganize the board, turn this canvas into an article, turn [[a note]] into canvas":"e.g. reorganize the board, turn this canvas into an article, turn [[a note]] into canvas","Canvas AI requires an active Canvas file.":"Canvas AI requires an active Canvas file.","Please select a canvas node first.":"Please select a canvas node first.","Please enter your canvas instruction.":"Please enter your canvas instruction.","Please reference at least one note with [[...]] before converting to canvas.":"Please reference at least one note with [[...]] before converting to canvas.","Canvas AI is expanding the current node...":"Canvas AI is expanding the current node...","Canvas AI is processing the board...":"Canvas AI is processing the board...","Canvas AI is reorganizing the board...":"Canvas AI is reorganizing the board...","Canvas AI is structuring the referenced content...":"Canvas AI is structuring the referenced content...","Canvas AI is drafting an article...":"Canvas AI is drafting an article...","Canvas AI is drafting slides...":"Canvas AI is drafting slides...","Canvas AI added":"Canvas AI added","Canvas AI updated the board:":"Canvas AI updated the board:","Canvas AI reorganized the board:":"Canvas AI reorganized the board:",nodes:"nodes",links:"links","nodes moved":"nodes moved","links rebuilt":"links rebuilt","nodes to the board.":"nodes to the board.","Expand the current node into the most useful next neighboring nodes.":"Expand the current node into the most useful next neighboring nodes.","Canvas AI returned invalid JSON.":"Canvas AI returned invalid JSON.","Canvas AI did not return any usable nodes.":"Canvas AI did not return any usable nodes.","Canvas AI did not return any usable actions.":"Canvas AI did not return any usable actions.","Canvas AI did not return any usable Markdown.":"Canvas AI did not return any usable Markdown.","The focused canvas node no longer exists.":"The focused canvas node no longer exists.","AI Article Draft":"AI Article Draft","AI Slides":"AI Slides","Failed to read the current canvas data.":"Failed to read the current canvas data.","Unable to allocate a file name for the generated artifact.":"Unable to allocate a file name for the generated artifact.",Untitled:"Untitled"},b={"Editing Toolbar Append Method":"工具栏的附加方法。","Choose where Editing Toolbar will append upon regeneration. To see the change, hit the refresh button below, or in the status bar menu.":"工具栏在Obsidian中的追加的位置,只对固定和跟随模式有效。如果你遇到工具栏显示问题,可以选择body试试。请点击下面或者状态栏菜单中的刷新按钮生效。","Editing Toolbar aesthetic":"工具栏样式","Choose between a glass morphism, tiny and default style for Editing Toolbar. To see the change, hit the refresh button below, or in the status bar menu.":"样式有毛玻璃,简约和默认风格选择。请点击下面或者状态栏菜单中的刷新按钮生效。","Editing Toolbar position":"工具栏位置","Choose between fixed position or cursor following mode.":"在固定位置,光标跟随模式或者置顶模式之间进行选择。","Editing Toolbar Columns":"工具栏栏目数","Choose the number of columns per row to display on Editing Toolbar.":"选择在Editing Toolbar上显示的每行的列数。","Editing Toolbar refresh":"刷新工具栏","Editing Toolbar Commands":"在工具栏中添加命令","Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.":"从Obsidian的命令库中添加一个命令到工具栏。要重新排列命令,可以拖放命令项。要删除它们,请使用命令项右边的删除按钮。图标选择Custom可以自定义图标","Format Brush Off!":"关闭格式刷!","Hide & Show":"隐藏 & 显示","Editing Toolbar will only refresh automatically after you have either added or deleted a command from it. To see UI changes to editingToolbar (above settings changes) use the refresh button. If you forget to refresh in settings, no worries. There is also a refresh button in the editingToolbar status bar menu.":"对外观的更改生效需要使用刷新按钮。如果你忘记在设置中刷新,在状态栏菜单中也有一个刷新按钮。","Font-Color formatting brush ON!":"字体颜色格式刷开启\n点击鼠标中键或者右键关闭格式刷",More:"更多","Copy commands from selected style.":"从所选样式复制命令。","Font Colors":"字体颜色","Format Brush":"格式刷","Background Color":"背景颜色",Refresh:"刷新",Add:"添加",Delete:"删除","Change Command Name":"更改命令名称","Change Submenu Name":"更改子菜单名称","Button Submenu":"按钮子菜单","Dropdown Menu":"下拉菜单","Menu type changed to":"菜单类型已更改为","Add Submenu":"添加子菜单","Add Separator":"添加分割线","Enter the icon code, it looks like format":"输入图标代码,类似格式","Please enter a new name: ":"请输入新名称:","Drag the slider to move the position":"拖动滑块来移动位置","Plugin Settings":"插件设置","Background-color formatting brush ON!":"开启背景色格式刷","Clear formatting brush ON!":"清除格式刷已开启","Clear formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush":"清除格式刷已开启\n点击鼠标中键或者右键关闭格式刷","The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden":"当鼠标移到工具栏上方时,工具栏显示,否则自动隐藏","Editing Toolbar Auto-hide":"工具栏是否自动隐藏","Editing Toolbar Centred Display":"工具栏是否居中显示","Whether the toolbar is centred or full-width, the default is full-width.":"工具栏居中还是全宽显示,默认全宽显示","Custom Backgroud Color":"设置自定义背景色","Custom Font Color":"设置自定义字体颜色","🎨 Set Custom Background":"🎨 设置自定义背景","🖌️ Set Custom Font Color":"🖌️ 设置自定义字体颜色","Click on the picker to adjust the color":"点击选取器来调整颜色","Mobile Enabled or Not":"是否在移动端启用","Whether to enable on mobile devices with device width less than 768px.":"是否在设备宽度小于768的移动设备启用。默认不启用",Reset:"重置",Fix:"修复","Fix Editing Toolbar":"修复工具栏",General:"常规",Appearance:"外观",Commands:"命令","Choose between fixed position or cursor following mode":"选择固定位置或者光标跟随模式","Add and manage commands":"添加和管理命令","Choose where Editing Toolbar will append upon regeneration.":"选择工具栏在Obsidian中的追加的位置。","Whether to enable on mobile devices with device width less than 768px":"是否在移动设备中启用。默认不启用","Choose between a glass morphism, tiny and default style":"选择毛玻璃,简约和默认风格","Refresh Toolbar":"刷新工具栏","Add Command":"添加命令",Settings:"设置","Adjust Toolbar Position[Fixed mode]":"调整工具栏位置[固定模式]","Position Style":"位置样式",Columns:"列数","Drag to Adjust Position":"拖动调整位置","Vertical Position":"垂直位置","Horizontal Position":"水平位置","Toolbar Position":"工具栏位置","Choose an icon":"选择一个图标","Search for an icon...":"搜索图标...",All:"全部",Obsidian:"Obsidian",Glyph:"Glyph","Choose a command":"选择一个命令","The command":"命令","already exists":"已存在","Enter the icon code, format as ":"输入图标代码,格式为 ","No matching icons found":"没有找到匹配的图标","Custom Commands":"自定义命令","Toolbar Commands":"工具栏命令",ID:"ID",Prefix:"前缀",Suffix:"后缀","Custom Format Commands":"自定义格式命令","Add, edit or delete custom format commands.":"添加、编辑或删除自定义格式命令","Command ID":"命令ID",'Unique identifier, no spaces, e.g.: "my-custom-format"':'唯一标识符,不包含空格,例如:"my-custom-format"',"Displayed name in toolbar and menu":"在工具栏和菜单中显示的名称","Add content before selected text":"在选中的文本前添加内容","Add content after selected text":"在选中的文本后添加内容","Character offset of cursor after formatting":"格式化后光标的字符偏移量","Line offset of cursor after formatting":"格式化后光标的行偏移量","Whether to insert at the beginning of the next line":"是否在下一行首插入","Command icon (click to select)":"命令图标(点击选择)","Choose Icon":"选择图标",Save:"保存",Cancel:"取消","Edit Custom Command":"编辑自定义命令","Add Custom Command":"添加自定义命令","Command ID and command name cannot be empty":"命令ID和命令名称不能为空","Command ID cannot contain spaces":"命令ID不能包含空格",'Command ID "${this.commandId}" already exists':'命令ID "${this.commandId}" 已存在',"Command Name":"命令名称","Cursor Position Offset":"光标位置偏移量","Line Offset":"行偏移量","Line Head Format":"行首格式",Icon:"图标","Are you sure you want to restore all settings to default? This will lose all your custom configurations.":"确定要恢复所有设置为默认值吗?这将丢失您的所有自定义配置。","Restore default":"恢复默认值","Restore default settings":"恢复默认设置","🔄Restore default settings":"🔄恢复默认设置","🔧Data repair":"🔧数据修复","Command IDs have been successfully repaired!":"命令ID已成功修复!","No command IDs need to be repaired":"没有命令ID需要修复","Error repairing command IDs, please check the console for details":"修复命令ID时出错,请查看控制台了解详情","Error restoring default settings, please check the console for details":"恢复默认设置时出错,请查看控制台了解详情","Successfully restored default settings!":"已成功恢复默认设置!",Close:"关闭",Tips:"提示","This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly":"此次更新更改了部分命令的ID,请点击此按钮修复命令以确保工具栏正常工作","Repair command ID":"修复命令ID","This will reset all your custom configurations":"这将重置您的所有自定义配置","Notice:":"注意","This update rebuilds the entire code, reducing resource consumption":"此次更新重构了全部代码,降低了资源占用","Optimized mobile usage, added canvas support, and added custom commands":"优化了移动端,增加了对canvas支持,增加了自定义命令","⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible":"⚠️此次更新不兼容2.x旧版本命令id,请点击【修复命令】进行兼容","⚠️If you want to restore the default settings, please click [Restore default settings]":"⚠️如果想恢复默认设置,请点击【恢复默认设置】","Please execute a editingToolbar format command first, then enable the format brush":"请先执行一个格式命令,然后再启用格式刷","Format brush ON! Select text to apply【":"格式刷已开启\n选中文本应用【","】format":"】格式\n点击鼠标右键关闭格式刷","Add to Toolbar":"添加到工具栏","This command is already in the toolbar.":"该命令已存在于工具栏中","Command added to toolbar":"命令已添加到工具栏","Add this command to the toolbar.":"添加该命令到工具栏","Callout Type":"Callout 类型",Title:"标题","Optional, leave blank for default title":"可选,留空则使用默认标题","Input title":"输入标题","Collapse State":"折叠状态",Open:"展开",Closed:"折叠",Content:"内容",Insert:"插入","Input content":"输入内容","Link Text":"链接文本","Link Alias":"链接别名","Link URL":"链接地址","Embed Content":"嵌入内容","Image Size":"图片尺寸","Insert New Line":"插入新行","Paste and Parse":"粘贴并解析","URL Format Error":"URL格式错误","Image Width":"图片宽度","Image Height":"图片高度","Insert a link on the next line":"在下一行插入链接","If it is an image, turn on":"如果是图片,请开启","Link Title(optional)":"链接标题(可选)",Alias:"别名",Optional:"可选","Default 0, format will keep the text selected":"默认0,格式化将保持文本选中","to insert":"插入","Latest Changes":"最新更新","📋View full changelog":"📋查看完整更新日志","Open changelog":"打开更新日志","Loading changelog...":"加载更新日志...","Open the complete changelog in your browser":"在浏览器中打开完整更新日志","Enable Multiple Configurations":"启用多配置","Enable different command configurations for each position style (following, top, fixed).":"启用每个位置样式的不同命令配置(following,top,fixed)","Currently editing commands for":"当前编辑的命令配置为:","position style":"样式","Current Configuration":"当前配置","Switch between different command configurations.":"切换不同的命令配置","Following Style":"跟随样式","Top Style":"顶部样式","Fixed Style":"固定样式","Mobile Style":"移动端样式",configuration:"配置","Deploy command to configurations":"部署命令到配置","All Configurations":"所有配置",Deploy:"部署","Command deployed to selected configurations":"命令已部署到选中的配置","No configuration selected for deployment":"没有选中的配置","Command already exists in selected configurations":"命令已存在于选中的配置","Command deployed to: ":"命令已部署到:","Command Deleted":"命令已删除","Confirm Delete?":"确认删除?","Are you sure you want to restore all settings to default? But custom commands will be preserved.":"您确定要将所有设置恢复为默认值吗?但自定义命令将被保留。","Successfully restored default settings! (Custom commands preserved)":"成功恢复默认设置!(自定义命令已保留)","This will reset all your custom configurations, but custom commands will be preserved":"这将重置您的所有自定义配置,但自定义命令将被保留","Import/Export":"导入/导出","Export Configuration":"导出配置","Export your toolbar configuration to share with others.":"导出您的工具栏配置以与他人共享",Export:"导出","Import Configuration":"导入配置","Import toolbar configuration from JSON.":"从JSON导入工具栏配置",Import:"导入","Usage Instructions":"使用说明","Export: Generate a JSON configuration that you can save or share.":"导出:生成可保存或共享的JSON配置","Import: Paste a previously exported JSON configuration.":"导入:粘贴先前导出的JSON配置","You can choose to export all settings, only toolbar commands, or only custom commands":"您可以选择导出所有设置、仅工具栏命令或仅自定义命令","When importing, the plugin will only update the settings included in the import data":"导入时,插件将仅更新导入数据中包含的设置","Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup.":"警告:导入配置将覆盖您当前的设置。建议先导出当前配置作为备份。","Export Type":"导出类型","Choose what to export":"选择要导出的内容","All Settings":"所有设置","Toolbar Commands Only":"仅工具栏命令","Custom Commands Only":"仅自定义命令","Export Content":"导出内容","Copy this content to share with others":"复制此内容以与他人共享","Loading...":"加载中...","Copy to Clipboard":"复制","Configuration copied to clipboard":"配置已复制到剪贴板","Failed to copy configuration":"复制配置失败","Paste the configuration JSON here":"在此处粘贴配置JSON","Paste configuration here...":"在此处粘贴配置...","Invalid import data":"无效的导入数据","Configuration imported successfully":"配置导入成功","No valid configuration found in import data":"导入数据中未找到有效配置","Failed to import configuration. Invalid format.":"导入配置失败。格式无效。","Import Mode":"导入模式","Choose how to import the configuration":"选择如何导入配置","Update Mode (Add new items and update existing ones)":"更新模式(添加新项目并更新现有项目)","Overwrite Mode (Replace all settings with imported ones)":"覆盖模式(用导入的配置替换所有设置)","Configuration imported successfully (Overwrite mode)":"配置导入成功(覆盖模式)","Configuration imported successfully (Update mode)":"配置导入成功(更新模式)","Warning: Overwrite mode will completely replace your current settings with the imported ones. Consider exporting your current configuration first as a backup.":"警告:覆盖模式将完全替换您当前的设置与导入的设置。建议先导出当前配置作为备份。","Warning: Update mode will add new items and update existing ones based on the imported configuration.":"警告:更新模式将根据导入的配置添加新项目并更新现有项目。","Add Format Command":"添加格式命令",Regex:"正则","Prefix/Suffix":"前缀/后缀","Insert Special Char":"增加特殊符号","Add Regex Command":"添加正则表达式命令","Switch Regex Command Window":"切换到正则命令窗口","Please select text first":"请先选中文本","Place the cursor in a paragraph or select text first.":"请将光标放在当前段落内,或先选中文本。","Current line is empty, please select text or move to a non-empty line":"当前行为空,请选中文本或移动到非空行","Use current line for regex commands":"正则命令使用当前行","When no text is selected, regex commands will use the current line instead of clipboard content":"当没有选中文本时,正则命令将使用当前行内容而不是剪贴板内容","The selected text does not meet the condition requirements":"选中的文本不满足条件要求","Regex command execution error: ":"正则表达式命令执行错误:","Copy code":"复制代码","Copied!":"已复制!","Explain the syntax of JavaScript regular expressions":"解释JavaScript正则表达式的语法","Apply regular expression replacement":"应用正则表达式替换","Conditional matching":"条件匹配","Complete regular expression code (copy to AI for explanation)":"完整正则表达式代码(复制到AI解释)","Error: ":"错误:","Regex pattern cannot be empty":"正则表达式不能为空","Command already exists":"命令已存在","Choose icon":"选择图标","URL to Markdown link":"URL转Markdown链接","Convert MM/DD/YYYY to YYYY-MM-DD":"将MM/DD/YYYY日期格式转换为YYYY-MM-DD","Add bold to keywords":"添加粗体到关键词","Format phone number":"格式化电话号码","Remove extra spaces":"删除多余空格","Convert HTML bold tags to Markdown format":"将HTML粗体标签转换为Markdown格式","Convert quoted text to quote block":"将引用的文本转换为引用块","Convert CSV to Markdown table row":"将CSV转换为Markdown表格行","Add uniform alias to Markdown links":"添加统一别名到Markdown链接","Delete empty lines (multiline mode)":"删除空行(多行模式)","Add list symbol to each line (multiline mode)":"添加列表符号到每行(多行模式)","If the text contains important, set the text highlight (conditional format)":"如果文本包含重要内容,设置文本高亮(条件格式)","Matching pattern":"匹配模式","Regex pattern to match":"正则表达式匹配","Replacement pattern (use $1, $2, etc. to reference capture groups)":"替换模式(使用$1, $2等引用捕获组)","Ignore case":"忽略大小写","Global replace":"全局替换","Multiline mode":"多行模式","Use condition":"使用条件","Condition pattern":"条件模式","Only apply custom command when text matches the condition":"仅在文本匹配条件时应用自定义命令","Must exist regular expression or text":"必须存在正则表达式或文本","Replacement pattern":"替换模式","Match case-insensitive":"匹配不区分大小写","^ and $ match the start and end of each line":"^ 和 $ 匹配每行的开始和结束","Replace all matches":"替换所有匹配",Command:"命令","Input example text to view the formatting effect of the command...":"输入示例文本以查看命令的格式化效果...",Description:"描述","[Example]":"[示例]","[Requirements]":"[要求]","[Output]":"[输出]","AI question template:":"AI问题模板:","I need to convert the url to a markdown format link":"我需要将URL转换为Markdown格式链接","For example, convert https://example.com to [https://example.com](https://example.com)":"例如,将https://example.com转换为[https://example.com](https://example.com)","Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)":"使用js正则表达式实现,并输出以下格式的参数(结果不需要用json转义)","[Description]":"[描述]","How to use AI to get regular expressions?":"如何使用AI获取正则表达式?","Regular expression examples":"正则表达式示例","Edit regular expression command":"编辑正则表达式命令","Add regular expression command":"添加正则表达式命令","Result:":"结果:","Example text:":"示例文本:",Preview:"预览",Result:"结果","Update Import":"增量导入","Overwrite Import":"覆盖导入","Importing configuration...":"正在导入配置...","Following Style Only":"仅Following样式","Top Style Only":"仅Top样式","Fixed Style Only":"仅Fixed样式","Mobile Style Only":"仅Mobile样式","Unknown import type":"未知导入类型","All Toolbar Commands":"所有工具栏命令","Following style commands successfully initialized":"Following样式初始化成功","Top style commands successfully initialized":"Top样式初始化成功","Fixed style commands successfully initialized":"Fixed样式初始化成功","Mobile style commands successfully initialized":"Mobile样式初始化成功","Commands initialized successfully":"命令初始化成功","Reset Commands":"重置命令","Are you sure you want to reset the current configuration?":"您确定要将当前配置进行重置吗?","Commands reset successfully":"命令重置成功","Initialize Commands":"初始化命令","Initialize commands to default settings":"初始化命令到默认设置","Reset commands to default settings":"重置命令到默认设置",Clear:"清除","Remove all commands from this configuration.":"清除当前样式下的所有命令","Are you sure you want to clear all commands under the current style?":"您确定要清除当前样式下的所有命令吗?","Current style commands have been cleared":"当前样式下的命令已清除","Manage Commands":"管理命令","Reset or clear all commands in this configuration":"重置或清除当前样式下的所有命令","Import Commands from Other Styles":"从其他样式导入命令","Copy commands from another style configuration.":"从另一个样式配置复制命令","Main menu only":"Main menu only","This import will update:":"此次导入将更新:","Custom commands":"自定义命令","Toolbar commands":"工具栏命令","All settings":"所有设置","Following style only":"跟随样式","Top style only":"顶部样式","Fixed style only":"固定样式","Mobile style only":"移动端样式","Main Menu Commands":"主菜单命令","Following Style Commands":"跟随样式命令","Top Style Commands":"顶部样式命令","Fixed Style Commands":"固定样式命令","Mobile Style Commands":"移动端样式命令","General settings":"常规设置","Please paste configuration data first":"请先粘贴配置数据","Invalid import data format":"无效的导入数据格式","Do you want to continue?":"您确定要继续吗?","Warning: Update mode will add new items and update existing ones.":"警告:更新模式将添加新项目并更新现有项目。","Warning: Overwrite mode will completely replace your current settings with the imported ones.":"警告:覆盖模式将完全替换您当前的设置与导入的设置。","Overwrite Mode (Replace settings with imported ones)":"覆盖模式(用导入的配置替换所有设置)","Warning: Overwrite mode will replace existing settings with imported ones.":"警告:覆盖模式将用导入的配置替换所有设置。","Enable Multiple Config":"启用多配置","One-click Clear":"一键清除","This import will:":"此次导入将:","Update general settings":"更新常规设置","Update Main Menu Commands":"更新主菜单命令","Update Custom Commands":"更新自定义命令","Update Following Style Commands":"更新跟随样式命令","Update Top Style Commands":"更新顶部样式命令","Update Fixed Style Commands":"更新固定样式命令","Clear all Main Menu Commands":"清除所有主菜单命令","Clear all Custom Commands":"清除所有自定义命令","Clear all Following Style Commands":"清除所有跟随样式命令","Clear all Top Style Commands":"清除所有顶部样式命令","Clear all Fixed Style Commands":"清除所有固定样式命令","Clear all Mobile Style Commands":"清除所有移动端样式命令","Set Multiple Config to:":"设置多配置为:",Enable:"启用",Disable:"禁用","Set Position Style to:":"设置位置样式为:",Following:"跟随",Top:"顶部",Fixed:"固定",Mobile:"移动端","All commands":"所有命令","⚠️ Overwrite mode will replace existing settings with imported ones.":"⚠️ 覆盖模式将用导入的配置替换所有设置。","ℹ️ Update mode will merge imported settings with existing ones.":"ℹ️ 更新模式将合并导入的设置与现有的设置。","Imported settings:":"导入的设置:","Imported commands:":"导入的命令:","Disable toolbar for this view":"禁用此视图的工具栏","Enable toolbar for this view":"启用此视图的工具栏","Manage all view types":"管理所有视图类型","Current View: ":"当前视图:","Appearance Style":"外观样式","Position Settings":"位置设置","Join the Community":"加入社区","Share your toolbar settings and styles in our":"分享您的工具栏设置和样式:","section!":"社区!","Get inspired by what others have created or showcase your own customizations.":"获取灵感或展示您的自定义设置。","Toolbar Preview (With a hypothetical command configuration.)":"工具栏预览(按钮仅供参考)","Toolbar Theme":"工具栏主题","Select a preset toolbar theme, automatically setting the background color, icon color, and size for the selected style.":"选择预设的工具栏主题,自动设置背景颜色、图标颜色和大小","Toolbar Background Color":"工具栏背景颜色","Set the background color of the toolbar.":"设置工具栏的背景颜色","Toolbar Icon Color":"工具栏图标颜色","Set the color of the toolbar icon.":"设置工具栏图标颜色","Toolbar Icon Size":"工具栏图标大小","Set the size of the toolbar icon (px); default: 18px":"设置工具栏图标大小(px)默认18px","Custom theme":"自定义主题","Fixed Position Offset":"固定位置偏移","Choose the offset of the Editing Toolbar in the fixed position.":"选择固定位置工具栏的偏移量。","Renumber List":"列表重新编号","Insert link":"插入链接","Please enter a URL first":"请先输入一个URL","Failed to fetch title for":"获取标题失败","Link Title (optional)":"链接标题(可选)","Unable to detect editor width":"无法检测编辑器宽度","Fit Editor Width":"自适应宽度","Fetch Remote Title":"获取URL标题","Please execute a format command or select format text first, then enable the format brush":"请先执行一个格式命令或选中带格式的文本,然后启用格式刷",Confirm:"确认","Use \\n to represent line breaks":"使用\\n表示换行符","Use ↵ to represent line breaks":"使用↵表示换行符","All commands have been removed.":"所有命令已被清除。","Top Toolbar":"顶部工具栏","Enable the toolbar positioned at the top.":"启用位于顶部的工具栏。","Following Toolbar":"跟随工具栏","Enable the toolbar that appears upon text selection.":"启用在选中文本时出现的工具栏。","Fixed Toolbar":"固定工具栏","Enable the toolbar whose position may be fixed where you please.":"启用可以固定在任意位置的工具栏。","Toolbar Settings":"工具栏设置","Choose which toolbar style's appearance you want to edit.":"选择要编辑外观的工具栏样式。","Vertical Split":"垂直分割","Text Enhancement Tools":"文本增强工具","Get Plain Text":"获取无语法文本","Full Half Converter":"全角半角转换","Insert Blank Lines":"插入空行","Remove Blank Lines":"删除空行","Split Lines":"拆分行","Dedupe Lines":"去除重复行","Add Prefix/Suffix":"添加前后缀","Number Lines (Custom)":"添加行号(自定义)","Trim Line Ends":"去除行首尾空格","Shrink Extra Spaces":"压缩多余空格","Remove All Whitespace":"移除所有空白","Extract Between Strings":"提取字符串之间内容","Merge Lines":"合并行","List to Table":"多级列表转表格","Table to List":"表格转多级列表","Line Operations":"行操作","Text Processing":"文本处理","Advanced Tools":"高级工具","Align Text Right":"右对齐",Bold:"加粗",Checklist:"清单","Clear Text Formatting":"清除文本格式",Copy:"复制",Cut:"剪切",Embed:"嵌入","Header 1":"标题 1","Header 2":"标题 2","Header 3":"标题 3","Header 4":"标题 4","Header 5":"标题 5","Header 6":"标题 6",Highlight:"高亮","Horizontal Divider":"水平分隔线","Indent list":"增加列表缩进","Inline Math":"行内公式",Italic:"斜体",Link:"链接",MathBlock:"数学公式块","Ordered List":"有序列表",Paste:"粘贴","Redo Edit":"重做编辑",Strikethrough:"删除线",Underline:"下划线","Undo Edit":"撤销编辑","Unindent List":"减少列表缩进","Unordered List":"无序列表","Insert Embed":"插入嵌入","Insert Link":"插入链接","Insert Tag":"插入标签","Insert Internal link":"插入内部链接","Insert Code":"插入代码","Insert Blockquote":"插入引用块","Insert Comment":"插入注释","Insert Callout":"插入 Callout","Insert MathBlock":"插入数学公式块","Insert Table":"插入表格","Swap Line Up":"上移当前行","Swap Line Down":"下移当前行","Attach File":"附加文件","Clear Formatting":"清除格式","Cycle List and Checklist":"循环切换列表与清单",Blockquote:"引用块",Callout:"Callout",Superscript:"上标",Subscript:"下标","Inline Code":"行内代码","Code Block":"代码块",Wikilink:"内部链接","Justify Text":"两端对齐","Align Text Left":"左对齐","Center Text":"居中对齐","Change Font Color":"更改字体颜色","Change Background Color":"更改背景颜色","Fullscreen Focus Mode":"全屏专注模式","Workplace Fullscreen":"工作区全屏","Renumber Ordered List":"重新编号有序列表","Toggle Format Brush":"切换格式刷","Toggle Fullscreen Focus Mode":"切换全屏专注模式","Toggle Workplace Fullscreen Focus":"切换工作区全屏","Enter prefix":"输入前缀","Enter suffix":"输入后缀",Delimiter:"分隔符","Enter delimiter (e.g., comma, tab)":"输入分隔符(例如:逗号、制表符)","Column Number":"列号","Enter column number (starting from 1)":"输入列号(从1开始)","Start String":"起始字符串","Enter start string":"输入起始字符串","End String":"结束字符串","Enter end string":"输入结束字符串","Number Lines Configuration":"行号配置","Start Number":"起始数字",Step:"步长",Separator:"分隔符","Merge Lines Settings":"合并行设置","Separator (leave empty for smart spacing)":"分隔符 (留空则开启智能中英间距)","e.g., comma, pipe, arrow":"例如: , 或 | 或 →","Plain text copied to clipboard":"无语法文本已复制到剪贴板","Whitespace cleaning completed":"空白字符清洗完成","List pattern detected, auto-split":"检测到列表模式,已自动拆分","No obvious separator or list pattern detected":"未识别到明显的分隔符或列表模式","Paste failed":"粘贴失败","Detected Chinese context: converted to full-width symbols":"检测为中文语境:已转换为全角符号","Detected code/English context: converted to half-width symbols":"检测为代码/英文语境:已转换为半角符号","Please select text to dedupe first":"请先选择要去重的文本","Deduplication completed, remaining":"去重完成,剩余",lines:"行","Prefix/suffix added":"添加前后缀完成","Please select text to number first":"请先选择要编号的文本","Numbering completed: starting from":"已完成编号:从","Please specify start or end string":"请指定起始或结束字符串",Extracted:"提取了",matches:"个匹配项","No matches found":"未找到匹配项","Extraction failed":"提取失败","Please select lines to merge first":"请先选择要合并的行","Merged with":"已按","Merge completed":"已完成合并",Item:"项目","Super conversion completed: context preserved and layout optimized":"超级转换完成:已保留上下文并优化排版","Please select a valid Markdown table":"请先选择有效的 Markdown 表格","Table converted to multi-level list":"表格已还原为多级列表","AI Editor":"AI 编辑","Enable AI Editor":"启用 AI 编辑","Enable AI editor features such as inline completion and selection rewrite.":"启用行内补全和选区改写等 AI 编辑能力。","Try AI Editing":"试试 AI 编辑","Unlock inline completion, rewrite, frontmatter, and canvas generation right inside your toolbar.":"在当前工具栏中直接解锁行内补全、改写、Frontmatter 生成和 Canvas 草稿等能力。","Sign in to PKMer AI for free managed AI, or use your own compatible model.":"登录 PKMer AI 即可优先使用免费托管 AI,也可以改用你自己的兼容模型。","What you get":"你将获得","Inline completion while you write":"写作过程中直接获得行内补全","Rewrite, summarize, and continue text in place":"原地改写、总结、续写当前内容","Generate frontmatter, lists, tables, and canvas drafts":"生成 Frontmatter、列表、表格与 Canvas 草稿","Before you enable AI":"启用前请知悉","The plugin itself does not intentionally store your note content.":"插件本身不会主动存储你的笔记内容。","AI requests are sent only to the provider you choose, such as PKMer AI or your custom model.":"AI 请求只会发送到你选择的服务提供方,例如 PKMer AI 或你的自定义模型。","Those requests remain subject to the provider privacy policy, terms, and model rules.":"这些请求仍然受到对应服务提供方的隐私政策、服务条款和模型规则约束。","You can turn AI off at any time in settings.":"你可以随时在设置中关闭 AI。","Agree & Enable AI":"同意并启用 AI","Not now":"暂不开启","AI editing is now enabled. The plugin does not intentionally store your note content, and requests follow your chosen provider policies.":"AI 编辑已启用。插件不会主动存储你的笔记内容,但请求仍会遵循你所选 AI 服务方的策略。",Enabled:"已启用",Disabled:"已停用","Completion Mode":"补全模式","Choose whether completion is triggered manually or automatically after a short pause.":"选择手动触发,或在短暂停顿后自动触发补全。","Enable Inline Completion":"启用行内补全","Show ghost text suggestions inside the editor.":"在编辑器中显示幽灵文字补全建议。","Completion Delay (ms)":"补全延迟(毫秒)","Delay before auto-triggering inline completion.":"自动触发补全前的等待时长。","Completion Max Tokens":"补全最大 Tokens","Upper bound for one inline completion response. Increase it if completions feel cut off.":"单次行内补全的长度上限。如果经常感觉补全被截断,可以适当调高。",Manual:"手动",Auto:"自动","Text Tools":"文本工具","AI Tools":"AI 工具","Inline Completion":"行内补全","Select text to see more tools":"选中文本后显示更多工具","AI Assistant":"AI 助手","AI module is ready":"AI 已就绪","Use AI enhancements directly from the toolbar.":"直接在工具栏中使用 AI 编辑增强。","Place the cursor for inline completion, or select text to unlock rewrite actions.":"将光标放在编辑位置可触发行内补全;选中文本后可使用改写类能力。","Selected text detected. Rewrite actions are ready.":"已检测到选中文本,可直接使用改写、续写、总结与解释功能。","AI Continue Writing":"AI 继续写作","AI Summarize Selection":"AI 总结选中文本","AI Explain Selection":"AI 解释选中文本","At Cursor":"光标位置","On Selection":"选中文本","Continue writing at the current cursor.":"基于当前光标位置继续写作。","Polish tone, wording, and structure.":"润色语气、措辞与结构。","Extend from the selected passage.":"基于选中内容继续往下写。","Turn long text into concise points.":"将较长内容提炼成精简要点。","Explain the meaning and key ideas.":"解释这段内容的含义与重点。","Use your own prompt on the selection.":"对选中内容使用你的自定义指令。","Custom Model":"自定义模型",Unavailable:"不可用","Checking...":"检查中...",AI:"AI","AI Complete":"AI补全","AI Rewrite":"AI改写","AI Continue":"AI续写","AI Summarize":"AI总结","AI Explain":"AI解释","AI Translate":"AI翻译","AI Tone":"AI语气","AI Fix":"AI纠错","AI Shorten":"AI缩写","AI Expand":"AI扩写","AI Simplify":"AI简化","AI Professional":"AI专业化","AI Casual":"AI口语化","AI Custom":"AI自定义",Custom:"自定义",Edit:"编辑",Tone:"语气",Translate:"翻译",Generate:"生成","Improve writing":"优化写作","Fix spelling & grammar":"修复拼写与语法","Make shorter":"缩短内容","Make longer":"扩展内容","Simplify language":"简化表达","Professional tone":"专业语气","Casual tone":"口语语气",English:"英文",Chinese:"中文",Japanese:"日文",German:"德语",French:"法语",Spanish:"西班牙语","Explain this":"解释这段内容",Summarize:"总结内容","Continue writing":"继续写作","Press Enter to send, Shift+Enter for newline, Esc to close.":"回车发送,Shift+Enter 换行,Esc 关闭。[[]]双链引用文档","Type [[]] to reference document content.":"输入 [[文档名]] 可引用文档内容。","Enter inserts a newline. Tap Send to submit.":"回车换行,点击发送提交。","Open AI Settings":"打开 AI 设置","Trigger AI Inline Completion":"触发 AI 行内补全","AI Improve Selection":"AI 优化选中文本","AI Custom Rewrite":"AI 自定义改写",History:"历史记录","No history":"暂无历史记录",Send:"发送","Selected text":"选中文本","Current document":"当前文档",characters:"字符","Custom Prompt Templates":"自定义提示词模板","Manage quick-access templates for custom AI prompts":"管理快速访问的自定义 AI 提示词模板","Edit Template":"编辑模板","Add Template":"添加模板","Template Name":"模板名称","Prompt Content":"提示词内容","Enter template name":"输入模板名称","Enter prompt content":"输入提示词内容","Template name and content cannot be empty":"模板名称和内容不能为空","Describe what you want AI to do...":"描述你想让 AI 做什么...","Manage Templates":"管理模板","Login to PKMer AI":"登录 PKMer AI","PKMer AI":"PKMer AI","Sign in to PKMer to use managed AI without manual model setup.":"登录 PKMer 后即可使用托管 AI,无需手动配置模型参数。","Log in to PKMer AI to get free AI features without manual model setup.":"登录 PKMer AI 后即可免费使用 AI 能力,无需手动配置模型参数。","Log in to PKMer AI and you can start using free AI features right away.":"登录 PKMer AI 后即可立即开始免费使用 AI 能力。","Need a PKMer AI account?":"还没有 PKMer AI 账号?","Open PKMer AI":"前往 PKMer AI","PKMer Account":"PKMer 账号",Login:"登录",Logout:"退出登录","Refresh Quota":"刷新额度","Check Quota":"查询额度","More Quota":"更多额度","PKMer AI is ready. You can use AI now and check your quota here.":"PKMer AI 已就绪。你现在可以直接使用 AI,并在这里查询额度。","PKMer managed defaults are used automatically after sign-in.":"登录后会自动使用 PKMer 的托管默认配置。","Checking current AI route...":"正在检查当前 AI 链路...","Current route: PKMer AI. If unavailable, it falls back to your custom model.":"当前链路:优先使用 PKMer AI;如果不可用,则回退到自定义模型。","Current route: PKMer AI.":"当前链路:PKMer AI。","Current route: Custom model.":"当前链路:自定义模型。","No provider available. Log in to PKMer or complete the custom model settings.":"当前没有可用 provider。请登录 PKMer,或完成自定义模型配置。","No provider available. Log in to PKMer to enable AI.":"当前没有可用 provider。请登录 PKMer 以启用 AI。","Unable to determine current AI route.":"无法判断当前 AI 链路。",Account:"账户",Complete:"补全","Quick Trigger":"快速触发","Improve Selection":"优化选中文本","Custom Rewrite":"自定义改写","Logout from PKMer":"退出 PKMer","Editor Features":"编辑增强功能","PKMer Model":"PKMer 模型","Choose models by task.":"按场景选择模型。",Mode:"模式","Light tasks use the light model. Complex tasks use the reasoning model.":"轻量任务用轻量模型,复杂任务用推理模型。",Completion:"补全",Rewrite:"改写",Reasoning:"推理",Structured:"结构化","Used for inline completion.":"用于行内补全。","Used for normal rewrite.":"用于普通改写。","Used for explain, summarize, and custom prompts.":"用于解释、总结和自定义指令。","Used for frontmatter and canvas.":"用于 Frontmatter 和 Canvas。","PKMer route only.":"仅对 PKMer 生效。","Light model":"轻量模型","Reasoning model":"推理模型","Inline completion":"行内补全","Configure inline completion and rewrite after your AI provider is ready.":"在 AI provider 准备就绪后,再配置行内补全和改写能力。","Custom Model (Optional)":"自定义模型(可选)","Custom model is used automatically when PKMer AI is unavailable.":"当 PKMer AI 不可用时,会自动使用你的自定义模型。","Enable Custom Model":"启用自定义模型","Use your own OpenAI-compatible provider as a fallback.":"将你自己的 OpenAI 兼容服务作为备用链路。","Custom API Format":"自定义接口格式","Choose whether the custom model uses an OpenAI-compatible endpoint or the native Ollama API.":"选择自定义模型使用 OpenAI 兼容接口,还是原生 Ollama API。","OpenAI-compatible":"OpenAI 兼容",Ollama:"Ollama","Custom API Base URL":"自定义 API 地址","OpenAI-compatible endpoint for your own provider.":"填写你自己的 OpenAI 兼容接口地址。","Native Ollama endpoint. The root URL, /api, /api/chat, or /api/generate are all supported.":"填写原生 Ollama 接口地址。支持根地址、/api、/api/chat 或 /api/generate。","Custom Model Name":"自定义模型名称","Model identifier used for inline completion and rewrite requests.":"用于行内补全和改写请求的模型标识。","Detected Ollama Models":"检测到的 Ollama 模型","Fetch available models from your Ollama service.":"从你的 Ollama 服务读取可用模型。","Choose a detected Ollama model to fill the model field.":"选择一个检测到的 Ollama 模型,自动填入模型名称。","Select a detected model":"选择一个检测到的模型","Custom API Key":"自定义 API Key","Optional for Ollama. Leave empty unless your gateway requires authentication.":"Ollama 模式下此项可选;除非你的网关要求鉴权,否则可以留空。","Stored securely in Obsidian secret storage.":"已安全存储在 Obsidian 的秘密存储区。","Will be stored securely in Obsidian secret storage.":"将安全存储在 Obsidian 的秘密存储区。","Current Obsidian version does not support secure secret storage.":"当前 Obsidian 版本不支持安全秘密存储。","Stored securely":"已安全存储","Enter API key":"输入 API Key",Temperature:"温度","Lower values are more stable; higher values are more creative.":"更低的值更稳定,更高的值更具创造性。","Test Connection":"测试连接","Send a lightweight request to verify your custom model settings.":"发送一次轻量请求,验证你的自定义模型配置是否可用。","Please fill in the required custom model settings first.":"请先填写必需的自定义模型配置。","No Ollama models found at this endpoint.":"这个 Ollama 地址下没有读取到任何模型。","Failed to load Ollama models:":"读取 Ollama 模型列表失败:","Please fill in Custom API Base URL, Custom Model Name, and Custom API Key first.":"请先填写自定义 API 地址、自定义模型名称和自定义 API Key。","Testing...":"测试中...","Testing custom model connection...":"正在测试自定义模型连接...","Custom model connection succeeded.":"自定义模型连接成功。","Custom model connection failed:":"自定义模型连接失败:","Unknown connection error.":"未知连接错误。","PKMer AI request failed because your quota is insufficient. Please get more quota in PKMer and try again.":"PKMer AI 请求失败:当前账号额度不足。请前往 PKMer 获取更多额度后重试。","Available Variables":"可用变量","Document path":"文件路径","Full document content":"文档全文",Date:"日期",Time:"时间","Date and time":"日期时间","Vault name":"仓库名","Linked note references":"双链引用","Use [[note name]] to reference the content of other notes.":"使用 [[文件名]] 引用其他笔记内容。","Manual Completion Shortcut":"手动补全快捷键","Default shortcut is Ctrl+J. You can customize it in Obsidian Hotkeys.":"默认快捷键是 Ctrl+J,你也可以在 Obsidian 快捷键设置中自定义。","Open Hotkey Settings":"打开快捷键设置","More Options":"更多选项",Advanced:"高级","Advanced AI settings are usually not needed.":"大多数情况下不需要调整这些高级 AI 设置。","Not logged in":"未登录","Logged in":"已登录",Quota:"额度","Unable to refresh PKMer quota.":"无法刷新 PKMer 额度。","PKMer quota refreshed:":"PKMer 额度已刷新:","AI features are disabled in settings.":"AI 功能已在设置中关闭。","Inline completion is disabled in settings.":"行内补全已在设置中关闭。","Current editor does not support AI inline completion.":"当前编辑器不支持 AI 行内补全。","Current editor does not support AI rewrite.":"当前编辑器不支持 AI 改写。","AI Custom Instruction":"AI 自定义指令",Instruction:"指令",Suggestions:"建议","Referenced notes":"引用文档","Please enter your custom AI instruction":"请输入你的自定义 AI 指令","Current Obsidian version does not support secure token storage.":"当前 Obsidian 版本不支持安全令牌存储。","Login cancelled or timed out.":"登录已取消或已超时。","PKMer login is already in progress. Please continue in the opened browser window.":"PKMer 登录已在进行中,请在已打开的浏览器窗口中继续完成授权。","Successfully logged in to PKMer!":"已成功登录 PKMer!","Failed to complete login. Please try again.":"登录流程未完成,请重试。","Login failed. Please try again.":"登录失败,请重试。","OAuth state mismatch. Please try logging in again.":"OAuth 状态校验失败:可能是另一个 Obsidian 窗口或库拦截了本次回调。请关闭多余库后重试。","Logged out from PKMer.":"已退出 PKMer 登录。","AI thinking":"AI 思考中","AI generating":"AI 正在生成","Ask AI to edit or generate...":"告诉 AI 你想编辑或生成什么…","Submit custom instruction":"提交自定义指令","AI is writing...":"AI 正在生成…","AI is generating":"AI 正在生成","AI suggestion":"AI 建议","Press Tab to accept":"按 Tab 接受补全",Replace:"替换","Insert below":"插入到下方","Try again":"重试",Discard:"丢弃","AI List":"AI 列表","AI Table":"AI 表格","AI Base":"AI Base","AI Canvas":"AI Canvas","AI Canvas Prompt":"AI 画板指令","AI Frontmatter":"AI 属性",Toolbox:"百宝箱","AI Toolbox":"AI 百宝箱","Convert to list":"转为列表","Convert to table":"转为表格","Convert to base":"转为 Base","Convert to canvas":"转为 Canvas","Generate frontmatter":"生成 Frontmatter","Insert at cursor":"插入到光标处",Create:"创建","Create & Embed":"创建并嵌入","AI file suggestion":"AI 文件建议","AI frontmatter suggestion":"AI 属性建议","AI Base File":"AI Base","AI Canvas File":"AI Canvas","Insert frontmatter":"插入 Frontmatter","Replace frontmatter":"替换 Frontmatter","Failed to apply frontmatter.":"应用 Frontmatter 失败。","Created AI file:":"已创建 AI 文件:","Failed to create AI file.":"创建 AI 文件失败。","AI file creation is unavailable.":"当前无法创建 AI 文件。","AI generated content is empty.":"AI 生成的内容为空。","AI returned invalid Canvas JSON.":"AI 返回的 Canvas JSON 无效。","AI Canvas Expand":"AI 画板扩展",Canvas:"画板","Expand Current Node":"扩展当前节点","Global Prompt":"全局指令","Expand current canvas node":"扩展当前节点指令","Canvas Tools":"画板工具","Canvas global prompt":"画板全局指令","Connect related nodes":"连接相关节点","Add missing branches":"补充缺失分支","Clarify structure":"理清结构层次","Generate next steps":"生成下一步行动","Add risks and dependencies":"补充风险与依赖","Selected nodes":"已选节点",Focused:"聚焦",Selected:"已选","Tidy selected layout":"整理已选布局","Reorganize the selected nodes into a clearer local hierarchy with tidy spacing and fewer edge crossings. Reuse current nodes instead of adding new ones.":"把已选节点整理成更清晰的局部层级,留出更整齐的间距并尽量减少连线交叉,复用当前节点而不是新增节点。","Connect selected nodes":"连接已选节点","Connect the selected nodes with the most meaningful relationships.":"为已选节点补充最有意义的关系连线。","Add the missing branches around the selected nodes.":"围绕已选节点补充缺失的分支。","Clarify the structure around the selected nodes and remove ambiguity.":"理清已选节点周围的结构,并消除歧义。","Generate the next actionable steps from the selected nodes.":"根据已选节点生成下一步可执行行动。","Add the risks, dependencies, and constraints related to the selected nodes.":"补充与已选节点相关的风险、依赖和约束。","Generate the next actionable steps from the current canvas node.":"根据当前画板节点生成下一步可执行行动。","Add the risks, dependencies, and constraints related to the current canvas node.":"补充与当前画板节点相关的风险、依赖和约束。","Add the missing branches around the current canvas node.":"围绕当前画板节点补充缺失的分支。","Clarify the structure around the current canvas node and remove ambiguity.":"理清当前画板节点周围的结构,并消除歧义。","Organize canvas layout":"整理画布布局","Reorganize this canvas into a clean hierarchy with tidy spacing, aligned sibling nodes, and fewer edge crossings. Reuse current nodes instead of adding new ones.":"把这张画布整理成清晰的层级结构,保持整齐间距、对齐同级节点,并尽量减少连线交叉,复用当前节点而不是新增节点。","Reduce edge crossings":"减少连线交叉","Reorganize the canvas to reduce edge crossings, keep related nodes close, and make the reading path clearer. Reuse current nodes instead of adding new ones.":"重组这张画布以减少连线交叉,让相关节点保持接近,并让阅读路径更清晰,复用当前节点而不是新增节点。","Canvas to article":"画板转文章","Convert this canvas into a polished Markdown article draft.":"把这张画板转换成一篇整理好的 Markdown 文章草稿。","Canvas to slides":"画板转幻灯片","Convert this canvas into Obsidian Slides Markdown.":"把这张画板转换成 Obsidian Slides 的 Markdown。","Convert the referenced content into a structured canvas with one central topic, main branches, supporting details, and explicit relations.":"把[[]]引用内容转成结构化画板,包含一个中心主题、主分支、支撑细节和明确关系。","Reorganize board":"重组整个画板","Reorganize the whole canvas into a clearer hierarchy with better grouping.":"把整张画板重组为更清晰的层级和分组。","Reorganize the existing canvas nodes into a clearer hierarchy and grouping. Reuse current nodes instead of adding new ones.":"把现有画板节点重组为更清晰的层级和分组,复用当前节点而不是新增节点。","Connect board clusters":"连接画板分区","Connect the related clusters across the whole canvas and add missing bridge nodes.":"连接整张画板中相关的分区,并补充缺失的桥接节点。","Summarize main narrative":"提炼主线叙事","Identify the main narrative of this canvas and improve the section flow.":"识别这张画板的主线叙事,并优化章节流转。","e.g. split into next steps, risks, dependencies":"例如:拆成下一步、风险点、依赖项","e.g. connect related nodes, add missing branches, clarify structure":"例如:连接相关节点、补充缺失分支、理清结构","e.g. tidy selected layout, connect selected nodes, add missing branches":"例如:整理已选布局、连接已选节点、补充缺失分支","e.g. tidy selected layout, connect selected nodes, add missing branches, convert [[a note]] to canvas":"例如:整理已选布局、连接已选节点、补充缺失分支、把 [[笔记]] 转成画板","e.g. reorganize the board, turn this canvas into an article, turn this canvas into Obsidian Slides":"例如:重组整张画板、把这张画板转成文章、把这张画板转成 Obsidian Slides","e.g. reorganize the board, turn this canvas into an article, turn [[a note]] into canvas":"例如:重组整张画板、把这张画板转成文章、把 [[笔记]] 转成画板","Canvas AI requires an active Canvas file.":"Canvas AI 需要当前处于一个已打开的画板文件中。","Please select a canvas node first.":"请先选中一个画板节点。","Please enter your canvas instruction.":"请输入你的画板指令。","Please reference at least one note with [[...]] before converting to canvas.":"转为画板前,请先用 [[...]] 引用至少一篇笔记。","Canvas AI is expanding the current node...":"Canvas AI 正在扩展当前节点…","Canvas AI is processing the board...":"Canvas AI 正在处理当前画板…","Canvas AI is reorganizing the board...":"Canvas AI 正在重组当前画板…","Canvas AI is structuring the referenced content...":"Canvas AI 正在把引用内容结构化为画板…","Canvas AI is drafting an article...":"Canvas AI 正在生成文章草稿…","Canvas AI is drafting slides...":"Canvas AI 正在生成幻灯片草稿…","Canvas AI added":"Canvas AI 已新增","Canvas AI updated the board:":"Canvas AI 已更新画板:","Canvas AI reorganized the board:":"Canvas AI 已重组画板:",nodes:"节点",links:"连线","nodes moved":"个节点已移动","links rebuilt":"条连线已重建","nodes to the board.":"个节点到画板。","Expand the current node into the most useful next neighboring nodes.":"将当前节点扩展为最有用的下一层相邻节点。","Canvas AI returned invalid JSON.":"Canvas AI 返回了无效的 JSON。","Canvas AI did not return any usable nodes.":"Canvas AI 没有返回可用节点。","Canvas AI did not return any usable actions.":"Canvas AI 没有返回可用操作。","Canvas AI did not return any usable Markdown.":"Canvas AI 没有返回可用的 Markdown。","The focused canvas node no longer exists.":"当前聚焦的画板节点已不存在。","AI Article Draft":"AI 文章草稿","AI Slides":"AI 幻灯片","Failed to read the current canvas data.":"读取当前画板数据失败。","Unable to allocate a file name for the generated artifact.":"无法为生成结果分配可用文件名。",Untitled:"未命名"},y={...b};const C={ar:{},cs:{},da:{},de:{},en:f,"en-gb":{"Editing Toolbar Append Method":"Editing Toolbar Append Method","Choose where Editing Toolbar will append upon regeneration. To see the change, hit the refresh button below, or in the status bar menu.":"Choose where Editing Toolbar will append upon regeneration. To see the change, hit the refresh button below, or in the status bar menu.","Editing Toolbar aesthetic":"Editing Toolbar aesthetic","Choose between a glass morphism, tiny and default style for Editing Toolbar. To see the change, hit the refresh button below, or in the status bar menu.":"Choose between a glass morphism, tiny and default style for Editing Toolbar. To see the change, hit the refresh button below, or in the status bar menu.","Editing Toolbar position":"Editing Toolbar position","Choose between fixed position or cursor following mode.":"Choose between fixed position, cursor following or Top mode.","Editing Toolbar Columns":"Editing Toolbar Columns","Choose the number of columns per row to display on Editing Toolbar.":"Choose the number of columns per row to display on Editing Toolbar.","Editing Toolbar refresh":"Editing Toolbar refresh","Editing Toolbar Commands":"Editing Toolbar Commands","Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.":"Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.","Format Brush Off!":"Format Brush Off!","Hide & Show":"Hide & Show","Editing Toolbar will only refresh automatically after you have either added or deleted a command from it. To see UI changes to editingToolbar (above settings changes) use the refresh button. If you forget to refresh in settings, no worries. There is also a refresh button in the editingToolbar status bar menu.":"Editing Toolbar will only refresh automatically after you have either added or deleted a command from it. To see UI changes to editingToolbar (above settings changes) use the refresh button. If you forget to refresh in settings, no worries. There is also a refresh button in the editingToolbar status bar menu.","Font-Color formatting brush ON!":"Font-Colour formatting brush ON!",More:"More","Font Colors":"Font Colours","Format Brush":"Format Brush","Background color":"Background colour",Refresh:"Refresh",Add:"Add",Delete:"Delete","Change Command Name":"Change Command Name","Add Submenu":"Add Submenu","Add Separator":"Add Separator","Enter the icon code, it looks like format":"Enter the icon code, it looks like format","Please enter a new name: ":"Please enter a new name: ","Drag the slider to move the position":"Drag the slider to move the position","Plugin Settings":"Plugin Settings","Background-color formatting brush ON!":"Background-colour formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush","Clear formatting brush ON!":"Clear formatting brush ON!","Clear formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush":"Clear formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush","The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden":"The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden","Editing Toolbar Auto-hide":"Editing Toolbar Auto-hide","Editing Toolbar Centred Display":"Editing Toolbar Centred Display","Whether the toolbar is centred or full-width, the default is full-width.":"Whether the toolbar is centred or full-width, the default is full-width.","Custom Backgroud Color":"Custom Backgroud Colour","Custom Font Color":"Custom Font Colour","🎨 Set Custom Background":"🎨 Set Custom Background","🖌️ Set Custom Font Color":"🖌️ Set Custom Font Colour","Click on the picker to adjust the color":"Click on the picker to adjust the colour","Mobile Enabled or Not":"Mobile Enabled or Not","Whether to enable the plugin for the mobile client, the default is enabled.":"Whether to enable the plugin for the mobile client, the default is enabled.","Whether to enable on mobile devices with device width less than 768px.":"Whether to enable on mobile devices with device width less than 768px, the default is disable.",Reset:"Reset",Fix:"Fix","Fix Editing Toolbar":"Fix Editing Toolbar",General:"General",Appearance:"Appearance",Commands:"Commands","Choose between fixed position or cursor following mode":"Choose between fixed position, cursor following or Top mode.","Add and manage commands":"Add and manage commands","Choose where Editing Toolbar will append upon regeneration.":"Choose where Editing Toolbar will append upon regeneration.","Whether to enable on mobile devices with device width less than 768px":"Whether to enable on mobile devices with device width less than 768px","Choose between a glass morphism, tiny and default style.":"Choose between a glass morphism, tiny and default style.","Refresh Toolbar":"Refresh Toolbar","Add Command":"Add Command",Settings:"Settings","Position Style":"Position Style",Columns:"Columns","Drag to Adjust Position":"Drag to Adjust Position","Vertical Position":"Vertical Position","Horizontal Position":"Horizontal Position","Toolbar Position":"Toolbar Position","Choose an icon":"Choose an icon","Search for an icon...":"Search for an icon...",All:"All",Obsidian:"Obsidian",Glyph:"Glyph",Custom:"Custom","Choose a command":"Choose a command","The command":"The command","already exists":"already exists","Enter the icon code, format as ":"Enter the icon code, format as ","No matching icons found":"No matching icons found","Custom Commands":"Custom Commands","Toolbar Commands":"Toolbar Commands",ID:"ID",Prefix:"Prefix",Suffix:"Suffix",Pattern:"Pattern","Custom Format Commands":"Custom Format Commands","Add, edit or delete custom format commands.":"Add, edit or delete custom format commands.",Edit:"Edit","Command ID":"Command ID",'Unique identifier, no spaces, e.g.: "my-custom-format"':'Unique identifier, no spaces, e.g.: "my-custom-format"',"Displayed name in toolbar and menu":"Displayed name in toolbar and menu","Add content before selected text":"Add content before selected text","Add content after selected text":"Add content after selected text","Character offset of cursor after formatting":"Character offset of cursor after formatting","Line offset of cursor after formatting":"Line offset of cursor after formatting","Whether to insert at the beginning of the next line":"Whether to insert at the beginning of the next line","Command icon (click to select)":"Command icon (click to select)","Choose Icon":"Choose Icon",Save:"Save",Cancel:"Cancel","Edit Custom Command":"Edit Custom Command","Add Custom Command":"Add Custom Command","Command ID and command name cannot be empty":"Command ID and command name cannot be empty","Command ID cannot contain spaces":"Command ID cannot contain spaces",'Command ID "${this.commandId}" already exists':'Command ID "${this.commandId}" already exists',"Cursor Position Offset":"Cursor Position Offset","Line Offset":"Line Offset","Line Head Format":"Line Head Format",Icon:"Icon","Command Name":"Command Name","Are you sure you want to restore all settings to default? This will lose all your custom configurations.":"Are you sure you want to restore all settings to default? This will lose all your custom configurations.","Restore default":"Restore default","Restore default settings":"Restore default settings","🔄Restore default settings":"🔄Restore default settings","🔧Data repair":"🔧Data repair","Command IDs have been successfully repaired!":"Command IDs have been successfully repaired!","No command IDs need to be repaired":"No command IDs need to be repaired","Error repairing command IDs, please check the console for details":"Error repairing command IDs, please check the console for details","Error restoring default settings, please check the console for details":"Error restoring default settings, please check the console for details","Successfully restored default settings!":"Successfully restored default settings!",Close:"Close",Tips:"Tips","This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly":"This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly","Repair command ID":"Repair command ID","This will reset all your custom configurations":"This will reset all your custom configurations","Notice:":"Notice:","This update rebuilds the entire code, reducing resource consumption":"This update rebuilds the entire code, reducing resource consumption","Optimized mobile usage, added canvas support, and added custom commands":"Optimized mobile usage, added canvas support, and added custom commands","⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible":"⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible","⚠️If you want to restore the default settings, please click [Restore default settings]":"⚠️If you want to restore the default settings, please click [Restore default settings]","Please execute a editingToolbar format command first, then enable the format brush":"Please execute a editingToolbar format command first, then enable the format brush","Format brush ON! Select text to apply【":"Format brush ON! Select text to apply【","】format":"】format\nClick the mouse middle or right key to close the formatting-brush","Add to Toolbar":"Add to Toolbar","This command is already in the toolbar.":"This command is already in the toolbar.","Command added to toolbar":"Command added to toolbar","Add this command to the toolbar.":"Add this command to the toolbar.","Callout Type":"Callout Type",Title:"Title","Optional, leave blank for default title":"Optional, leave blank for default title","Input title":"Input title","Collapse State":"Collapse State",Open:"Open",Closed:"Closed",Content:"Content",Insert:"Insert",Default:"Default","Input content":"Input content","Link Text":"Link Text","Link Alias":"Link Alias","Link URL":"Link URL","Embed Content":"Embed Content","Image Size":"Image Size","Insert New Line":"Insert New Line","Paste and Parse":"Paste and Parse","URL Format Error":"URL Format Error","Image Width":"Image Width","Image Height":"Image Height","If it is an image, turn on":"If it is an image, turn on","Insert a link on the next line":"Insert a link on the next line","Link Title(optional)":"Link Title(optional)",Alias:"Alias",Optional:"Optional","Default 0, format will keep the text selected":"Default 0, format will keep the text selected","to insert":"to insert","Latest Changes":"Latest Changes","📋View full changelog":"📋View full changelog","Open changelog":"Open changelog","Loading changelog...":"Loading changelog...","Open the complete changelog in your browser":"Open the complete changelog in your browser","Enable Multiple Configurations":"Enable Multiple Configurations","Enable different command configurations for each position style (following, top, fixed).":"Enable different command configurations for each position style (following, top, fixed).","Currently editing commands for":"Currently editing commands for","position style":"position style","Current Configuration":"Current Configuration","Switch between different command configurations.":"Switch between different command configurations.","Following Style":"Following Style","Top Style":"Top Style","Fixed Style":"Fixed Style","Mobile Style":"Mobile Style",configuration:"configuration","Deploy command to configurations":"Deploy command to configurations","All Configurations":"All Configurations",Deploy:"Deploy","Command deployed to selected configurations":"Command deployed to selected configurations","No configuration selected for deployment":"No configuration selected for deployment","Command already exists in selected configurations":"Command already exists in selected configurations","Command deployed to: ":"Command deployed to: ","Command Deleted.":"Command Deleted.","Confirm Delete?":"Confirm Delete?",Confirm:"Confirm","Are you sure you want to restore all settings to default? But custom commands will be preserved.":"Are you sure you want to restore all settings to default? But custom commands will be preserved.","Successfully restored default settings! (Custom commands preserved)":"Successfully restored default settings! (Custom commands preserved)","This will reset all your custom configurations, but custom commands will be preserved":"This will reset all your custom configurations, but custom commands will be preserved","Import/Export":"Import/Export","Export Configuration":"Export Configuration","Export your toolbar configuration to share with others.":"Export your toolbar configuration to share with others.",Export:"Export","Import Configuration":"Import Configuration","Import toolbar configuration from JSON.":"Import toolbar configuration from JSON.",Import:"Import","Usage Instructions":"Usage Instructions","Export: Generate a JSON configuration that you can save or share.":"Export: Generate a JSON configuration that you can save or share.","Import: Paste a previously exported JSON configuration.":"Import: Paste a previously exported JSON configuration.","You can choose to export all settings, only toolbar commands, or only custom commands":"You can choose to export all settings, only toolbar commands, or only custom commands","When importing, the plugin will only update the settings included in the import data":"When importing, the plugin will only update the settings included in the import data","Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup.":"Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup.","Export Type":"Export Type","Choose what to export":"Choose what to export","All Settings":"All Settings","Toolbar Commands Only":"Toolbar Commands Only","Custom Commands Only":"Custom Commands Only","Export Content":"Export Content","Copy this content to share with others":"Copy this content to share with others","Loading...":"Loading...","Copy to Clipboard":"Copy","Configuration copied to clipboard":"Configuration copied to clipboard","Failed to copy configuration":"Failed to copy configuration","Paste the configuration JSON here":"Paste the configuration JSON here","Paste configuration here...":"Paste configuration here...","Invalid import data":"Invalid import data","Configuration imported successfully":"Configuration imported successfully","No valid configuration found in import data":"No valid configuration found in import data","Failed to import configuration. Invalid format.":"Failed to import configuration. Invalid format.","Import Mode":"Import Mode","Choose how to import the configuration":"Choose how to import the configuration","Update Mode (Add new items and update existing ones)":"Update Mode (Add new items and update existing ones)","Overwrite Mode (Replace all settings with imported ones)":"Overwrite Mode (Replace all settings with imported ones)","Configuration imported successfully (Overwrite mode)":"Configuration imported successfully (Overwrite mode)","Configuration imported successfully (Update mode)":"Configuration imported successfully (Update mode)","Warning: Overwrite mode will completely replace your current settings with the imported ones. Consider exporting your current configuration first as a backup.":"Warning: Overwrite mode will completely replace your current settings with the imported ones. Consider exporting your current configuration first as a backup.","Warning: Update mode will add new items and update existing ones based on the imported configuration.":"Warning: Update mode will add new items and update existing ones based on the imported configuration.","Add Format Command":"Add Format Command",Regex:"Regex","Prefix/Suffix":"Prefix/Suffix","Insert Special Char":"Insert Special Char","Add Regex Command":"Add Regex Command","Switch Regex Command Window":"Switch Regex Command Window","Please select text first":"Please select text first","The selected text does not meet the condition requirements":"The selected text does not meet the condition requirements","Regex command execution error: ":"Regex command execution error: ","Copy code":"Copy code","Copied!":"Copied!","Explain the syntax of JavaScript regular expressions":"Explain the syntax of JavaScript regular expressions","Apply regular expression replacement":"Apply regular expression replacement","Conditional matching":"Conditional matching","Complete regular expression code (copy to AI for explanation)":"Complete regular expression code (copy to AI for explanation)","Error: ":"Error: ","Regex pattern cannot be empty":"Regex pattern cannot be empty","Command already exists":"Command already exists","Choose icon":"Choose icon","URL to Markdown link":"URL to Markdown link","Convert MM/DD/YYYY to YYYY-MM-DD":"Convert MM/DD/YYYY to YYYY-MM-DD","Add bold to keywords":"Add bold to keywords","Format phone number":"Format phone number","Remove extra spaces":"Remove extra spaces","Convert HTML bold tags to Markdown format":"Convert HTML bold tags to Markdown format","Convert quoted text to quote block":"Convert quoted text to quote block","Convert CSV to Markdown table row":"Convert CSV to Markdown table row","Add uniform alias to Markdown links":"Add uniform alias to Markdown links","Delete empty lines (multiline mode)":"Delete empty lines (multiline mode)","Add list symbol to each line (multiline mode)":"Add list symbol to each line (multiline mode)","If the text contains important, set the text highlight (conditional format)":"If the text contains important, set the text highlight (conditional format)","Matching pattern":"Matching pattern","Regex pattern to match":"Regex pattern to match","Replacement pattern (use $1, $2, etc. to reference capture groups)":"Replacement pattern (use $1, $2, etc. to reference capture groups)","Ignore case":"Ignore case","Global replace":"Global replace","Multiline mode":"Multiline mode","Use condition":"Use condition","Condition pattern":"Condition pattern","Only apply custom command when text matches the condition":"Only apply custom command when text matches the condition","Must exist regular expression or text":"Must exist regular expression or text","Replacement pattern":"Replacement pattern","Match case-insensitive":"Match case-insensitive","^ and $ match the start and end of each line":"^ and $ match the start and end of each line","Replace all matches":"Replace all matches",Command:"Command","Input example text to view the formatting effect of the command...":"Input example text to view the formatting effect of the command...",Description:"Description","[Example]":"[Example]","[Requirements]":"[Requirements]","[Output]":"[Output]","AI question template:":"AI question template:","I need to convert the url to a markdown format link":"I need to convert the url to a markdown format link","For example, convert https://example.com to [https://example.com](https://example.com)":"For example, convert https://example.com to [https://example.com](https://example.com)","Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)":"Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)","[Description]":"[Description]","How to use AI to get regular expressions?":"How to use AI to get regular expressions?","Regular expression examples":"Regular expression examples","Edit regular expression command":"Edit regular expression command","Add regular expression command":"Add regular expression command","Result: ":"Result: ","Example text:":"Example text:",Preview:"Preview",Result:"Result","Please select text or copy text to clipboard first":"Please select text or copy text to clipboard first","Overwrite Import":"Overwrite Import","Update Import":"Update Import","Importing configuration...":"Importing configuration...","Following Style Only":"Following Style Only","Top Style Only":"Top Style Only","Fixed Style Only":"Fixed Style Only","Mobile Style Only":"Mobile Style Only","Unknown import type":"Unknown import type","All Toolbar Commands":"All Toolbar Commands","Initialize Commands":"Initialize Commands","Copy commands from the main menu configuration":"Copy commands from the main menu configuration","Initialize commands to default settings":"Initialize commands to default settings","Reset Commands":"Reset Commands","Commands reset successfully":"Commands reset successfully","Following style commands initialized from current menu commands":"Following style commands initialized from current menu commands","Commands initialized successfully":"Commands initialized successfully","Reset to Menu Commands":"Reset to Menu Commands","Are you sure you want to reset the current configuration?":"Are you sure you want to reset the current configuration?","Following style commands successfully initialized":"Following style commands successfully initialized","Top style commands successfully initialized":"Top style commands successfully initialized","Fixed style commands successfully initialized":"Fixed style commands successfully initialized","Mobile style commands successfully initialized":"Mobile style commands successfully initialized","Reset commands to default settings":"Reset commands to default settings",Clear:"Clear","Remove all commands from this configuration.":"Remove all commands from this configuration.","Are you sure you want to clear all commands under the current style?":"Are you sure you want to clear all commands under the current style?","Current style commands have been cleared":"Current style commands have been cleared","Manage Commands":"Manage Commands","Reset or clear all commands in this configuration":"Reset or clear all commands in this configuration","One-click Clear":"One-click Clear","Import Commands from Other Styles":"Import Commands from Other Styles","Copy commands from another style configuration.":"Copy commands from another style configuration.","Main menu only":"Main menu only","This import will update:":"This import will update:","Custom commands":"Custom commands","Toolbar commands":"Toolbar commands","General settings":"General settings","Please paste configuration data first":"Please paste configuration data first","Invalid import data format":"Invalid import data format","Import From":"Import From","This import will:":"This import will:","Update general settings":"Update general settings","Update Main Menu Commands":"Update Main Menu Commands","Update Custom Commands":"Update Custom Commands","Update Following Style Commands":"Update Following Style Commands","Update Top Style Commands":"Update Top Style Commands","Update Fixed Style Commands":"Update Fixed Style Commands","Update Mobile Style Commands":"Update Mobile Style Commands","Clear all Main Menu Commands":"Clear all Main Menu Commands","Clear all Custom Commands":"Clear all Custom Commands","Clear all Following Style Commands":"Clear all Following Style Commands","Clear all Top Style Commands":"Clear all Top Style Commands","Clear all Fixed Style Commands":"Clear all Fixed Style Commands","Clear all Mobile Style Commands":"Clear all Mobile Style Commands","Overwrite Mode (Replace settings with imported ones)":"Overwrite Mode (Replace settings with imported ones)","Warning: Overwrite mode will replace existing settings with imported ones.":"Warning: Overwrite mode will replace existing settings with imported ones.","Warning: Update mode will add new items and update existing ones.":"Warning: Update mode will add new items and update existing ones.","Enable Multiple Config":"Enable Multiple Config","Set Multiple Config to:":"Set Multiple Config to:",Enable:"Enable",Disable:"Disable","Set Position Style to:":"Set Position Style to:",Following:"Following",Top:"Top",Fixed:"Fixed",Mobile:"Mobile","All commands":"All commands","⚠️ Overwrite mode will replace existing settings with imported ones.":"⚠️ Overwrite mode will replace existing settings with imported ones.","ℹ️ Update mode will merge imported settings with existing ones.":"ℹ️ Update mode will merge imported settings with existing ones.","Do you want to continue?":"Do you want to continue?","Imported settings:":"Imported settings:","Imported commands:":"Imported commands:","Disable toolbar for this view":"Disable toolbar for this view","Enable toolbar for this view":"Enable toolbar for this view","Manage all view types":"Manage all view types","Current View: ":"Current View: ","Appearance Style":"Appearance Style","Position Settings":"Position Settings","All commands have been removed.":"All commands have been removed.","Join the Community":"Join the Community","Share your toolbar settings and styles in our":"Share your toolbar settings and styles in our","Get inspired by what others have created or showcase your own customizations.":"Get inspired by what others have created or showcase your own customisations.","Toolbar Preview (With a hypothetical command configuration.)":"Toolbar Preview (With a hypothetical command configuration.)","Toolbar Theme":"Toolbar Theme","Select a preset toolbar theme, automatically setting the background color, icon color, and size for the selected style.":"Select a preset toolbar theme, automatically setting the background colour, icon colour, and size for the selected style.","Toolbar Background Color":"Toolbar Background Colour","Set the background color of the toolbar.":"Set the background colour of the toolbar.","Toolbar Icon Color":"Toolbar Icon Colour","Set the color of the toolbar icon.":"Set the colour of the toolbar icon.","Toolbar Icon Size":"Toolbar Icon Size","Set the size of the toolbar icon (px); default: 18px":"Set the size of the toolbar icon (px); default: 18px","Custom Theme":"Custom Theme","Fixed Position Offset":"Fixed Position Offset","Choose the offset of the Editing Toolbar in the fixed position.":"Choose the offset of the Editing Toolbar in the fixed position.","Renumber List":"Renumber List","Fetch Remote Title":"Fetch Remote Title","Please enter a URL first":"Please enter a URL first","Failed to fetch title for":"Failed to fetch title for","Link Title (optional)":"Link Title (optional)","Unable to detect editor width":"Unable to detect editor width","Fit Editor Width":"Fit Editor Width","Please execute a format command or select format text first, then enable the format brush":"Please execute a format command or select format text first, then enable the format brush","Use \\n to represent line breaks":"Use \\n to represent line breaks","Use ↵ to represent line breaks":"Use ↵ to represent line breaks","Top Toolbar":"Top Toolbar","Enable the toolbar positioned at the top.":"Enable the toolbar positioned at the top.","Following Toolbar":"Following Toolbar","Enable the toolbar that appears upon text selection.":"Enable the toolbar that appears upon text selection.","Fixed Toolbar":"Fixed Toolbar","Enable the toolbar whose position may be fixed where you please.":"Enable the toolbar whose position may be fixed where you please.","Toolbar Settings":"Toolbar Settings","Choose which toolbar style's appearance you want to edit.":"Choose which toolbar style's appearance you want to edit.","Vertical Split":"Vertical Split"},es:{},fr:{},hi:{},id:{},it:{},ja:{},ko:{},nl:{},nn:{},pl:{},pt:{},"pt-br":{"Editing Toolbar Append Method":"Método de Inserção da Barra de Ferramentas","Choose where Editing Toolbar will append upon regeneration. To see the change, hit the refresh button below, or in the status bar menu.":"Escolha onde a Barra de Ferramentas será inserida ao regenerar. Para ver a alteração, clique no botão de atualização abaixo, ou no menu da barra de status.","Editing Toolbar aesthetic":"Estilo da Barra de Ferramentas","Choose between a glass morphism, tiny and default style for Editing Toolbar. To see the change, hit the refresh button below, or in the status bar menu.":"Escolha entre um estilo de vidro, pequeno e padrão para a Barra de Ferramentas. Para ver a alteração, clique no botão de atualização abaixo, ou no menu da barra de status.","Editing Toolbar position":"Posição da Barra de Ferramentas","Choose between fixed position or cursor following mode.":"Escolha entre posição fixa ou modo contextual.","Editing Toolbar Columns":"Colunas da Barra de Ferramentas","Choose the number of columns per row to display on Editing Toolbar.":"Escolha o número de colunas por linha para exibir na Barra de Ferramentas.","Editing Toolbar refresh":"Atualizar Barra de Ferramentas","Editing Toolbar Commands":"Comandos da Barra de Ferramentas","Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.":"Adicione um comando na Barra de Ferramentas a partir da biblioteca de comandos do Obsidian. Para reordenar os comandos, arraste e solte os itens do comando. Para deletar, use o botão de deletar à direita do item do comando. A Barra de Ferramentas não atualizará automaticamente após reordenar os comandos. Use o botão de atualização acima.","Format Brush Off!":"Pincel de formatação desligado!","Hide & Show":"Ocultar & Mostrar","Editing Toolbar will only refresh automatically after you have either added or deleted a command from it. To see UI changes to editingToolbar (above settings changes) use the refresh button. If you forget to refresh in settings, no worries. There is also a refresh button in the editingToolbar status bar menu.":"A barra de edição só atualizará automaticamente após você ter adicionado ou deletado um comando dela. Para ver as alterações na interface do editingToolbar (alterações acima das configurações) use o botão de atualização. Se você esquecer de atualizar nas configurações, não se preocupe. Também há um botão de atualização na barra de status da Barra de Edição.","Font-Color formatting brush ON!":"Pincel de cor da fonte ativado!",More:"Mais","Font Colors":"Cores de Texto","Format Brush":"Pincel de formatação","Background Color":"Cor de Fundo",Refresh:"Atualizar",Add:"Adicionar",Delete:"Deletar","Change Command Name":"Alterar Nome do Comando","Add Submenu":"Adicionar Submenu","Add Separator":"Adicionar Separador","Enter the icon code, it looks like format":"Digite o código do ícone, parece com o formato ","Please enter a new name: ":"Por favor, digite um novo nome: ","Drag the slider to move the position":"Arraste o slider para mover a posição","Plugin Settings":"Configurações do Plugin","Background-color formatting brush ON!":"Pincel de cor de fundo ativado!\nClique com o botão do meio ou direito do mouse para sair do modo.","Clear formatting brush ON!":"Pincel de formatação ativado!","Clear formatting brush ON!\nClick the mouse middle or right key to close the formatting-brush":"Pincel de formatação ativado!\nClique com o botão do meio ou direito do mouse para sair do modo de formatação.","The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden":"A barra de ferramentas é exibida quando o mouse passa por cima dela, caso contrário, ela é automaticamente oculta.","Editing Toolbar Auto-hide":"Auto-ocultação da Barra de Ferramentas","Editing Toolbar Centred Display":"Exibição Centralizada da Barra de Ferramentas","Whether the toolbar is centred or full-width, the default is full-width.":"Define se a barra de ferramentas é centralizada ou ocupa toda a largura. O padrão é largura completa.","Custom Backgroud Color":"Cor de Fundo Personalizada","Custom Font Color":"Cor de Texto Personalizada","🎨 Set Custom Background":"🎨 Definir Cor de Fundo Personalizada","🖌️ Set Custom Font Color":"🖌️ Definir Cor de Texto Personalizada","Click on the picker to adjust the color":"Clique no seletor para ajustar a cor","Mobile Enabled or Not":"Habilitar em dispositivos móveis","Whether to enable the plugin for the mobile client, the default is enabled.":"Habilitar o plugin para dispositivos móveis, o padrão é habilitado.","Whether to enable on mobile devices with device width less than 768px.":"Habilitar o plugin para dispositivos móveis com largura de dispositivo menor que 768px, o padrão é desabilitado.",Reset:"Reiniciar",Fix:"Ajustar","Fix Editing Toolbar":"Ajustar Barra de Ferramentas",General:"Geral",Appearance:"Aparência",Commands:"Comandos","Choose between fixed position or cursor following mode":"Escolha entre posição fixa ou modo contextual.","Add and manage commands":"Adicionar e gerenciar comandos","Choose where Editing Toolbar will append upon regeneration.":"Escolha onde a Barra de Ferramentas será inserida ao regenerar.","Whether to enable on mobile devices with device width less than 768px":"Habilitar o plugin para dispositivos móveis com largura de dispositivo menor que 768px","Choose between a glass morphism, tiny and default style.":"Escolha entre um estilo de vidro, pequeno e padrão.","Refresh Toolbar":"Atualizar Barra de Ferramentas","Add Command":"Adicionar Comando",Settings:"Configurações","Position Style":"Estilo de Posição",Columns:"Colunas","Drag to Adjust Position":"Arraste para ajustar a posição","Vertical Position":"Posição Vertical","Horizontal Position":"Posição Horizontal","Toolbar Position":"Posição da Barra de Ferramentas","Choose an icon":"Escolha um ícone","Search for an icon...":"Pesquisar por um ícone...",All:"Todos",Obsidian:"Obsidian",Glyph:"Ícone",Custom:"Personalizado","Choose a command":"Escolha um comando","The command":"O comando","already exists":"já existe","Enter the icon code, format as ":"Digite o código do ícone, formato como ","No matching icons found":"Nenhum ícone correspondente encontrado","Custom Commands":"Comandos Personalizados","Toolbar Commands":"Comandos da Barra de Ferramentas",ID:"ID",Prefix:"Prefixo",Suffix:"Sufixo",Pattern:"Padrão","Custom Format Commands":"Comandos de Formatação Personalizados","Add, edit or delete custom format commands.":"Adicionar, editar ou deletar comandos de formatação personalizados.",Edit:"Editar","Command ID":"ID do Comando",'Unique identifier, no spaces, e.g.: "my-custom-format"':'Identificador único, sem espaços, exemplo: "meu-comando-personalizado"',"Displayed name in toolbar and menu":"Nome exibido na barra de ferramentas e menu","Add content before selected text":"Adicionar conteúdo antes do texto selecionado","Add content after selected text":"Adicionar conteúdo depois do texto selecionado","Character offset of cursor after formatting":"Deslocamento do cursor após a formatação","Line offset of cursor after formatting":"Deslocamento da linha do cursor após a formatação","Whether to insert at the beginning of the next line":"Inserir no início da próxima linha","Command icon (click to select)":"Ícone do comando (clique para selecionar)","Choose Icon":"Escolha um ícone",Save:"Salvar",Cancel:"Cancelar","Edit Custom Command":"Editar Comando Personalizado","Add Custom Command":"Adicionar Comando Personalizado","Command ID and command name cannot be empty":"ID do Comando e nome do comando não podem ser vazios","Command ID cannot contain spaces":"ID do Comando não pode conter espaços",'Command ID "${this.commandId}" already exists':'ID do Comando "${this.commandId}" já existe',"Cursor Position Offset":"Deslocamento do cursor","Line Offset":"Deslocamento da linha","Line Head Format":"Formatação de linha inicial",Icon:"Ícone","Command Name":"Nome do Comando","Are you sure you want to restore all settings to default? This will lose all your custom configurations.":"Tem certeza que deseja restaurar todas as configurações para a configuração padrão? Isso irá apagar todas as suas configurações personalizadas.","Restore default":"Restaurar padrão","Restore default settings":"Restaurar configurações padrão","🔄Restore default settings":"🔄Restaurar configurações padrão","🔧Data repair":"🔧Reparar dados","Command IDs have been successfully repaired!":"IDs de comando foram reparadas com sucesso!","No command IDs need to be repaired":"Nenhuma ID de comando precisa ser reparada","Error repairing command IDs, please check the console for details":"Erro ao reparar IDs de comando, por favor verifique o console para mais detalhes","Error restoring default settings, please check the console for details":"Erro ao restaurar configurações padrão, por favor verifique o console para mais detalhes","Successfully restored default settings!":"Configurações padrão restauradas com sucesso!",Close:"Fechar",Tips:"Dicas","This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly":"Esta atualização alterou o ID de alguns comandos. Clique neste botão para repará-los e garantir que a barra de ferramentas funcione corretamente.","Repair command ID":"Reparar ID do Comando","This will reset all your custom configurations":"Isso irá resetar todas as suas configurações personalizadas","Notice:":"Nota:","This update rebuilds the entire code, reducing resource consumption":"Esta atualização recompila todo o código, reduzindo o consumo de recursos","Optimized mobile usage, added canvas support, and added custom commands":"Otimizado para uso em dispositivos móveis, adicionando suporte para o canvas e adicionado comandos personalizados","⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible":"⚠️Esta atualização não é compatível com IDs de comando da versão 2.x. Clique em [Reparar comando] para corrigir a compatibilidade.","⚠️If you want to restore the default settings, please click [Restore default settings]":"⚠️Se você quiser restaurar as configurações padrão, por favor clique em [Restaurar configurações padrão].","Please execute a editingToolbar format command first, then enable the format brush":"Execute um comando de formatação da barra de edição antes de ativar o pincel de formatação.","Format brush ON! Select text to apply【":"Pincel de formatação ativado! Selecione o texto para aplicar【","】format":"】\nClique com o botão do meio ou direito do mouse para sair do modo de formatação.","Add to Toolbar":"Adicionar à Barra de Edição","This command is already in the toolbar.":"Este comando já está na barra de ferramentas.","Command added to toolbar":"Comando adicionado à barra de ferramentas","Add this command to the toolbar.":"Adicionar este comando à barra de ferramentas.","Callout Type":"Tipo de Callout",Title:"Título","Optional, leave blank for default title":"Opcional, deixe em branco para o título padrão","Input title":"Digite o título","Collapse State":"Estado de Colapso",Open:"Abrir",Closed:"Fechado",Content:"Conteúdo",Insert:"Inserir",Default:"Padrão","Input content":"Digite o conteúdo","Link Text":"Texto do Link","Link Alias":"Alias do Link","Link URL":"URL do Link","Embed Content":"Conteúdo Embutido","Image Size":"Tamanho da Imagem","Insert New Line":"Inserir Nova Linha","Paste and Parse":"Colar e Analisar","URL Format Error":"Erro de Formato de URL","Image Width":"Largura da Imagem","Image Height":"Altura da Imagem","If it is an image, turn on":"Se for uma imagem, ative","Insert a link on the next line":"Inserir um link na próxima linha","Link Title(optional)":"Título do Link(opcional)",Alias:"Alias",Optional:"Opcional","Default 0, format will keep the text selected":"Padrão 0, o formato manterá o texto selecionado","to insert":"para inserir","Latest Changes":"Últimas Alterações","📋View full changelog":"📋Ver o registro de alterações completo","Open changelog":"Abrir registro de alterações","Loading changelog...":"Carregando registro de alterações...","Open the complete changelog in your browser":"Abrir o registro de alterações completo no seu navegador","Enable Multiple Configurations":"Habilitar Múltiplas Configurações","Enable different command configurations for each position style (following, top, fixed).":"Habilitar configurações de comandos diferentes para cada modo de posição (following, top, fixed).","Currently editing commands for":"Atualmente editando comandos para","position style":"estilo de posição","Current Configuration":"Configuração Atual","Switch between different command configurations.":"Trocar entre configurações de comando diferentes.","Following Style":"Estilo Contextual","Top Style":"Estilo de Topo","Fixed Style":"Estilo Fixo","Mobile Style":"Estilo Mobile",configuration:"configuração","Deploy command to configurations":"Implantar comando para configurações","All Configurations":"Todas as Configurações",Deploy:"Implantar","Command deployed to selected configurations":"Comando implantado para configurações selecionadas","No configuration selected for deployment":"Nenhuma configuração selecionada para implantação","Command already exists in selected configurations":"Comando já existe nas configurações selecionadas","Command deployed to: ":"Comando implantado para: ","Command Deleted":"Comando Deletado","Confirm Delete?":"Confirmar Deleção?",Confirm:"Confirmar","Are you sure you want to restore all settings to default? But custom commands will be preserved.":"Tem certeza que deseja restaurar todas as configurações para a configuração padrão? Mas os comandos personalizados serão preservados.","Successfully restored default settings! (Custom commands preserved)":"Configurações padrão restauradas com sucesso! (Comandos personalizados preservados)","This will reset all your custom configurations, but custom commands will be preserved":"Isso redefinirá todas as suas configurações personalizadas, mas os comandos personalizados serão preservados.","Import/Export":"Importar/Exportar","Export Configuration":"Exportar Configuração","Export your toolbar configuration to share with others.":"Exportar sua configuração da barra de ferramentas para compartilhar com outros.",Export:"Exportar","Import Configuration":"Configuração de Importação","Import toolbar configuration from JSON.":"Importar configuração da barra de ferramentas em formato JSON.",Import:"Importar","Usage Instructions":"Instruções de Uso","Export: Generate a JSON configuration that you can save or share.":"Exportar: Gerar uma configuração em formato JSON para salvar ou compartilhar.","Import: Paste a previously exported JSON configuration.":"Importar: Colar uma configuração em formato JSON exportada anteriormente.","You can choose to export all settings, only toolbar commands, or only custom commands":"Você pode escolher exportar todas as configurações, apenas comandos da barra de ferramentas, ou apenas comandos personalizados","When importing, the plugin will only update the settings included in the import data":"Ao importar, o plugin irá atualizar apenas as configurações incluídas nos dados de importação","Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup.":"Aviso: A importação de configuração irá sobrescrever suas configurações atuais. Considere exportar sua configuração atual primeiro como backup.","Export Type":"Tipo de Exportação","Choose what to export":"Escolha o que exportar","All Settings":"Todas as Configurações","Toolbar Commands Only":"Apenas Comandos da Barra de Ferramentas","Custom Commands Only":"Apenas Comandos Personalizados","Export Content":"Conteúdo de Exportação","Copy this content to share with others":"Copiar este conteúdo para compartilhar com outros","Loading...":"Carregando...","Copy to Clipboard":"Copiar para a área de transferência","Configuration copied to clipboard":"Configuração copiada para a área de transferência","Failed to copy configuration":"Falha ao copiar configuração","Paste the configuration JSON here":"Colar a configuração JSON aqui","Paste configuration here...":"Colar configuração aqui...","Invalid import data":"Dados de importação inválidos","Configuration imported successfully":"Configuração importada com sucesso","No valid configuration found in import data":"Nenhuma configuração válida encontrada nos dados de importação","Failed to import configuration. Invalid format.":"Falha ao importar configuração. Formato inválido.","Import Mode":"Modo de Importação","Choose how to import the configuration":"Escolha como importar a configuração","Update Mode (Add new items and update existing ones)":"Modo de Atualização (Adicionar novos itens e atualizar os existentes)","Overwrite Mode (Replace all settings with imported ones)":"Modo de Sobrescrever (Substituir todas as configurações com as importadas)","Configuration imported successfully (Overwrite mode)":"Configuração importada com sucesso (Modo de Sobrescrever)","Configuration imported successfully (Update mode)":"Configuração importada com sucesso (Modo de Atualização)","Warning: Overwrite mode will completely replace your current settings with the imported ones. Consider exporting your current configuration first as a backup.":"Aviso: O modo de sobrescrever irá substituir todas as suas configurações atuais com as importadas. Considere exportar sua configuração atual primeiro como backup.","Warning: Update mode will add new items and update existing ones based on the imported configuration.":"Aviso: O modo de atualização irá adicionar novos itens e atualizar os existentes com base na configuração importada.","Add Format Command":"Adicionar formatação",Regex:"Regex","Prefix/Suffix":"Prefixo/Sufixo","Insert Special Char":"Inserir Caractere Especial","Add Regex Command":"Adicionar regex","Switch Regex Command Window":"Trocar para Janela de Comando de Regex","Please select text first":"Por favor selecione o texto primeiro","Current line is empty, please select text or move to a non-empty line":"A linha atual está vazia, por favor selecione o texto ou mova para uma linha não vazia","Use current line for regex commands":"Usar a linha atual para comandos de regex","When no text is selected, regex commands will use the current line instead of clipboard content":"Quando não houver texto selecionado, comandos de regex usarão a linha atual em vez do conteúdo da área de transferência","The selected text does not meet the condition requirements":"O texto selecionado não atende aos requisitos da condição","Regex command execution error: ":"Erro ao executar comando de regex: ","Copy code":"Copiar código","Copied!":"Copiado!","Explain the syntax of JavaScript regular expressions":"Explicar a sintaxe das expressões regulares em JavaScript","Apply regular expression replacement":"Aplicar substituição de expressão regular","Conditional matching":"Ocorrência Condicional","Complete regular expression code (copy to AI for explanation)":"Código de expressão regular completo (copiar para explicação com IA)","Error: ":"Erro: ","Regex pattern cannot be empty":"Padrão de regex não pode estar vazio","Command already exists":"Comando já existe","Choose icon":"Escolha um ícone","URL to Markdown link":"URL para Link Markdown","Convert MM/DD/YYYY to YYYY-MM-DD":"Converter MM/DD/YYYY para YYYY-MM-DD","Add bold to keywords":"Adicionar negrito a palavras-chave","Format phone number":"Formatar número de telefone","Remove extra spaces":"Remover espaços extra","Convert HTML bold tags to Markdown format":"Converter tags HTML de negrito para negrito em Markdown","Convert quoted text to quote block":"Converter texto citado para bloco de citação","Convert CSV to Markdown table row":"Converter CSV para linha de tabela Markdown","Add uniform alias to Markdown links":"Adicionar um alias uniforme aos links Markdown","Delete empty lines (multiline mode)":"Deletar linhas vazias (modo multilinha)","Add list symbol to each line (multiline mode)":"Adicionar símbolo de lista a cada linha (modo multilinha)","If the text contains important, set the text highlight (conditional format)":'Se o texto contiver "importante", aplicar destaque ao texto (formatação condicional)',"Matching pattern":"Padrão de correspondência","Regex pattern to match":"Padrão de regex a ser correspondido","Replacement pattern (use $1, $2, etc. to reference capture groups)":"Padrão de substituição (use $1, $2, etc. para referenciar grupos de captura)","Ignore case":"Ignorar maiúsculas e minúsculas","Global replace":"Substituir globalmente","Multiline mode":"Modo multilinha","Use condition":"Usar condição","Condition pattern":"Padrão de condição","Only apply custom command when text matches the condition":"Aplicar o comando personalizado apenas quando o texto corresponder à condição","Must exist regular expression or text":"Deve existir expressão regular ou texto","Replacement pattern":"Padrão de substituição","Match case-insensitive":"Ignorar maiúsculas e minúsculas","^ and $ match the start and end of each line":"^ e $ correspondem ao início e ao fim de cada linha","Replace all matches":"Substituir todas as ocorrências",Command:"Comando","Input example text to view the formatting effect of the command...":"Digite um texto de exemplo para visualizar o efeito do comando...",Description:"Descrição","[Example]":"[Exemplo]","[Requirements]":"[Requisitos]","[Output]":"[Saída]","AI question template:":"Modelo de pergunta para IA:","I need to convert the url to a markdown format link":"Preciso converter a URL para um link no formato Markdown","For example, convert https://example.com to [https://example.com](https://example.com)":"Por exemplo, converter https://exemplo.com para [https://exemplo.com](https://exemplo.com)","Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)":"Usar expressões regulares em JavaScript para implementar e gerar os parâmetros no formato abaixo (o resultado não precisa ser escapado em JSON).","[Description]":"[Descrição]","How to use AI to get regular expressions?":"Como usar IA para obter expressões regulares?","Regular expression examples":"Exemplos de expressões regulares","Edit regular expression command":"Editar comando de expressão regular","Add regular expression command":"Adicionar comando de expressão regular","Result: ":"Resultado: ","Example text:":"Texto de exemplo:",Preview:"Visualizar",Result:"Resultado","Please select text or copy text to clipboard first":"Por favor selecione o texto ou copie o texto para a área de transferência primeiro","Overwrite Import":"Sobrescrever Importação","Update Import":"Atualizar Importação","Importing configuration...":"Importando configuração...","Following Style Only":"Disponível apenas no modo contextual","Top Style Only":"Disponível apenas no modo de topo","Fixed Style Only":"Disponível apenas no modo fixo","Mobile Style Only":"Disponível apenas no modo mobile","Unknown import type":"Tipo de importação desconhecido","All Toolbar Commands":"Todos os Comandos da Barra de Ferramentas","Initialize Commands":"Inicializar Comandos","Copy commands from the main menu configuration":"Copiar comandos do menu principal","Initialize commands to default settings":"Inicializar comandos para configurações padrão","Reset Commands":"Resetar Comandos","Commands reset successfully":"Comandos resetados com sucesso","Following style commands initialized from current menu commands":"Comandos de estilo contextual inicializados a partir dos comandos do menu principal","Commands initialized successfully":"Comandos inicializados com sucesso","Reset to Menu Commands":"Resetar para Comandos do Menu","Are you sure you want to reset the current configuration?":"Tem certeza que deseja resetar a configuração atual?","Following style commands successfully initialized":"Comandos de estilo contextual inicializados com sucesso","Top style commands successfully initialized":"Comandos de estilo de topo inicializados com sucesso","Fixed style commands successfully initialized":"Comandos de estilo fixo inicializados com sucesso","Mobile style commands successfully initialized":"Comandos de estilo mobile inicializados com sucesso","Reset commands to default settings":"Resetar comandos para configurações padrão",Clear:"Limpar","Remove all commands from this configuration.":"Remover todos os comandos desta configuração.","Are you sure you want to clear all commands under the current style?":"Tem certeza que deseja limpar todos os comandos sob o estilo atual?","Current style commands have been cleared":"Comandos do estilo atual foram limpos","Manage Commands":"Gerenciar Comandos","Reset or clear all commands in this configuration":"Resetar ou limpar todos os comandos nesta configuração","One-click Clear":"Limpar com um clique","Import Commands from Other Styles":"Importar Comandos de Outros Estilos","Copy commands from another style configuration.":"Copiar comandos de outra configuração de estilo.","Main menu only":"Apenas para o menu principal","This import will update:":"Esta importação irá atualizar:","Custom commands":"Comandos Personalizados","Toolbar commands":"Comandos da Barra de Ferramentas","General settings":"Configurações Gerais","Please paste configuration data first":"Por favor coloque os dados da configuração primeiro","Invalid import data format":"Formato de dados de importação inválido","Import From":"Importar de","This import will:":"Esta importação irá:","Update general settings":"Atualizar configurações gerais","Update Main Menu Commands":"Atualizar Comandos do Menu Principal","Update Custom Commands":"Atualizar Comandos Personalizados","Update Following Style Commands":"Atualizar Comandos de Estilo Contextual","Update Top Style Commands":"Atualizar Comandos de Estilo de Topo","Update Fixed Style Commands":"Atualizar Comandos de Estilo Fixo","Update Mobile Style Commands":"Atualizar Comandos de Estilo Mobile","Clear all Main Menu Commands":"Limpar todos os Comandos do Menu Principal","Clear all Custom Commands":"Limpar todos os Comandos Personalizados","Clear all Following Style Commands":"Limpar todos os Comandos de Estilo Contextual","Clear all Top Style Commands":"Limpar todos os Comandos de Estilo de Topo","Clear all Fixed Style Commands":"Limpar todos os Comandos de Estilo Fixo","Clear all Mobile Style Commands":"Limpar todos os Comandos de Estilo Mobile","Overwrite Mode (Replace settings with imported ones)":"Modo de Sobrescrever (Substituir configurações com as importadas)","Warning: Overwrite mode will replace existing settings with imported ones.":"Aviso: O modo de sobrescrever irá substituir todas as suas configurações atuais com as importadas.","Warning: Update mode will add new items and update existing ones.":"Aviso: O modo de atualização irá adicionar novos itens e atualizar os existentes com base na configuração importada.","Enable Multiple Config":"Habilitar Múltiplas Configurações","Set Multiple Config to:":"Definir Múltiplas Configurações para:",Enable:"Habilitar",Disable:"Desabilitar","Set Position Style to:":"Definir Estilo de Posição para:",Following:"Contextual",Top:"Topo",Fixed:"Fixo",Mobile:"Mobile","All commands":"Todos os Comandos","⚠️ Overwrite mode will replace existing settings with imported ones.":"⚠️ O modo de sobrescrever irá substituir todas as suas configurações atuais com as importadas.","ℹ️ Update mode will merge imported settings with existing ones.":"ℹ️ O modo de atualização irá adicionar novos itens e atualizar os existentes com base na configuração importada.","Do you want to continue?":"Deseja continuar?","Imported settings:":"Configurações importadas:","Imported commands:":"Comandos importados:","Disable toolbar for this view":"Desabilitar barra de ferramentas para esta visualização","Enable toolbar for this view":"Habilitar barra de ferramentas para esta visualização","Manage all view types":"Gerenciar todos os tipos de visualização","Current View: ":"Visualização Atual: ","Appearance Style":"Estilo de Aparência","Position Settings":"Configurações de Posição","All commands have been removed.":"Todos os comandos foram removidos.","Join the Community":"Juntar-se à Comunidade","Share your toolbar settings and styles in our":"Compartilhe suas configurações e estilos da barra de ferramentas em nossa","Get inspired by what others have created or showcase your own customizations.":"Inspire-se com o que outros criaram ou mostre suas próprias personalizações.","Toolbar Preview (With a hypothetical command configuration.)":"Visualização da barra de ferramentas (com uma configuração de comandos hipotética).","Toolbar Theme":"Estilo da Barra de Ferramentas","Select a preset toolbar theme, automatically setting the background color, icon color, and size for the selected style.":"Selecione um estilo de barra de ferramentas pré-definido, automaticamente definindo a cor de fundo, cor do ícone e tamanho para o estilo selecionado.","Toolbar Background Color":"Cor de Fundo da Barra de Ferramentas","Set the background color of the toolbar.":"Definir a cor de fundo da barra de ferramentas.","Toolbar Icon Color":"Cor do Ícone da Barra de Ferramentas","Set the color of the toolbar icon.":"Definir a cor do ícone da barra de ferramentas.","Toolbar Icon Size":"Tamanho do Ícone da Barra de Ferramentas","Set the size of the toolbar icon (px); default: 18px":"Definir o tamanho do ícone da barra de ferramentas (px); padrão: 18px","Custom Theme":"Estilo Personalizado","Fixed Position Offset":"Deslocamento Fixo","Choose the offset of the Editing Toolbar in the fixed position.":"Escolha o deslocamento da Barra de Edição na posição fixa.","Renumber List":"Renumerar Lista","Fetch Remote Title":"Obter Título Remoto","Please enter a URL first":"Por favor digite uma URL primeiro","Failed to fetch title for":"Falha ao obter título para","Link Title (optional)":"Título do Link (opcional)","Unable to detect editor width":"Não foi possível detectar a largura do editor","Fit Editor Width":"Ajustar Largura do Editor","Please execute a format command or select format text first, then enable the format brush":"Execute um comando de formatação ou selecione um texto já formatado antes de ativar o pincel de formatação.","Use \\n to represent line breaks":"Use \\n para representar quebras de linha","Use ↵ to represent line breaks":"Use ↵ para representar quebras de linha","Top Toolbar":"Barra de Ferramentas de Topo","Enable the toolbar positioned at the top.":"Habilitar a barra de ferramentas posicionada no topo.","Following Toolbar":"Barra de Ferramentas Contextual","Enable the toolbar that appears upon text selection.":"Habilitar a barra de ferramentas que aparece ao selecionar texto.","Fixed Toolbar":"Barra de Ferramentas Fixa","Enable the toolbar whose position may be fixed where you please.":"Habilitar a barra de ferramentas cuja posição pode ser fixa onde você preferir.","Toolbar Settings":"Configurações da Barra de Ferramentas","Choose which toolbar style's appearance you want to edit.":"Escolha qual estilo de barra de ferramentas você deseja editar.","Vertical Split":"Divisão Vertical"},ro:{},ru:{},tr:{},"zh-cn":b,"zh-tw":y}[e.moment.locale()];function v(e){return"string"!=typeof e||0===e.length?"":C&&C[e]||f[e]||e}class w extends e.FuzzySuggestModal{constructor(e,t,o=!1,n,i){super(e.app),this.customCallback=null,this.plugin=e,this.command=t,this.issub=o,this.customCallback=n||null,this.setPlaceholder(v("Choose an icon")),this.currentEditingConfig=i||""}capitalJoin(e){return e.split(" ").map(e=>e[0].toUpperCase()+e.substring(1)).join(" ")}getItems(){return i}getItemText(e){return this.capitalJoin(e.replace("feather-","").replace("remix-","").replace("bx-","").replace(/([A-Z])/g," $1").trim().replace(/-/gi," "))}renderSuggestion(t,o){const n=createSpan({cls:"editingToolbarIconPick"});o.appendChild(n),e.setIcon(n,t.item),super.renderSuggestion(t,o)}async onChooseItem(e){if("Custom"===e)return this.customCallback?void new x(this.app,this.plugin,{id:this.command.id,name:this.command.name,icon:""},this.issub,e=>{this.customCallback(e)}).open():void new x(this.app,this.plugin,this.command,this.issub,null,this.currentEditingConfig).open();if(this.customCallback)return void this.customCallback(e);const t=this.plugin.getCurrentCommands(this.currentEditingConfig);if(this.command.icon){let o=s(this.plugin,this.command,this.issub,t);this.issub?t[o.index].SubmenuCommands[o.subindex].icon=e:t[o.index].icon=e,this.plugin.updateCurrentCommands(t,this.currentEditingConfig)}else this.command.icon=e,t.push(this.command),this.plugin.updateCurrentCommands(t,this.currentEditingConfig);await this.plugin.saveSettings(),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100),console.log(`%c命令 '${this.command.name}' 已添加到编辑工具栏`,"color: Violet")}}class x extends e.Modal{constructor(e,t,o,n,i,a){super(e),this.customCallback=null,this.plugin=t,this.item=o,this.issub=n,this.customCallback=i||null,this.currentEditingConfig=a||"",this.containerEl.addClass("editingToolbar-Modal"),this.containerEl.addClass("customicon")}onOpen(){const{contentEl:e}=this;e.createEl("b",{text:v("Enter the icon code, format as ")});const t=document.createElement("textarea");t.className="wideInputPromptInputEl",t.placeholder="",t.value=this.item.icon||"",t.style.width="100%",t.style.height="200px",e.appendChild(t),t.addEventListener("input",async()=>{const e=t.value;if(this.customCallback)return void(this.item.icon=e);this.item.icon=e;const o=this.plugin.getCurrentCommands(this.currentEditingConfig),n=s(this.plugin,this.item,this.issub,o);if(this.issub){let t=n.subindex;-1===t?this.plugin.settings.menuCommands[n.index].SubmenuCommands.push(this.item):this.plugin.settings.menuCommands[n.index].SubmenuCommands[t].icon=e}else{let e=n.index;-1===e?this.plugin.settings.menuCommands.push(this.item):this.plugin.settings.menuCommands[e].icon=this.item.icon}await this.plugin.saveSettings()}),this.submitEnterCallback&&t.addEventListener("keydown",this.submitEnterCallback)}onClose(){const{contentEl:e}=this;e.empty(),this.customCallback?this.customCallback(this.item.icon||""):setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)}}class k extends e.FuzzySuggestModal{constructor(e,t){super(e.app),this.plugin=e,this.app,this.setPlaceholder(v("Choose a command")),this.currentEditingConfig=t||""}getItems(){return app.commands.listCommands()}getItemText(e){return v(e.name)}async onChooseItem(t){const o=this.plugin.getCurrentCommands(this.currentEditingConfig);o.findIndex(e=>e.id==t.id)>-1?new e.Notice(v("The command")+v(t.name)+v("already exists"),3e3):t.icon?(o.push(t),this.plugin.updateCurrentCommands(o,this.currentEditingConfig),await this.plugin.saveSettings(),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100),console.log(`%c命令 '${t.name}' 已添加到编辑工具栏`,"color: Violet")):new w(this.plugin,t,!1,null,this.currentEditingConfig).open()}}class S extends e.Modal{constructor(e,t,o,n,i){super(t.app),this.plugin=t,this.item=o,this.issub=n,this.currentEditingConfig=i||"",this.containerEl.addClass("editingToolbar-Modal"),this.containerEl.addClass("changename")}onOpen(){const{contentEl:t}=this;t.createEl("b",{text:v("Please enter a new name: ")});const o=new e.TextComponent(t);o.inputEl.classList.add("InputPromptInputEl"),o.setPlaceholder("").setValue(this.item.name??"").onChange(e.debounce(async e=>{const t=this.plugin.getCurrentCommands(this.currentEditingConfig);let o=s(this.plugin,this.item,this.issub,t);if(this.item.name=e,this.issub){let n=o.subindex;-1===n?t[o.index].SubmenuCommands.push(this.item):t[o.index].SubmenuCommands[n].name=e}else{let e=o.index;-1===e?t.push(this.item):t[e].name=this.item.name}this.plugin.updateCurrentCommands(t),await this.plugin.saveSettings()},100,!0)).inputEl.addEventListener("keydown",this.submitEnterCallback)}onClose(){const{contentEl:e}=this;e.empty(),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)}}class T extends e.Modal{constructor(e,t){super(t.app),this.needSave=!1,this.plugin=t,this.containerEl.addClass("editingToolbar-Modal")}onOpen(){const{contentEl:t}=this;t.createEl("p",{text:v("Drag the slider to move the position")});const o=t.createDiv({cls:"slider-container"}),n=o.createDiv({cls:"vertical-slider-container"});n.createEl("p",{text:v("Vertical Position")});const i=o.createDiv({cls:"horizontal-slider-container"});i.createEl("p",{text:v("Horizontal Position")});const a=o.createDiv({cls:"columns-slider-container"});a.createEl("p",{text:v("Editing Toolbar Columns")});const s=document.body.clientHeight,r=document.body.clientWidth,l=Math.floor(s/3),c=-Math.floor(s),d=Math.floor(r/2),u=-Math.floor(r/2),m=new e.SliderComponent(n).setLimits(c,l,5).setValue(this.plugin.settings.verticalPosition||0).onChange(e.debounce(e=>{this.needSave=!0,this.plugin.settings.verticalPosition=e,h(this.plugin.settings)},100,!0)).setDynamicTooltip(),p=new e.SliderComponent(i).setLimits(u,d,10).setValue(this.plugin.settings.horizontalPosition||0).onChange(e.debounce(e=>{this.needSave=!0,this.plugin.settings.horizontalPosition=e,g(this.plugin.settings)},100,!0)).setDynamicTooltip(),f=new e.SliderComponent(a).setLimits(1,32,1).setValue(this.plugin.settings.cMenuNumRows||12).onChange(e.debounce(async e=>{this.needSave=!0,this.plugin.settings.cMenuNumRows=e,await this.plugin.saveSettings(),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)},100,!0)).setDynamicTooltip();o.createDiv({cls:"reset-container"}).createEl("button",{text:v("Reset"),cls:"reset-button"}).addEventListener("click",()=>{this.needSave=!0,m.setValue(0),p.setValue(0),f.setValue(12),this.plugin.settings.verticalPosition=0,this.plugin.settings.horizontalPosition=0,this.plugin.settings.cMenuNumRows=12,h(this.plugin.settings),g(this.plugin.settings)})}async onClose(){const{contentEl:e}=this;e.empty(),this.needSave&&await this.plugin.saveSettings()}}function E(){return e.moment.locale()}function I(e=E()){const t=e.toLowerCase();return"zh"===t||t.startsWith("zh-")}const A=[{value:"04-fast",label:"04-fast"},{value:"03-agent",label:"03-agent"}],M={mode:"smart",completion:"04-fast",rewrite:"04-fast",reasoning:"03-agent",artifact:"03-agent"};function P(e,t){const o=e.pkmerModelRouting??M;if("smart"===o.mode)return M[t];const n=o[t]?.trim();return n||M[t]}const N=[{id:"template-demo-variables",name:"📝 总结要点",prompt:"分析 {{file:content}} 的内容结构,重点关注:\n{{selection}}\n\n请提供:\n1. 内容概要\n2. 关键要点\n3. 改进建议",icon:"lucide-sparkles"},{id:"template-task-variables",name:"提炼任务",prompt:'当前日期{{date}},从 {{file:content}} 中提取所有待办事项,重点关注:\n{{selection}}\n\n请严格按 Obsidian Tasks 插件格式输出,每条任务单独一行:\n\n- [ ] 任务描述 ⏫/🔼/🔽/⏬ 优先级\n- [ ] 任务描述 📅 YYYY-MM-DD 截止日期\n- [ ] 任务描述 ⏰ YYYY-MM-DD HH:mm 提醒时间\n- [ ] 任务描述 🛫 YYYY-MM-DD 开始日期\n- [ ] 任务描述 🔁 every day/week/month 重复周期\n- [ ] 任务描述 #标签 #项目名\n\n提取规则:\n1. 如果原文提到"紧急/马上/立即/今天",添加 ⏫(最高优先级)\n2. 如果提到"明天/本周/尽快",添加 🔼(高优先级)\n3. 如果提到具体日期,转换为 📅 YYYY-MM-DD 格式\n4. 如果提到时间,转换为 ⏰ YYYY-MM-DD HH:mm 格式\n5. 如果提到周期性工作,添加 🔁 every week/month\n6. 为每个任务添加合适的标签(如 #工作 #会议 #跟进)\n\n示例输出:\n- [ ] 完成项目报告 ⏫ 📅 2026-04-25 #工作\n- [ ] 每周团队例会 🔁 every week on Monday ⏰ 09:00 #会议\n- [ ] 跟进客户需求 🔼 📅 2026-04-23 #跟进',icon:"lucide-sparkles"},{id:"template-dataview",name:"生成 Dataview",prompt:"根据我的需求,帮我生成一个 Obsidian Dataview 查询代码块。要求:\n1. 使用 DataviewJS 或 DQL 语法\n2. 包含必要的字段筛选和排序\n3. 添加注释说明每个部分的作用\n4. 如果需要复杂逻辑,使用 DataviewJS\n\n我的需求:",icon:"lucide-database"},{id:"template-templater",name:"设计 Templater 模板",prompt:"帮我设计一个 Obsidian Templater 模板。要求:\n1. 使用 Templater 语法 (<% %>)\n2. 包含动态日期、时间等变量\n3. 支持用户输入提示\n4. 添加必要的条件判断和循环\n5. 注释说明每个部分的用途\n\n模板用途:",icon:"lucide-file-code"},{id:"template-mermaid",name:"绘制 Mermaid 图表",prompt:"根据我选中的文本内容 {{selection}},生成 Mermaid 图表代码。要求:\n1. 选择合适的图表类型(流程图/时序图/类图/甘特图等)\n2. 使用清晰的节点命名\n3. 添加必要的样式和注释\n4. 确保语法正确可渲染\n",icon:"lucide-workflow"},{id:"template-metadata",name:"设计 YAML",prompt:"根据当前笔记内容 {{file:content}},帮我设计一个适合当前笔记的 YAML Frontmatter 元数据结构。要求:\n1. 根据笔记内容推荐合适的字段\n2. 包含常用字段(tags, aliases, date等)\n3. 添加自定义字段建议\n4. 注释说明每个字段的用途\n\n笔记类型:",icon:"lucide-file-json"},{id:"template-callout",name:"使用Callout包装",prompt:"根据我选中的文本内容 {{selection}},帮我使用 Obsidian Callout 块进行包装。要求:\n1. 选择合适的 callout 类型(note/tip/warning/danger等)\n2. 支持嵌套和折叠\n3. 包含标题和内容\n4. 可以包含代码块或列表\n\n内容需求:",icon:"lucide-message-square"}],D=[{id:"template-demo-variables",name:"📝 Summarize Key Points",prompt:"Analyze the structure of {{file:content}} and focus on:\n{{selection}}\n\nPlease provide:\n1. A concise overview\n2. The key points\n3. Suggestions for improvement",icon:"lucide-sparkles"},{id:"template-task-variables",name:"Extract Tasks",prompt:"Today's date is {{date}}. Extract all actionable tasks from {{file:content}}, with special attention to:\n{{selection}}\n\nPlease output strictly in Obsidian Tasks plugin format, one task per line:\n\n- [ ] Task description ⏫/🔼/🔽/⏬ priority\n- [ ] Task description 📅 YYYY-MM-DD due date\n- [ ] Task description ⏰ YYYY-MM-DD HH:mm reminder\n- [ ] Task description 🛫 YYYY-MM-DD start date\n- [ ] Task description 🔁 every day/week/month recurrence\n- [ ] Task description #tag #project\n\nExtraction rules:\n1. If the text implies urgency such as urgent, ASAP, immediately, or today, add ⏫.\n2. If it implies near-term timing such as tomorrow, this week, or soon, add 🔼.\n3. Convert explicit dates to 📅 YYYY-MM-DD.\n4. Convert explicit times to ⏰ YYYY-MM-DD HH:mm when possible.\n5. Add recurrence for repeated work such as 🔁 every week/month.\n6. Add practical tags for each task.\n\nExample output:\n- [ ] Finish project report ⏫ 📅 2026-04-25 #work\n- [ ] Weekly team sync 🔁 every week on Monday ⏰ 09:00 #meeting\n- [ ] Follow up on client request 🔼 📅 2026-04-23 #follow-up",icon:"lucide-sparkles"},{id:"template-dataview",name:"Generate Dataview",prompt:"Help me generate an Obsidian Dataview query block based on my requirement. Requirements:\n1. Use DataviewJS or DQL syntax.\n2. Include the necessary filters and sorting.\n3. Add short comments explaining each part.\n4. Use DataviewJS if the logic is complex.\n\nMy requirement:",icon:"lucide-database"},{id:"template-templater",name:"Design Templater Template",prompt:"Help me design an Obsidian Templater template. Requirements:\n1. Use Templater syntax (<% %>).\n2. Include dynamic date, time, and similar variables.\n3. Support user input prompts.\n4. Add necessary conditionals and loops.\n5. Comment the purpose of each section.\n\nTemplate purpose:",icon:"lucide-file-code"},{id:"template-mermaid",name:"Create Mermaid Diagram",prompt:"Based on my selected text {{selection}}, generate Mermaid diagram code. Requirements:\n1. Choose an appropriate diagram type such as flowchart, sequence, class, or gantt.\n2. Use clear node names.\n3. Add useful styling and comments when needed.\n4. Ensure the syntax is valid and renderable.\n",icon:"lucide-workflow"},{id:"template-metadata",name:"Design YAML",prompt:"Based on the current note content {{file:content}}, help me design a suitable YAML Frontmatter structure for this note. Requirements:\n1. Recommend fields that fit the note content.\n2. Include common fields such as tags, aliases, and date.\n3. Suggest useful custom fields.\n4. Briefly explain the purpose of each field.\n\nNote type:",icon:"lucide-file-json"},{id:"template-callout",name:"Wrap with Callout",prompt:"Based on my selected text {{selection}}, wrap it using an Obsidian Callout block. Requirements:\n1. Choose an appropriate callout type such as note, tip, warning, or danger.\n2. Support nesting and folding when helpful.\n3. Include a title and content.\n4. Allow code blocks or lists when needed.\n\nContent requirement:",icon:"lucide-message-square"}];function L(e=E()){return(I(e)?N:D).map(e=>({...e}))}const F={enabled:!1,consentAccepted:!1,onboardingShown:!1,providerMode:"pkmer-first",enableInlineCompletion:!0,inlineCompletionHintLearned:!1,completionTrigger:"manual",completionDelay:500,enableRewrite:!0,showRewriteToolbarOnSelection:!1,rewriteMinSelectionLength:1,pkmerApiBaseUrl:"https://newapi.pkmer.cn",pkmerModel:"04-fast",pkmerModelRouting:M,pkmer:{tokenExpiresAt:0,userInfo:null},enableCustomModel:!1,customModel:{apiFormat:"openai-compatible",baseUrl:"",apiKey:"",model:"",temperature:.2},customPromptHistory:[],customPromptTemplates:L("zh-cn")},O=[{instruction:"improve",label:"Improve writing",group:"Edit"},{instruction:"fix-grammar",label:"Fix spelling & grammar",group:"Edit"},{instruction:"make-shorter",label:"Make shorter",group:"Edit"},{instruction:"make-longer",label:"Make longer",group:"Edit"},{instruction:"simplify",label:"Simplify language",group:"Edit"},{instruction:"professional",label:"Professional tone",group:"Tone"},{instruction:"casual",label:"Casual tone",group:"Tone"},{instruction:"translate-en",label:"English",group:"Translate"},{instruction:"translate-zh",label:"Chinese",group:"Translate"},{instruction:"translate-ja",label:"Japanese",group:"Translate"},{instruction:"translate-de",label:"German",group:"Translate"},{instruction:"translate-fr",label:"French",group:"Translate"},{instruction:"translate-es",label:"Spanish",group:"Translate"},{instruction:"explain",label:"Explain this",group:"Generate"},{instruction:"summarize",label:"Summarize",group:"Generate"},{instruction:"continue",label:"Continue writing",group:"Generate"}],R="editing-toolbar-pkmer-access-token",q="editing-toolbar-pkmer-refresh-token",B="editing-toolbar-pkmer-ai-token",$="editing-toolbar-custom-model-api-key",_="https://api.pkmer.cn/api/v1/oauth/authorize",z="https://api.pkmer.cn/api/v1/oauth/token",U="https://api.pkmer.cn/api/v1/oauth/userinfo",V="pkmer_dd2a562c8653ca0112a050150d974ccd",H="openid profile email ai:token",W="http://localhost:10891/editing-toolbar/callback",j="obsidian://editing-toolbar-pkmer-auth",K=10891,Y=["body","workspace"],G=["default","tiny","glass","custom"],J=["following","top","fixed"],Z={lastVersion:"0.0.0",aestheticStyle:"default",positionStyle:"top",menuCommands:[{id:"editing-toolbar:editor-undo",name:"Undo Edit",icon:"undo-glyph"},{id:"editing-toolbar:editor-redo",name:"Redo Edit",icon:"redo-glyph"},{id:"editing-toolbar:toggle-format-brush",name:"Format Brush",icon:"paintbrush"},{id:"editing-toolbar:format-eraser",name:"Clear Text Formatting",icon:"eraser"},{id:"editing-toolbar:header2-text",name:"Header 2",icon:"header-2"},{id:"editing-toolbar:header3-text",name:"Header 3",icon:"header-3"},{id:"SubmenuCommands-header",name:"submenu",icon:"header-n",SubmenuCommands:[{id:"editing-toolbar:header1-text",name:"Header 1",icon:"header-1"},{id:"editing-toolbar:header4-text",name:"Header 4",icon:"header-4"},{id:"editing-toolbar:header5-text",name:"Header 5",icon:"header-5"},{id:"editing-toolbar:header6-text",name:"Header 6",icon:"header-6"}]},{id:"editing-toolbar:toggle-bold",name:"Bold",icon:"bold-glyph"},{id:"editing-toolbar:toggle-italics",name:"Italic",icon:"italic-glyph"},{id:"editing-toolbar:toggle-strikethrough",name:"Strikethrough",icon:"strikethrough-glyph"},{id:"editing-toolbar:underline",name:"Underline",icon:"underline-glyph"},{id:"editing-toolbar:toggle-highlight",name:"Highlight",icon:"highlight-glyph"},{id:"SubmenuCommands-text-tools",name:"Text Tools",icon:"box",menuType:"dropdown",SubmenuCommands:[{id:"editing-toolbar:get-plain-text",name:"Get Plain Text",icon:"lucide-file-text"},{id:"editing-toolbar:smart-symbols",name:"Full Half Converter",icon:"lucide-at-sign"},{id:"editingToolbar-Divider-Line",name:"Line Operations",icon:"vertical-split"},{id:"editing-toolbar:insert-blank-lines",name:"Insert Blank Lines",icon:"lucide-space"},{id:"editing-toolbar:remove-blank-lines",name:"Remove Blank Lines",icon:"lucide-minimize-2"},{id:"editing-toolbar:split-lines",name:"Split Lines",icon:"lucide-split"},{id:"editing-toolbar:merge-lines",name:"Merge Lines",icon:"lucide-merge"},{id:"editing-toolbar:dedupe-lines",name:"Dedupe Lines",icon:"lucide-filter"},{id:"editingToolbar-Divider-Line",name:"Text Processing",icon:"vertical-split"},{id:"editing-toolbar:add-wrap",name:"Add Prefix/Suffix",icon:"lucide-wrap-text"},{id:"editing-toolbar:number-lines",name:"Number Lines (Custom)",icon:"lucide-list-ordered"},{id:"editing-toolbar:remove-whitespace-trim",name:"Trim Line Ends",icon:"lucide-scissors"},{id:"editing-toolbar:remove-whitespace-compress",name:"Shrink Extra Spaces",icon:"lucide-minimize"},{id:"editing-toolbar:remove-whitespace-all",name:"Remove All Whitespace",icon:"lucide-eraser"},{id:"editingToolbar-Divider-Line",name:"Advanced Tools",icon:"vertical-split"},{id:"editing-toolbar:list-to-table",name:"List to Table",icon:"lucide-table"},{id:"editing-toolbar:table-to-list",name:"Table to List",icon:"lucide-list"},{id:"editing-toolbar:extract-between",name:"Extract Between Strings",icon:"lucide-brackets"}]},{id:"SubmenuCommands-lucdf3en5",name:"submenu",icon:"edit",SubmenuCommands:[{id:"editing-toolbar:editor-cut",name:"Cut",icon:"lucide-scissors"},{id:"editing-toolbar:editor-copy",name:"Copy",icon:"lucide-copy"},{id:"editing-toolbar:editor-paste",name:"Paste",icon:"lucide-clipboard-type"},{id:"editing-toolbar:editor:swap-line-down",name:"Swap Line Down",icon:"lucide-corner-right-down"},{id:"editing-toolbar:editor:swap-line-up",name:"Swap Line Up",icon:"lucide-corner-right-up"}]},{id:"editing-toolbar:editor:attach-file",name:"Attach File",icon:"lucide-paperclip"},{id:"editing-toolbar:editor:insert-table",name:"Insert Table",icon:"lucide-table"},{id:"editing-toolbar:editor:cycle-list-checklist",name:"Cycle List and Checklist",icon:"check-circle"},{id:"SubmenuCommands-luc8efull",name:"submenu",icon:"message-square",SubmenuCommands:[{id:"editing-toolbar:editor:toggle-blockquote",name:"Blockquote",icon:"lucide-text-quote"},{id:"editing-toolbar:insert-callout",name:"Callout",icon:"lucide-quote"}]},{id:"SubmenuCommands-mdcmder",name:"submenu",icon:'',SubmenuCommands:[{id:"editing-toolbar:superscript",name:"Superscript",icon:"superscript-glyph"},{id:"editing-toolbar:subscript",name:"Subscript",icon:"subscript-glyph"},{id:"editing-toolbar:editor:toggle-code",name:"Inline Code",icon:"code-glyph"},{id:"editing-toolbar:codeblock",name:"Code Block",icon:"codeblock-glyph"},{id:"editing-toolbar:editor:insert-wikilink",name:"Wikilink",icon:''},{id:"editing-toolbar:editor:insert-embed",name:"Embed",icon:"note-glyph"},{id:"editing-toolbar:insert-link",name:"Link",icon:"link-glyph"},{id:"editing-toolbar:hrline",name:"Horizontal Divider",icon:''},{id:"editing-toolbar:toggle-inline-math",name:"Inline Math",icon:"lucide-sigma"},{id:"editing-toolbar:editor:insert-mathblock",name:"MathBlock",icon:"lucide-sigma-square"}]},{id:"SubmenuCommands-list",name:"submenu-list",icon:"bullet-list-glyph",SubmenuCommands:[{id:"editing-toolbar:editor:toggle-checklist-status",name:"Checklist",icon:"checkbox-glyph"},{id:"editing-toolbar:renumber-ordered-list",name:"Renumber Ordered List",icon:"list-restart"},{id:"editing-toolbar:toggle-numbered-list",name:"Ordered List",icon:''},{id:"editing-toolbar:toggle-bullet-list",name:"Unordered List",icon:''},{id:"editing-toolbar:undent-list",name:"Unindent List",icon:''},{id:"editing-toolbar:indent-list",name:"Indent list",icon:''}]},{id:"SubmenuCommands-aligin",name:"submenu-aligin",icon:'',SubmenuCommands:[{id:"editing-toolbar:justify",name:"Justify Text",icon:''},{id:"editing-toolbar:left",name:"Align Text Left",icon:''},{id:"editing-toolbar:center",name:"Center Text",icon:''},{id:"editing-toolbar:right",name:"Align Text Right",icon:''}]},{id:"editing-toolbar:change-font-color",name:"Change Font Color",icon:''},{id:"editing-toolbar:change-background-color",name:"Change Background Color",icon:''},{id:"editing-toolbar:fullscreen-focus",name:"Fullscreen Focus Mode",icon:"fullscreen"},{id:"editing-toolbar:workplace-fullscreen-focus",name:"Workplace Fullscreen",icon:"exit-fullscreen"}],followingCommands:[],topCommands:[],fixedCommands:[],mobileCommands:[],enableMultipleConfig:!1,enableTopToolbar:!1,enableFollowingToolbar:!1,enableFixedToolbar:!1,appendMethod:"workspace",shouldShowMenuOnSelect:!1,cMenuVisibility:!0,cMenuBottomValue:4.25,cMenuNumRows:12,cMenuWidth:610,cMenuFontColor:"#2DC26B",cMenuBackgroundColor:"#d3f8b6",autohide:!1,Iscentered:!1,custom_bg1:"#FFB78B8C",custom_bg2:"#CDF4698C",custom_bg3:"#A0CCF68C",custom_bg4:"#F0A7D88C",custom_bg5:"#ADEFEF8C",custom_fc1:"#D83931",custom_fc2:"#DE7802",custom_fc3:"#245BDB",custom_fc4:"#6425D0",custom_fc5:"#646A73",isLoadOnMobile:!1,horizontalPosition:0,verticalPosition:0,formatBrushes:{},customCommands:[],viewTypeSettings:{},appearanceByStyle:{top:{toolbarBackgroundColor:"rgba(var(--background-secondary-rgb), 0.7)",toolbarIconColor:"var(--text-normal)",toolbarIconSize:18,aestheticStyle:"default"},following:{toolbarBackgroundColor:"rgba(var(--background-secondary-rgb), 0.7)",toolbarIconColor:"var(--text-normal)",toolbarIconSize:18,aestheticStyle:"default"},fixed:{toolbarBackgroundColor:"rgba(var(--background-secondary-rgb), 0.7)",toolbarIconColor:"var(--text-normal)",toolbarIconSize:18,aestheticStyle:"default"},mobile:{toolbarBackgroundColor:"rgba(var(--background-secondary-rgb), 0.7)",toolbarIconColor:"var(--text-normal)",toolbarIconSize:18,aestheticStyle:"default"}},toolbarBackgroundColor:"rgba(var(--background-secondary-rgb), 0.7)",toolbarIconColor:"var(--text-normal)",toolbarIconSize:18,useCurrentLineForRegex:!1,ai:F};class X{static isAllowedViewType(e,t){if(!e)return!1;const o=e.getViewType(),n=window.app?.plugins?.plugins?.["editing-toolbar"];if(n?.settings?.viewTypeSettings&&void 0!==n.settings.viewTypeSettings[o])return n.settings.viewTypeSettings[o];return(t||["markdown","canvas","thino_view","meld-encrypted-view"]).includes(o)}static isSourceMode(e){return!!e&&"source"===e.getMode?.()}}const Q="editing-toolbar:ai-tools";const ee=[{id:"list",label:"Convert to list",icon:"lucide-list-tree"},{id:"table",label:"Convert to table",icon:"lucide-table-properties"},{id:"frontmatter",label:"Generate frontmatter",icon:"lucide-file-badge-2"},{id:"canvas",label:"Convert to canvas",icon:"lucide-waypoints"}],te=["Obsidian Markdown rules:","- Preserve valid Obsidian flavored Markdown whenever relevant.","- Preserve wikilinks like [[Note]], embeds like ![[Note]], tags, task lists, callouts, tables, and inline formatting if they appear in the source.","- For internal links use [[wikilinks]] rather than Markdown links whenever the target looks like a vault note.","- Use standard Markdown headings, lists, blockquotes, tables, and code fences only when they fit the content naturally."].join("\n"),oe=["Obsidian Bases skill requirements:","- Output a valid .base file body in YAML only. No explanations. No code fences.","- A base file may contain top-level filters, formulas, properties, summaries, and views.","- filters can be a single filter string or nested and/or/not filter objects.","- formulas is a mapping from formula name to expression string.","- properties config can include displayName for note, file, and formula properties.","- views must be an array. Each view should have a useful type such as table, cards, list, or map, plus a name and order.","- Use practical property names inferred from the source content, and use formula.* when referencing formula properties in views.","- Common file properties include file.name, file.basename, file.path, file.folder, file.ext, file.ctime, file.mtime, file.tags, file.links, file.backlinks, file.embeds, file.properties.","- Keep YAML valid: quote strings when needed, keep indentation consistent, and do not reference undefined formulas.","- Prefer concise but actually usable output over placeholders.","- If the source describes tabular entities, create at least one table view.","- If the source suggests browsing or grouping, consider cards or list views too.","- If filtering logic is unclear, you may omit filters rather than inventing weak ones.","- If there are date-like fields, formulas may use date(), now(), today(), and duration .days accessors.","- Return one complete semantic .base file ready to save and open in Obsidian.","Suggested schema pattern:","filters:"," and: []","formulas:"," formula_name: 'expression'","properties:"," property_name:",' displayName: "Display Name"',"views:"," - type: table",' name: "Overview"'," order:"," - file.name"," - property_name"].join("\n"),ne=["Obsidian JSON Canvas skill requirements:","- Output valid JSON only. No explanations. No code fences.","- The top-level object must contain arrays named nodes and edges.","- Every node needs id, type, x, y, width, and height.","- Every edge must reference existing node ids using fromNode and toNode.","- Use unique 16-character lowercase hexadecimal ids for all nodes and edges.","- Valid node types are text, file, link, and group.","- Prefer text nodes unless a file node or link node is clearly required by the source.","- Text nodes should contain concise, structured Markdown-friendly text.","- Use \\n inside JSON strings for line breaks. Never emit literal \\\\n.","- If useful, use one group node to organize the canvas, but do not overuse groups.","- Layout should be readable: align to a loose grid, leave about 50-100px spacing between nodes, and avoid overlaps.","- Coordinates can be negative, but prefer a clean left-to-right or center-out layout.","- A good canvas usually has a clear central node and connected supporting nodes rather than isolated fragments.","- Unless the source is tiny, create a meaningful structure with several nodes instead of only one or two.","- Use edge labels only when they add real meaning.","- Return a canvas that is ready to save as a .canvas file and open directly in Obsidian.","Reference shape:","{",' "nodes": [',' { "id": "6f0ad84f44ce9c17", "type": "text", "x": 0, "y": 0, "width": 360, "height": 180, "text": "# Topic\\n\\n- point" }'," ],",' "edges": [',' { "id": "0123456789abcdef", "fromNode": "6f0ad84f44ce9c17", "toNode": "a1b2c3d4e5f67890", "toEnd": "arrow" }'," ]","}"].join("\n");let ie;const ae={markdown:".markdown-source-view",thino_view:".markdown-source-view",canvas:".canvas-wrapper",excalidraw:".view-header",image:".image-container",pdf:".view-content",meld_encrypted_view:".markdown-source-view"};function se(t){ie=e.requireApiVersion("0.15.0")?activeWindow.document:window.document;const o=function(){const t=[];t.push(app.workspace.rootSplit);const o=app.workspace.floatingSplit;return o?.children.forEach(o=>{o instanceof e.WorkspaceWindow&&t.push(o)}),t}(),n=e=>{const t=e.querySelectorAll(".editingToolbarModalBar"),o=e.querySelectorAll(".editingToolbarPopoverBar");t.forEach(e=>{e.firstChild&&e.removeChild(e.firstChild),e.remove()}),o.forEach(e=>{e.firstChild&&e.removeChild(e.firstChild),e.remove()})};n(ie),o&&o.forEach(e=>{e?.containerEl&&n(e.containerEl)}),t&&t.clearToolbarCache()}function re(t,o,n,i){const a=i||t.workspace.activeLeaf?.view?.containerEl?.ownerDocument||(e.requireApiVersion("0.15.0")?activeWindow.document:window.document);ie=a;const s=n||o.positionStyle||o.settings.positionStyle||"top";if("top"!==s){const e=o.getCachedToolbar(s);if(e&&e.ownerDocument===a)return e}const r=`.editingToolbarModalBar[data-toolbar-style="${s}"]`;let l=null;return l="top"===s?t.workspace.activeLeaf?.view.containerEl?.querySelector(r)||null:a.querySelector(r),l&&"top"!==s&&o.setCachedToolbar(s,l),l||null}const le=(e,t)=>t.reduce((e,t)=>e&&"undefined"!==e[t]?e[t]:void 0,e);function ce(e,t){return e&&void 0!==e[1][0]?t+e.flat(2).join("+").replace("Mod","Ctrl")+t:t+"–"+t}function de(e,t,o=!1){let n=e.commands.findCommand(t),i=o?"*":"";if(n){let t=n.hotkeys?[[le(n.hotkeys,[0,"modifiers"])],[le(n.hotkeys,[0,"key"])]]:void 0,o=e.hotkeyManager.customKeys[n.id];var a=o?[[le(o,[0,"modifiers"])],[le(o,[0,"key"])]]:void 0;return a?ce(a,i):ce(t,"")}return"–"}function ue(e){return/<[^>]+>/g.test(e)}function me(t,o){return o?ue(o)?(t.setIcon("lucide-square"),void(t.iconEl&&(t.iconEl.empty(),t.iconEl.innerHTML=o))):(t.setIcon(o),void(t.iconEl&&0===t.iconEl.childElementCount&&e.setIcon(t.iconEl,o))):(t.setIcon(""),void(t.iconEl&&t.iconEl.empty()))}function pe(e,t,o,n){const i=n.commandsManager.getActiveEditor(),a=i&&i.somethingSelected();0==t.cMenuVisibility?e.style.visibility="hidden":"following"===o?a||(e.style.visibility="hidden"):e.style.visibility="visible"}const he={improve:"lucide-wand-2","fix-grammar":"lucide-spell-check-2","make-shorter":"lucide-minimize-2","make-longer":"lucide-maximize-2",simplify:"lucide-align-left",professional:"lucide-briefcase",casual:"lucide-message-circle-more","translate-en":"lucide-languages","translate-zh":"lucide-languages","translate-ja":"lucide-languages","translate-de":"lucide-languages","translate-fr":"lucide-languages","translate-es":"lucide-languages",explain:"lucide-info",summarize:"lucide-file-text",continue:"lucide-pencil-line",custom:"lucide-message-square"},ge={"editing-toolbar:ai-inline-completion":"AI Complete","editing-toolbar:ai-canvas-expand":"AI Canvas Expand","editing-toolbar:ai-canvas-global-prompt":"AI Canvas Prompt","editing-toolbar:ai-rewrite-improve":"AI Rewrite","editing-toolbar:ai-rewrite-continue":"AI Continue","editing-toolbar:ai-rewrite-custom":"AI Custom","editing-toolbar:ai-tools:improve":"AI Rewrite","editing-toolbar:ai-tools:fix-grammar":"AI Fix","editing-toolbar:ai-tools:make-shorter":"AI Shorten","editing-toolbar:ai-tools:make-longer":"AI Expand","editing-toolbar:ai-tools:simplify":"AI Simplify","editing-toolbar:ai-tools:professional":"AI Professional","editing-toolbar:ai-tools:casual":"AI Casual","editing-toolbar:ai-tools:translate-en":"AI Translate","editing-toolbar:ai-tools:translate-zh":"AI Translate","editing-toolbar:ai-tools:translate-ja":"AI Translate","editing-toolbar:ai-tools:translate-de":"AI Translate","editing-toolbar:ai-tools:translate-fr":"AI Translate","editing-toolbar:ai-tools:translate-es":"AI Translate","editing-toolbar:ai-tools:explain":"AI Explain","editing-toolbar:ai-tools:summarize":"AI Summarize","editing-toolbar:ai-tools:continue":"AI Continue","editing-toolbar:ai-tools:custom":"AI Custom","editing-toolbar:ai-toolbox:list":"AI List","editing-toolbar:ai-toolbox:table":"AI Table","editing-toolbar:ai-toolbox:frontmatter":"AI Frontmatter","editing-toolbar:ai-toolbox:canvas":"AI Canvas"},fe=new Set(["editing-toolbar:ai-canvas-expand","editing-toolbar:ai-canvas-global-prompt"]);function be(e,t){e.lastExecutedCommand=t,e.lastExecutedCommandName=v(ge[t]?ge[t]:"AI")}function ye(e){return!!e&&fe.has(e)}function Ce(e){const t=e.lastExecutedCommand,o="canvas"===e.app.workspace.activeLeaf?.view?.getViewType?.(),n=e.commandsManager?.getActiveEditor?.();return t&&!ye(t)||!o||n?t&&ge[t]&&(!ye(t)||o)?v(ge[t]):t?.startsWith("editing-toolbar:ai")&&e.lastExecutedCommandName&&(!ye(t)||o)?e.lastExecutedCommandName:v("AI"):v("AI Canvas Prompt")}function ve(t,o,n,i,a){if(n<=100)return!1;return t+o+6*Math.max(1,Math.round(t/Math.max(i,1)))+(i+12)+("following"===a?i+10:0)+(e.Platform.isMobileApp||"mobile"===a?14:0)>=Math.max(n-16,2*i)}function we(t,o,n){ie=e.requireApiVersion("0.15.0")?activeWindow.document:window.document;const i=o.commandsManager.getActiveEditor();let a=re(t,o),s=a?.querySelector("#"+n);if(s){let e=s.rows,t=e.length;for(let a=1;a{t.style.fill=e})}else if("x-backgroundcolor-picker-table"==n){o.settings.cMenuBackgroundColor=e,c(e,i),ie.querySelectorAll("#change-background-color-icon").forEach(t=>{t.style.fill=e})}o.saveSettings()}}}}}const xe=function(e){let t=e;if(/^(rgb|RGB)/.test(t)){let e=t.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(","),o="#";for(let t=0;t{"hidden"==s.style.visibility?(s.style.visibility="visible",s.style.height="32px",((e,t,o)=>{if("following"!==o)return void(e=>{e.style.removeProperty("left"),e.style.removeProperty("top"),e.style.removeProperty("right"),e.style.removeProperty("bottom"),e.style.removeProperty("transform"),e.style.removeProperty("margin"),e.style.removeProperty("position")})(t);const n=t.ownerDocument.defaultView??window,i=e.getBoundingClientRect(),a=Math.max(t.offsetWidth,t.scrollWidth),s=Math.max(t.offsetHeight,t.scrollHeight),r=Math.max(12,n.innerWidth-a-12);let l=i.right-a;a<=0&&(l=i.left),l=Math.min(Math.max(l,12),r);let c=i.bottom+8;s>0&&c+s>n.innerHeight-12&&(c=Math.max(12,i.top-s-8)),t.style.position="fixed",t.style.left=`${l}px`,t.style.top=`${c}px`,t.style.right="auto",t.style.bottom="auto",t.style.transform="none",t.style.margin="0"})(l.buttonEl,s,a)):(s.style.visibility="hidden",s.style.height="0")}),l.buttonEl.innerHTML='',o.setIS_MORE_Button(!1),r}function Se(e){e.quiteAllFormatBrushes()}function Te(e,t){let o=t.getSelection();if(!o||""===o.trim())return;if(o.match(/^>\s*\[\![\w\s]*\]/m)){let e=o.split("\n"),n=[],i=!1,a=0,s=!1;for(let t=0;t+)\s*\[\!([\w\s]*)\]\s*(.*?)$/);if(!r||s)if(i){let e=o.match(/^(>+)\s*/);if(e&&e[1].length>=a){let e=o.replace(new RegExp(`^>{${a}}\\s*`),"");n.push(e)}else i=!1,n.push(o)}else n.push(o);else a=r[1].length,s=!0,r[3].trim()&&n.push(r[3].trim()),i=!0}return void t.replaceSelection(n.join("\n"))}o=o.replace(/(^#+\s|^#(?=\s)|^\>|^\- \[( |x)\]|^\+ |\<[^\<\>]+?\>|^1\. |^\s*\- |^\-+$|^\*+$)/gm,""),o=o.replace(/^[ ]+|[ ]+$/gm,""),o=o.replace(/\!?\[\[([^\[\]\|]*\|)*([^\(\)\[\]]+)\]\]/g,"$2"),o=o.replace(/\!?\[+([^\[\]\(\)]+)\]+\(([^\(\)]+)\)/g,"$1"),o=o.replace(/`([^`]+)`/g,"$1"),o=o.replace(/_([^_]+)_/g,"$1"),o=o.replace(/==([^=]+)==/g,"$1"),o=o.replace(/\*\*\*([^\*]+)\*\*\*/g,"$1"),o=o.replace(/\*\*?([^\*]+)\*\*?/g,"$1"),o=o.replace(/~~([^~]+)~~/g,"$1"),t.replaceSelection(o)}function Ee(t,o,n,i,a=!1,s){const r=s||i?.cm?.dom?.ownerDocument||i?.cm?.contentDOM?.ownerDocument||t.workspace.activeLeaf?.view?.containerEl?.ownerDocument||(e.requireApiVersion("0.15.0")?activeWindow.document:window.document);let l=re(t,n,"following",r);const c=t.workspace.getActiveViewOfType(e.ItemView);if(!X.isAllowedViewType(c))return void(l&&(l.style.visibility="hidden"));if(!(n.settings.enableFollowingToolbar||!n.settings.enableTopToolbar&&!n.settings.enableFixedToolbar&&"following"===n.positionStyle))return;l||(Ie(t,n,"following",r),l=re(t,n,"following",r));const d=c?.getViewType(),u="markdown"===d;let m=30;if(m="tiny"===n.settings.aestheticStyle?30:o+14,u)if(X.isSourceMode(c)){if(l){const e=a||i.somethingSelected();l.style.visibility=e?"visible":"hidden","visible"===l.style.visibility&&(l.style.height=m+"px",l.addClass("editingToolbarFlex"),l.removeClass("editingToolbarGrid"),function(e,t){const o=t.containerEl.getBoundingClientRect(),n=e.offsetWidth,i=e.offsetHeight,a=12,s=e.ownerDocument.defaultView?.innerWidth??window.innerWidth,r=t.getCursor("from");t.getCursor("to");const l=t.coordsAtPos(r),c=ie.getElementsByClassName("mod-left-split")[0]?.clientWidth??0,d=ie.getElementsByClassName("side-dock-ribbon mod-left")[0]?.clientWidth??0,u=c+d;let m=l.left-u-28;m+n>s-u&&(m=s-u-n-a);m=Math.max(0,m);let p=function(e,t,o,n){const i=e.getCursor("from"),a=e.getCursor("to"),s=e.coordsAtPos(a),r=i.line===a.line;let l=t.top-n-10;if(r)l<=o.top&&(l=s.bottom+10);else{if(e.getCursor("head").ch==e.getCursor("from").ch)l=t.top-n-10,l<=o.top&&(l=o.top+2*n);else{const t=(e=>{let t,o=e.getCursor("head");if(e.getCursor("head").ch!==e.getCursor("from").ch&&(o.ch=Math.max(0,o.ch-1)),e.cursorCoords)t=e.cursorCoords(!0,"window");else{if(!e.coordsAtPos)return;{const n=e.posToOffset(o);t=e.cm.coordsAtPos?.(n)??e.coordsAtPos(n)}}return t})(e);l=t.bottom+10,l>=o.bottom-n&&(l=o.bottom-2*n)}}return l}(t,l,o,i);p=Math.max(0,p),e.style.left=`${m}px`,e.style.top=`${p}px`}(l,i))}}else l&&(l.style.visibility="hidden");else l&&(l.style.visibility="visible",l.style.height=m+"px",l.addClass("editingToolbarFlex"),l.removeClass("editingToolbarGrid"))}function Ie(t,o,n,i){const a=o.settings,s=i||t.workspace.activeLeaf?.view?.containerEl?.ownerDocument||(e.requireApiVersion("0.15.0")?activeWindow.document:window.document);if(ie=s,!n){const e=[];if(a.enableTopToolbar&&e.push("top"),a.enableFollowingToolbar&&e.push("following"),a.enableFixedToolbar&&e.push("fixed"),0===e.length){const t=o.positionStyle||o.settings.positionStyle||"top";e.push(t)}return void e.forEach(e=>{Ie(t,o,e,s)})}const r=n;if(!a.cMenuVisibility){const e=re(t,o,r,s);return void(e&&(e.style.display="none"))}const l=(a.appearanceByStyle||{})[r]||{},c=l.toolbarIconSize??o.toolbarIconSize??18,d=l.aestheticStyle??a.aestheticStyle??"default",u="custom"===d?l.toolbarBackgroundColor??a.toolbarBackgroundColor:void 0,m="custom"===d?l.toolbarIconColor??a.toolbarIconColor:void 0,p={default:"editingToolbarDefaultAesthetic",tiny:"editingToolbarTinyAesthetic",glass:"editingToolbarGlassAesthetic",custom:"editingToolbarCustomAesthetic"};!function(){function n(e,t){Object.values(p).forEach(t=>{e.removeClass(t)});const o=p[t]||p.default;e.addClass(o)}const i=()=>{let i=0,l=0,p=c+8,h=createEl("div");if(h)if(h.addClass("editingToolbarModalBar"),h.setAttribute("data-toolbar-style",r),"top"===r)h.className+=" top",a.autohide&&(h.className+=" autohide"),a.Iscentered&&(h.className+=" centered");else if("following"===r)h.style.visibility="hidden";else if("fixed"===r){const e=c||18,t=`left: calc(50% - calc(${a.cMenuNumRows*(e+10)}px / 2));\n bottom: 4.25em; \n grid-template-columns: repeat(${a.cMenuNumRows}, ${e+10}px);\n gap: ${(e-18)/4}px`;h.setAttribute("style",t)}h.setAttribute("id","editingToolbarModalBar");let g=createEl("div");if(g.addClass("editingToolbarpopover"),g.addClass("editingToolbarTinyAesthetic"),g.addClass("editingToolbarPopoverBar"),g.setAttribute("data-toolbar-style",r),g.setAttribute("id","editingToolbarPopoverBar"),g.style.visibility="hidden",g.style.height="0",n(h,d),n(g,d),u&&(h.style.setProperty("--editing-toolbar-background-color",u),g.style.setProperty("--editing-toolbar-background-color",u)),m&&(h.style.setProperty("--editing-toolbar-icon-color",m),g.style.setProperty("--editing-toolbar-icon-color",m)),c&&(h.style.setProperty("--toolbar-icon-size",`${c}px`),g.style.setProperty("--toolbar-icon-size",`${c}px`)),"top"===r){let e=t.workspace.activeLeaf.view.containerEl,o=null;const n=t.workspace.activeLeaf.view.getViewType(),i=ae[n];if(i&&(o=e?.querySelector(i)),!o){const t=e?.querySelector(".view-content");if(t){const e=t.querySelectorAll(":scope > div");o=e.length>0?e[0]:t}}if(!o)return void console.log("Editing Toolbar: Failed to find target DOM element for toolbar insertion");const a="canvas"===n?e?.querySelector(".view-content"):null;"canvas"===n&&a?(a.insertAdjacentElement("beforebegin",h),e?.querySelector("#editingToolbarPopoverBar")||a.insertAdjacentElement("beforebegin",g)):(e?.querySelector("#editingToolbarPopoverBar")||("excalidraw"==n?o.insertAdjacentElement("afterend",g):o.insertAdjacentElement("afterbegin",g)),"excalidraw"==n?o.insertAdjacentElement("afterend",h):o.insertAdjacentElement("afterbegin",h));const r=[o?.clientWidth||o?.offsetWidth||0,e?.clientWidth||e?.getBoundingClientRect().width||0,s.defaultView?.innerWidth||0].filter(e=>e>0);l=r.length>0?Math.min(...r):0}else if("body"==a.appendMethod){s.querySelector(`.editingToolbarPopoverBar[data-toolbar-style="${r}"]`)||s.body.appendChild(g),s.body.appendChild(h),l=s.defaultView?.innerWidth||s.body?.clientWidth||0}else if("workspace"==a.appendMethod){const e=s.body?.querySelector(".mod-vertical.mod-root");if(!e)return;e.querySelector(`.editingToolbarPopoverBar[data-toolbar-style="${r}"]`)||e.insertAdjacentElement("afterbegin",g),e.insertAdjacentElement("afterbegin",h);const t=[s.body?.clientWidth||0,s.defaultView?.innerWidth||0].filter(e=>e>0);l=t.length>0?Math.min(...t):0}let f="top"===r?t.workspace.activeLeaf.view.containerEl?.querySelector("#editingToolbarPopoverBar"):s.querySelector(`.editingToolbarPopoverBar[data-toolbar-style="${r}"]`);const b=e=>e?f||(console.warn(`Editing Toolbar: missing popover host for style "${r}", falling back to toolbar host.`),h):h,y=o.getCurrentCommands(r),C=e=>v(e),w=(e,t)=>{const o=C(e);return"–"===t?o:`${o}(${t})`};y.forEach((n,s)=>{let c;if("SubmenuCommands"in n){let d;ve(i,p,l,p,r)?(o.setIS_MORE_Button(!0),d=new e.ButtonComponent(b(!0))):d=new e.ButtonComponent(h),d.setClass("editingToolbarCommandsubItem"+s),s>=a.cMenuNumRows?d.setClass("editingToolbarSecond"):"top"!==r&&d.buttonEl.setAttribute("aria-label-position","top"),ue(n.icon)?d.buttonEl.innerHTML=n.icon:d.setIcon(n.icon),i+=p+2;if("dropdown"===(n.menuType||"submenu")){d.setClass("editingToolbarDropdownButton");let i=de(t,n.id);c=w(n.name,i),d.setTooltip(c),d.onClick(i=>{const s=new e.Menu;n.SubmenuCommands.forEach(e=>{"editingToolbar-Divider-Line"===e.id?(s.addSeparator(),s.addItem(t=>{t.setTitle(v(e.name)).setDisabled(!0),me(t,"")})):s.addItem(n=>{const i=de(t,e.id,!1),s=v(e.name),l="–"!==i?`${s}`:s;if(n.setTitle(l).onClick(()=>{t.commands.executeCommandById(e.id);const n=o.commandsManager.getActiveEditor(),i=n&&n.somethingSelected();0==a.cMenuVisibility?h.style.visibility="hidden":"following"===r?i||(h.style.visibility="hidden"):h.style.visibility="visible"}),me(n,e.icon),"—"!==i){n.dom.createSpan({cls:"menu-item-hotkey"}).setText(i)}})}),s.dom.addClass("editing-toolbar-dropdown-menu"),s.showAtMouseEvent(i)})}else{let i=function(e){let t=createEl("div");return t.addClass(e),t}("subitem");i&&n.SubmenuCommands.forEach(n=>{let l=de(t,n.id);c=w(n.name,l);let u=new e.ButtonComponent(i).setTooltip(c).setClass("menu-item").onClick(()=>{t.commands.executeCommandById(n.id);const e=o.commandsManager.getActiveEditor(),i=e&&e.somethingSelected();0==a.cMenuVisibility?h.style.visibility="hidden":"following"===r?i||(h.style.visibility="hidden"):h.style.visibility="visible"});s=a.cMenuNumRows?m.setClass("editingToolbarSecond"):"top"!==r&&m.buttonEl.setAttribute("aria-label-position","top"),ue(n.icon)?m.buttonEl.innerHTML=n.icon:m.setIcon(n.icon);const g="fixed"===r;m.setClass(g?"editing-toolbar-ai-button-fixed":"editing-toolbar-ai-button-inline");let f=null;if(!g){const e=document.createElement("span");e.className="editing-toolbar-ai-button-main",f=document.createElement("span"),f.className="editing-toolbar-ai-button-label",f.textContent=Ce(o),e.appendChild(f),m.buttonEl.appendChild(e)}const y=document.createElement("span");y.className="editing-toolbar-ai-button-arrow",y.setAttribute("aria-hidden","true"),y.textContent=String.fromCharCode(9662),m.buttonEl.appendChild(y),i+=c;const C=async n=>{w=Date.now()+240;const i="unavailable"!==await o.aiManager.getToolbarRouteState(),s=o.commandsManager.getActiveEditor(),l="canvas"===t.workspace.activeLeaf?.view?.getViewType?.(),c=de(t,"editing-toolbar:ai-inline-completion",!1),d=c.includes("+")?c:"",u=new e.Menu,m=()=>{u.hide()},p=t=>{const n=async()=>{if(!i)return void new e.Notice(await o.aiManager.getProviderRouteStatusText());!1!==await t.action()&&(m(),t.commandIdForLabel&&(be(o,t.commandIdForLabel),f&&(f.textContent=Ce(o))),pe(h,a,r,o))};u.addItem(e=>{if(e.setTitle(v(t.title)).setIcon(t.icon).onClick(n),t.hotkey){e.dom.createSpan({cls:"menu-item-hotkey"}).setText(t.hotkey)}})},g=(t,n,s)=>{u.addItem(l=>{l.setTitle(v(t)).setIcon(n);const c=l.setSubmenu();s.forEach(t=>{c.addItem(n=>{if(n.setTitle(v(t.title)).setIcon(t.icon).onClick(async()=>{if(!i)return void new e.Notice(await o.aiManager.getProviderRouteStatusText());!1!==await t.action()&&(m(),t.commandIdForLabel&&(be(o,t.commandIdForLabel),f&&(f.textContent=Ce(o))),pe(h,a,r,o))}),t.hotkey){n.dom.createSpan({cls:"menu-item-hotkey"}).setText(t.hotkey)}})})})};if(l&&(p({title:v("Canvas global prompt"),icon:"lucide-sparkles",commandIdForLabel:"editing-toolbar:ai-canvas-global-prompt",action:()=>o.aiManager.openCanvasGlobalPromptModal()}),p({title:v("Expand current canvas node"),icon:"lucide-waypoints",commandIdForLabel:"editing-toolbar:ai-canvas-expand",action:()=>o.aiManager.openCanvasNodeExpansionModal()})),!l||s){p({title:"Trigger AI Inline Completion",icon:"lucide-sparkles",hotkey:d||void 0,commandIdForLabel:"editing-toolbar:ai-inline-completion",action:()=>o.aiManager.triggerInlineCompletion(s)});const e=new Map;O.forEach(t=>{const o=e.get(t.group)??[];o.push(t),e.set(t.group,o)});const t={Edit:"lucide-wand-sparkles",Tone:"lucide-messages-square",Translate:"lucide-languages",Generate:"lucide-bot"};e.forEach((e,n)=>{g(n,t[n]??"lucide-sparkles",e.map(e=>({title:e.label,icon:he[e.instruction],commandIdForLabel:`editing-toolbar:ai-tools:${e.instruction}`,action:async()=>{await o.aiManager.startRewrite(s,e.instruction)}})))}),l||p({title:"AI Custom Rewrite",icon:he.custom,commandIdForLabel:"editing-toolbar:ai-tools:custom",action:()=>o.aiManager.openCustomRewrite(s)}),g("AI Toolbox","lucide-boxes",ee.map(e=>({title:e.label,icon:e.icon,commandIdForLabel:`editing-toolbar:ai-toolbox:${e.id}`,action:async()=>o.aiManager.runToolboxAction(s,e.id)})))}u.dom.addClass("editing-toolbar-dropdown-menu"),u.dom.addClass("editing-toolbar-ai-dropdown-menu"),u.showAtMouseEvent(n)};let w=0;y.addEventListener("click",async e=>{e.preventDefault(),e.stopPropagation(),await C(e)}),m.buttonEl.addEventListener("click",async n=>{if(n.preventDefault(),n.stopPropagation(),Date.now(){t.commands.executeCommandById(n.id);const e=o.commandsManager.getActiveEditor(),i=e&&e.somethingSelected();0==a.cMenuVisibility?h.style.visibility="hidden":"following"===r?i||(h.style.visibility="hidden"):h.style.visibility="visible"}),ue(n.icon)?s.buttonEl.innerHTML=n.icon:s.setIcon(n.icon),i+=p;let l=createEl("div");if(l.addClass("subitem"),l){l.innerHTML=function(e){return`\n\n \n \n \n Theme Colors \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Standard Colors \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Custom Font Colors \n \n \n \n \n \n \n \n \n \n
\n\n`}(o),s.buttonEl.insertAdjacentElement("afterbegin",l),we(t,o,"x-color-picker-table");let n=l.querySelector(".x-color-picker-wrapper");new e.ButtonComponent(n).setIcon("paintbrush").setTooltip(v("Format Brush")).onClick(()=>{Se(o),o.setEN_FontColor_Format_Brush(!0),o.Temp_Notice=new e.Notice(v("Font-Color formatting brush ON!"),0)}),new e.ButtonComponent(n).setIcon("palette").setTooltip(v("Custom Font Color")).onClick(()=>{t.setting.open(),t.setting.openTabById("editing-toolbar"),setTimeout(()=>{const e=t.setting.activeTab.containerEl.querySelector(".editing-toolbar-tabs");if(e){const o=e.children[0];o?.click(),setTimeout(()=>{let e=t.setting.activeTab.containerEl.querySelector(".custom_font");e&&e.addClass?.("toolbar-cta")},100)}},200)})}}else if("editing-toolbar:change-background-color"==n.id){let s=new e.ButtonComponent(h);s.setClass("editingToolbarCommandsubItem-font-color").setTooltip(v("Background Color")).onClick(()=>{t.commands.executeCommandById(n.id);const e=o.commandsManager.getActiveEditor(),i=e&&e.somethingSelected();0==a.cMenuVisibility?h.style.visibility="hidden":"following"===r?i||(h.style.visibility="hidden"):h.style.visibility="visible"}),ue(n.icon)?s.buttonEl.innerHTML=n.icon:s.setIcon(n.icon),i+=p;let l=createEl("div");if(l.addClass("subitem"),l){l.innerHTML=function(e){return`\n\n \n \n \n Translucent Colors \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n Highlighter Colors \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Custom Colors \n \n \n \n \n \n \n \n \n \n
\n\n`}(o),s.buttonEl.insertAdjacentElement("afterbegin",l),we(t,o,"x-backgroundcolor-picker-table");let n=l.querySelector(".x-color-picker-wrapper");new e.ButtonComponent(n).setIcon("paintbrush").setTooltip(v("Format Brush")).onClick(()=>{Se(o),o.setEN_BG_Format_Brush(!0),o.Temp_Notice=new e.Notice(v("Font-Color formatting brush ON!"),0)}),new e.ButtonComponent(n).setIcon("palette").setTooltip(v("Custom Backgroud Color")).onClick(()=>{t.setting.open(),t.setting.openTabById("editing-toolbar"),setTimeout(()=>{const e=t.setting.activeTab.containerEl.querySelector(".editing-toolbar-tabs");if(e){const o=e.children[0];o?.click(),setTimeout(()=>{let e=t.setting.activeTab.containerEl.querySelector(".custom_bg");e&&e.addClass?.("toolbar-cta")},100)}},200)})}}else{let d;ve(i,p,l,p,r)?(o.setIS_MORE_Button(!0),d=new e.ButtonComponent(b(!0))):d=new e.ButtonComponent(h);let u=de(t,n.id);c=w(n.name,u),d.setTooltip(c).onClick(()=>{t.commands.executeCommandById(n.id);const e=o.commandsManager.getActiveEditor(),i=e&&e.somethingSelected();0==a.cMenuVisibility?h.style.visibility="hidden":"following"===r?i||(h.style.visibility="hidden"):h.style.visibility="visible"}),d.setClass("editingToolbarCommandItem"),s>=a.cMenuNumRows?d.setClass("editingToolbarSecond"):"top"!==r&&d.buttonEl.setAttribute("aria-label-position","top"),"editingToolbar-Divider-Line"==n.id&&d.setClass("editingToolbar-Divider-Line"),ue(n.icon)?d.buttonEl.innerHTML=n.icon:d.setIcon(n.icon),i+=p}}),ke(t,o,h),Math.abs(o.settings.cMenuWidth-Number(i))>i+4&&(o.settings.cMenuWidth=Number(i),setTimeout(()=>{o.saveSettings()},100))};if(!o.isLoadMobile())return;const l=t.workspace.getActiveViewOfType(e.ItemView);if(X.isAllowedViewType(l)){const n=re(t,o,r,s);if(n&&"top"!==r)return a.cMenuVisibility?"following"===r?(n.style.visibility="hidden",n.style.display=""):(n.style.visibility="visible",n.style.display=""):n.style.display="none",u&&n.style.setProperty("--editing-toolbar-background-color",u),m&&n.style.setProperty("--editing-toolbar-icon-color",m),void(c&&n.style.setProperty("--toolbar-icon-size",`${c}px`));if(i(),"top"!==r){const e=re(t,o,r,s);e&&o.setCachedToolbar(r,e)}g(o.settings),h(o.settings),function(t,o){ie=e.requireApiVersion("0.15.0")?activeWindow.document:window.document;const n=ie.querySelectorAll("#change-font-color-icon"),i=ie.querySelectorAll("#change-background-color-icon");n.length>0&&n.forEach(e=>{e.style.fill=t});i.length>0&&i.forEach(e=>{e.style.fill=o})}
+/**!
+ * Sortable 1.15.7
+ * @author RubaXa
+ * @author owenm
+ * @license MIT
+ */(a.cMenuFontColor,a.cMenuBackgroundColor)}}()}function Ae(e,t,o){return(t=function(e){var t=function(e,t){if("object"!=typeof e||!e)return e;var o=e[Symbol.toPrimitive];if(void 0!==o){var n=o.call(e,t||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function Me(){return Me=Object.assign?Object.assign.bind():function(e){for(var t=1;t"===t[0]&&(t=t.substring(1)),e)try{if(e.matches)return e.matches(t);if(e.msMatchesSelector)return e.msMatchesSelector(t);if(e.webkitMatchesSelector)return e.webkitMatchesSelector(t)}catch(e){return!1}return!1}}function He(e){return e.host&&e!==document&&e.host.nodeType&&e.host!==e?e.host:e.parentNode}function We(e,t,o,n){if(e){o=o||document;do{if(null!=t&&(">"===t[0]?e.parentNode===o&&Ve(e,t):Ve(e,t))||n&&e===o)return e;if(e===o)break}while(e=He(e))}return null}var je,Ke=/\s+/g;function Ye(e,t,o){if(e&&t)if(e.classList)e.classList[o?"add":"remove"](t);else{var n=(" "+e.className+" ").replace(Ke," ").replace(" "+t+" "," ");e.className=(n+(o?" "+t:"")).replace(Ke," ")}}function Ge(e,t,o){var n=e&&e.style;if(n){if(void 0===o)return document.defaultView&&document.defaultView.getComputedStyle?o=document.defaultView.getComputedStyle(e,""):e.currentStyle&&(o=e.currentStyle),void 0===t?o:o[t];t in n||-1!==t.indexOf("webkit")||(t="-webkit-"+t),n[t]=o+("string"==typeof o?"":"px")}}function Je(e,t){var o="";if("string"==typeof e)o=e;else do{var n=Ge(e,"transform");n&&"none"!==n&&(o=n+" "+o)}while(!t&&(e=e.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(o)}function Ze(e,t,o){if(e){var n=e.getElementsByTagName(t),i=0,a=n.length;if(o)for(;i=a:i<=a))return n;if(n===Xe())break;n=at(n,!1)}return!1}function tt(e,t,o,n){for(var i=0,a=0,s=e.children;a2&&void 0!==arguments[2]?arguments[2]:{},n=o.evt,i=function(e,t){if(null==e)return{};var o,n,i=function(e,t){if(null==e)return{};var o={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(-1!==t.indexOf(n))continue;o[n]=e[n]}return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=n&&"none"===o[eo]||a&&"none"===o[eo]&&l+c>n)?"vertical":"horizontal"},io=function(e){function t(e,o){return function(n,i,a,s){var r=n.options.group.name&&i.options.group.name&&n.options.group.name===i.options.group.name;if(null==e&&(o||r))return!0;if(null==e||!1===e)return!1;if(o&&"clone"===e)return e;if("function"==typeof e)return t(e(n,i,a,s),o)(n,i,a,s);var l=(o?n:i).options.group.name;return!0===e||"string"==typeof e&&e===l||e.join&&e.indexOf(l)>-1}}var o={},n=e.group;n&&"object"==De(n)||(n={name:n}),o.name=n.name,o.checkPull=t(n.pull,!0),o.checkPut=t(n.put),o.revertClone=n.revertClone,e.group=o},ao=function(){!oo&&wt&&Ge(wt,"display","none")},so=function(){!oo&&wt&&Ge(wt,"display","")};Xt&&!$e&&document.addEventListener("click",function(e){if(Wt)return e.preventDefault(),e.stopPropagation&&e.stopPropagation(),e.stopImmediatePropagation&&e.stopImmediatePropagation(),Wt=!1,!1},!0);var ro=function(e){if(Ct){e=e.touches?e.touches[0]:e;var t=(i=e.clientX,a=e.clientY,jt.some(function(e){var t=e[ut].options.emptyInsertThreshold;if(t&&!ot(e)){var o=Qe(e),n=i>=o.left-t&&i<=o.right+t,r=a>=o.top-t&&a<=o.bottom+t;return n&&r?s=e:void 0}}),s);if(t){var o={};for(var n in e)e.hasOwnProperty(n)&&(o[n]=e[n]);o.target=o.rootEl=t,o.preventDefault=void 0,o.stopPropagation=void 0,t[ut]._onDragOver(o)}}var i,a,s},lo=function(e){Ct&&Ct.parentNode[ut]._isOutsideThisEl(e.target)};function co(e,t){if(!e||!e.nodeType||1!==e.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(e));this.el=e,this.options=t=Me({},t),e[ut]=this;var o={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(e.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return no(e,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(e,t){e.setData("Text",t.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==co.supportPointer&&"PointerEvent"in window&&(!qe||Be),emptyInsertThreshold:5};for(var n in gt.initializePlugins(this,e,o),o)!(n in t)&&(t[n]=o[n]);for(var i in io(t),this)"_"===i.charAt(0)&&"function"==typeof this[i]&&(this[i]=this[i].bind(this));this.nativeDraggable=!t.forceFallback&&to,this.nativeDraggable&&(this.options.touchStartThreshold=1),t.supportPointer?ze(e,"pointerdown",this._onTapStart):(ze(e,"mousedown",this._onTapStart),ze(e,"touchstart",this._onTapStart)),this.nativeDraggable&&(ze(e,"dragover",this),ze(e,"dragenter",this)),jt.push(this.el),t.store&&t.store.get&&this.sort(t.store.get(this)||[]),Me(this,mt())}function uo(e,t,o,n,i,a,s,r){var l,c,d=e[ut],u=d.options.onMove;return!window.CustomEvent||Fe||Oe?(l=document.createEvent("Event")).initEvent("move",!0,!0):l=new CustomEvent("move",{bubbles:!0,cancelable:!0}),l.to=t,l.from=e,l.dragged=o,l.draggedRect=n,l.related=i||t,l.relatedRect=a||Qe(t),l.willInsertAfter=r,l.originalEvent=s,e.dispatchEvent(l),u&&(c=u.call(d,l,s)),c}function mo(e){e.draggable=!1}function po(){Jt=!1}function ho(e){for(var t=e.tagName+e.className+e.src+e.href+e.textContent,o=t.length,n=0;o--;)n+=t.charCodeAt(o);return n.toString(36)}function go(e){return setTimeout(e,0)}function fo(e){return clearTimeout(e)}co.prototype={constructor:co,_isOutsideThisEl:function(e){this.el.contains(e)||e===this.el||(_t=null)},_getDirection:function(e,t){return"function"==typeof this.options.direction?this.options.direction.call(this,e,t,Ct):this.options.direction},_onTapStart:function(e){if(e.cancelable){var t=this,o=this.el,n=this.options,i=n.preventOnFilter,a=e.type,s=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,r=(s||e).target,l=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||r,c=n.filter;if(function(e){Zt.length=0;var t=e.getElementsByTagName("input"),o=t.length;for(;o--;){var n=t[o];n.checked&&Zt.push(n)}}(o),!Ct&&!(/mousedown|pointerdown/.test(a)&&0!==e.button||n.disabled)&&!l.isContentEditable&&(this.nativeDraggable||!qe||!r||"SELECT"!==r.tagName.toUpperCase())&&!((r=We(r,n.draggable,o,!1))&&r.animated||St===r)){if(It=nt(r),Mt=nt(r,n.draggable),"function"==typeof c){if(c.call(this,e,r,this))return yt({sortable:t,rootEl:l,name:"filter",targetEl:r,toEl:o,fromEl:o}),bt("filter",t,{evt:e}),void(i&&e.preventDefault())}else if(c&&(c=c.split(",").some(function(n){if(n=We(l,n.trim(),o,!1))return yt({sortable:t,rootEl:n,name:"filter",targetEl:r,fromEl:o,toEl:o}),bt("filter",t,{evt:e}),!0})))return void(i&&e.preventDefault());n.handle&&!We(l,n.handle,o,!1)||this._prepareDragStart(e,s,r)}}},_prepareDragStart:function(e,t,o){var n,i=this,a=i.el,s=i.options,r=a.ownerDocument;if(o&&!Ct&&o.parentNode===a){var l=Qe(o);if(xt=a,vt=(Ct=o).parentNode,kt=Ct.nextSibling,St=o,Nt=s.group,co.dragged=Ct,Lt={target:Ct,clientX:(t||e).clientX,clientY:(t||e).clientY},qt=Lt.clientX-l.left,Bt=Lt.clientY-l.top,this._lastX=(t||e).clientX,this._lastY=(t||e).clientY,Ct.style["will-change"]="all",n=function(){bt("delayEnded",i,{evt:e}),co.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!Re&&i.nativeDraggable&&(Ct.draggable=!0),i._triggerDragStart(e,t),yt({sortable:i,name:"choose",originalEvent:e}),Ye(Ct,s.chosenClass,!0))},s.ignore.split(",").forEach(function(e){Ze(Ct,e.trim(),mo)}),ze(r,"dragover",ro),ze(r,"mousemove",ro),ze(r,"touchmove",ro),s.supportPointer?(ze(r,"pointerup",i._onDrop),!this.nativeDraggable&&ze(r,"pointercancel",i._onDrop)):(ze(r,"mouseup",i._onDrop),ze(r,"touchend",i._onDrop),ze(r,"touchcancel",i._onDrop)),Re&&this.nativeDraggable&&(this.options.touchStartThreshold=4,Ct.draggable=!0),bt("delayStart",this,{evt:e}),!s.delay||s.delayOnTouchOnly&&!t||this.nativeDraggable&&(Oe||Fe))n();else{if(co.eventCanceled)return void this._onDrop();s.supportPointer?(ze(r,"pointerup",i._disableDelayedDrag),ze(r,"pointercancel",i._disableDelayedDrag)):(ze(r,"mouseup",i._disableDelayedDrag),ze(r,"touchend",i._disableDelayedDrag),ze(r,"touchcancel",i._disableDelayedDrag)),ze(r,"mousemove",i._delayedDragTouchMoveHandler),ze(r,"touchmove",i._delayedDragTouchMoveHandler),s.supportPointer&&ze(r,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(n,s.delay)}}},_delayedDragTouchMoveHandler:function(e){var t=e.touches?e.touches[0]:e;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){Ct&&mo(Ct),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var e=this.el.ownerDocument;Ue(e,"mouseup",this._disableDelayedDrag),Ue(e,"touchend",this._disableDelayedDrag),Ue(e,"touchcancel",this._disableDelayedDrag),Ue(e,"pointerup",this._disableDelayedDrag),Ue(e,"pointercancel",this._disableDelayedDrag),Ue(e,"mousemove",this._delayedDragTouchMoveHandler),Ue(e,"touchmove",this._delayedDragTouchMoveHandler),Ue(e,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(e,t){t=t||"touch"==e.pointerType&&e,!this.nativeDraggable||t?this.options.supportPointer?ze(document,"pointermove",this._onTouchMove):ze(document,t?"touchmove":"mousemove",this._onTouchMove):(ze(Ct,"dragend",this),ze(xt,"dragstart",this._onDragStart));try{document.selection?go(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(e){}},_dragStarted:function(e,t){if(Ht=!1,xt&&Ct){bt("dragStarted",this,{evt:t}),this.nativeDraggable&&ze(document,"dragover",lo);var o=this.options;!e&&Ye(Ct,o.dragClass,!1),Ye(Ct,o.ghostClass,!0),co.active=this,e&&this._appendGhost(),yt({sortable:this,name:"start",originalEvent:t})}else this._nulling()},_emulateDragOver:function(){if(Ft){this._lastX=Ft.clientX,this._lastY=Ft.clientY,ao();for(var e=document.elementFromPoint(Ft.clientX,Ft.clientY),t=e;e&&e.shadowRoot&&(e=e.shadowRoot.elementFromPoint(Ft.clientX,Ft.clientY))!==t;)t=e;if(Ct.parentNode[ut]._isOutsideThisEl(e),t)do{if(t[ut]){if(t[ut]._onDragOver({clientX:Ft.clientX,clientY:Ft.clientY,target:e,rootEl:t})&&!this.options.dragoverBubble)break}e=t}while(t=He(t));so()}},_onTouchMove:function(e){if(Lt){var t=this.options,o=t.fallbackTolerance,n=t.fallbackOffset,i=e.touches?e.touches[0]:e,a=wt&&Je(wt,!0),s=wt&&a&&a.a,r=wt&&a&&a.d,l=Qt&&Vt&&it(Vt),c=(i.clientX-Lt.clientX+n.x)/(s||1)+(l?l[0]-Gt[0]:0)/(s||1),d=(i.clientY-Lt.clientY+n.y)/(r||1)+(l?l[1]-Gt[1]:0)/(r||1);if(!co.active&&!Ht){if(o&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))i.right+a||e.clientY>n.bottom&&e.clientX>n.left:e.clientY>i.bottom+a||e.clientX>n.right&&e.clientY>n.top}(e,i,this)&&!g.animated){if(g===Ct)return N(!1);if(g&&a===e.target&&(s=g),s&&(o=Qe(s)),!1!==uo(xt,a,Ct,t,s,o,e,!!s))return P(),g&&g.nextSibling?a.insertBefore(Ct,g.nextSibling):a.appendChild(Ct),vt=a,D(),N(!0)}else if(g&&function(e,t,o){var n=Qe(tt(o.el,0,o.options,!0)),i=dt(o.el,o.options,wt),a=10;return t?e.clientXd+c*a/2:lu-Ut)return-zt}else if(l>d+c*(1-i)/2&&lu-c*a/2))return l>d+c/2?1:-1;return 0}(e,s,o,i,w?1:r.swapThreshold,null==r.invertedSwapThreshold?r.swapThreshold:r.invertedSwapThreshold,Yt,_t===s),0!==b){var T=nt(Ct);do{T-=b,C=vt.children[T]}while(C&&("none"===Ge(C,"display")||C===wt))}if(0===b||C===s)return N(!1);_t=s,zt=b;var E=s.nextElementSibling,I=!1,A=uo(xt,a,Ct,t,s,o,e,I=1===b);if(!1!==A)return 1!==A&&-1!==A||(I=1===A),Jt=!0,setTimeout(po,30),P(),I&&!E?a.appendChild(Ct):s.parentNode.insertBefore(Ct,I?E:s),k&<(k,0,S-k.scrollTop),vt=Ct.parentNode,void 0===y||Yt||(Ut=Math.abs(y-Qe(s)[x])),D(),N(!0)}if(a.contains(Ct))return N(!1)}return!1}function M(r,l){bt(r,p,Ne({evt:e,isOwner:d,axis:i?"vertical":"horizontal",revert:n,dragRect:t,targetRect:o,canSort:u,fromSortable:m,target:s,completed:N,onMove:function(o,n){return uo(xt,a,Ct,t,o,Qe(o),e,n)},changed:D},l))}function P(){M("dragOverAnimationCapture"),p.captureAnimationState(),p!==m&&m.captureAnimationState()}function N(t){return M("dragOverCompleted",{insertion:t}),t&&(d?c._hideClone():c._showClone(p),p!==m&&(Ye(Ct,Dt?Dt.options.ghostClass:c.options.ghostClass,!1),Ye(Ct,r.ghostClass,!0)),Dt!==p&&p!==co.active?Dt=p:p===co.active&&Dt&&(Dt=null),m===p&&(p._ignoreWhileAnimating=s),p.animateAll(function(){M("dragOverAnimationComplete"),p._ignoreWhileAnimating=null}),p!==m&&(m.animateAll(),m._ignoreWhileAnimating=null)),(s===Ct&&!Ct.animated||s===a&&!s.animated)&&(_t=null),r.dragoverBubble||e.rootEl||s===document||(Ct.parentNode[ut]._isOutsideThisEl(e.target),!t&&ro(e)),!r.dragoverBubble&&e.stopPropagation&&e.stopPropagation(),h=!0}function D(){At=nt(Ct),Pt=nt(Ct,r.draggable),yt({sortable:p,name:"change",toEl:a,newIndex:At,newDraggableIndex:Pt,originalEvent:e})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){Ue(document,"mousemove",this._onTouchMove),Ue(document,"touchmove",this._onTouchMove),Ue(document,"pointermove",this._onTouchMove),Ue(document,"dragover",ro),Ue(document,"mousemove",ro),Ue(document,"touchmove",ro)},_offUpEvents:function(){var e=this.el.ownerDocument;Ue(e,"mouseup",this._onDrop),Ue(e,"touchend",this._onDrop),Ue(e,"pointerup",this._onDrop),Ue(e,"pointercancel",this._onDrop),Ue(e,"touchcancel",this._onDrop),Ue(document,"selectstart",this)},_onDrop:function(e){var t=this.el,o=this.options;At=nt(Ct),Pt=nt(Ct,o.draggable),bt("drop",this,{evt:e}),vt=Ct&&Ct.parentNode,At=nt(Ct),Pt=nt(Ct,o.draggable),co.eventCanceled||(Ht=!1,Yt=!1,Kt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),fo(this.cloneId),fo(this._dragStartId),this.nativeDraggable&&(Ue(document,"drop",this),Ue(t,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),qe&&Ge(document.body,"user-select",""),Ge(Ct,"transform",""),e&&($t&&(e.cancelable&&e.preventDefault(),!o.dropBubble&&e.stopPropagation()),wt&&wt.parentNode&&wt.parentNode.removeChild(wt),(xt===vt||Dt&&"clone"!==Dt.lastPutMode)&&Tt&&Tt.parentNode&&Tt.parentNode.removeChild(Tt),Ct&&(this.nativeDraggable&&Ue(Ct,"dragend",this),mo(Ct),Ct.style["will-change"]="",$t&&!Ht&&Ye(Ct,Dt?Dt.options.ghostClass:this.options.ghostClass,!1),Ye(Ct,this.options.chosenClass,!1),yt({sortable:this,name:"unchoose",toEl:vt,newIndex:null,newDraggableIndex:null,originalEvent:e}),xt!==vt?(At>=0&&(yt({rootEl:vt,name:"add",toEl:vt,fromEl:xt,originalEvent:e}),yt({sortable:this,name:"remove",toEl:vt,originalEvent:e}),yt({rootEl:vt,name:"sort",toEl:vt,fromEl:xt,originalEvent:e}),yt({sortable:this,name:"sort",toEl:vt,originalEvent:e})),Dt&&Dt.save()):At!==It&&At>=0&&(yt({sortable:this,name:"update",toEl:vt,originalEvent:e}),yt({sortable:this,name:"sort",toEl:vt,originalEvent:e})),co.active&&(null!=At&&-1!==At||(At=It,Pt=Mt),yt({sortable:this,name:"end",toEl:vt,originalEvent:e}),this.save())))),this._nulling()},_nulling:function(){bt("nulling",this),xt=Ct=vt=wt=kt=Tt=St=Et=Lt=Ft=$t=At=Pt=It=Mt=_t=zt=Dt=Nt=co.dragged=co.ghost=co.clone=co.active=null;var e=this.el;Zt.forEach(function(t){e.contains(t)&&(t.checked=!0)}),Zt.length=Ot=Rt=0},handleEvent:function(e){switch(e.type){case"drop":case"dragend":this._onDrop(e);break;case"dragenter":case"dragover":Ct&&(this._onDragOver(e),function(e){e.dataTransfer&&(e.dataTransfer.dropEffect="move");e.cancelable&&e.preventDefault()}(e));break;case"selectstart":e.preventDefault()}},toArray:function(){for(var e,t=[],o=this.el.children,n=0,i=o.length,a=this.options;n{t.createEl("p",{text:e})});const o=t.createDiv("confirm-modal-buttons");new e.ButtonComponent(o).setButtonText(this.options.cancelText).onClick(()=>this.close()),new e.ButtonComponent(o).setButtonText(this.options.confirmText).setCta().onClick(async()=>{await this.options.onConfirm(),this.close()})}onClose(){const{contentEl:e}=this;e.empty()}static show(e,t){new No(e,t).open()}}class Do extends e.Modal{constructor(e,t){super(e),this.changelogContent="",this.changelogLoaded=!1,this.plugin=t}async loadChangelog(){try{const t=await e.request({url:"https://raw.githubusercontent.com/PKM-er/obsidian-editing-toolbar/master/CHANGELOG.md",method:"GET"});if(!t)throw new Error("无法获取 Changelog 内容");{const e=t.split("\n");let o="",n=[],i=!1;for(const t of e)if(t.startsWith("## ")&&!o)o=t.substring(3).trim(),i=!0,n.push(t);else{if(t.startsWith("## ")&&i)break;i&&n.push(t)}this.changelogContent=n.join("\n")}}catch(e){console.error("加载 Changelog 时出错:",e),this.changelogContent="### 无法加载更新说明\n\n请[点击此处查看最新更新说明](https://github.com/PKM-er/obsidian-editing-toolbar/blob/master/CHANGELOG.md)"}this.changelogLoaded=!0,this.updateChangelogDisplay()}updateChangelogDisplay(){this.changelogContainer&&this.changelogContentEl&&this.changelogLoaded&&(this.changelogContentEl.empty(),e.MarkdownRenderer.renderMarkdown(this.changelogContent,this.changelogContentEl,"",this.plugin))}async fixCommandIds(){try{const t={"editor:toggle-numbered-list":"editing-toolbar:toggle-numbered-list","editor:toggle-bullet-list":"editing-toolbar:toggle-bullet-list","editor:toggle-highlight":"editing-toolbar:toggle-highlight","toggle-highlight":"editing-toolbar:toggle-highlight","editing-toolbar:editor:toggle-bold":"editing-toolbar:toggle-bold","editing-toolbar:editor:toggle-italics":"editing-toolbar:toggle-italics","editing-toolbar:editor:toggle-strikethrough":"editing-toolbar:toggle-strikethrough","editing-toolbar:editor:toggle-inline-math":"editing-toolbar:toggle-inline-math","editing-toolbar:editor:insert-callout":"editing-toolbar:insert-callout","editing-toolbar:editor:insert-link":"editing-toolbar:insert-link","cMenuToolbar-Divider-Line":"editingToolbar-Divider-Line"};let o=!1;const n=this.plugin.settings,i=e=>{e&&Array.isArray(e)&&e.forEach(e=>{e.id&&t[e.id]&&(e.id=t[e.id],o=!0),"editing-toolbar:format-eraser"===e.id&&(e.icon="eraser",o=!0),"editing-toolbar:change-font-color"===e.id&&(e.icon='',o=!0),e.SubmenuCommands&&i(e.SubmenuCommands)})},a=e=>{if(!e||!Array.isArray(e))return!1;for(const t of e){if("editing-toolbar:toggle-format-brush"===t.id)return!0;if(t.SubmenuCommands){if(a(t.SubmenuCommands))return!0}}return!1},s=e=>{if(!e||!Array.isArray(e))return!1;if(!a(e)&&e.length>=2){const t={id:"editing-toolbar:toggle-format-brush",name:"Format Brush",icon:"paintbrush"};return e.splice(2,0,t),!0}return!1},r=e=>{if(!e||!Array.isArray(e))return!1;for(const t of e)if("SubmenuCommands-text-tools"===t.id)return!0;return!1},l=e=>{if(!e||!Array.isArray(e))return!1;if(!r(e)){const t=Z.menuCommands.find(e=>"SubmenuCommands-text-tools"===e.id);if(t){const o=11;return e.length>=o?e.splice(o,0,t):e.push(t),!0}}return!1};n.menuCommands&&(i(n.menuCommands),s(n.menuCommands)&&(o=!0),l(n.menuCommands)&&(o=!0)),n.enableMultipleConfig&&(n.followingCommands&&(i(n.followingCommands),s(n.followingCommands)&&(o=!0)),n.topCommands&&(i(n.topCommands),s(n.topCommands)&&(o=!0),l(n.topCommands)&&(o=!0)),n.fixedCommands&&(i(n.fixedCommands),s(n.fixedCommands)&&(o=!0)),n.mobileCommands&&(i(n.mobileCommands),s(n.mobileCommands)&&(o=!0))),o?(await this.plugin.saveSettings(),new e.Notice(v("Command IDs have been successfully repaired!")),dispatchEvent(new Event("editingToolbar-NewCommand"))):new e.Notice(v("No command IDs need to be repaired"))}catch(t){console.error("修复命令ID时出错:",t),new e.Notice(v("Error repairing command IDs, please check the console for details"))}}async reloadPlugin(e){const{plugins:t}=this.app;try{await t.disablePlugin(e),await t.enablePlugin(e)}catch(e){console.error(e)}}async restoreDefaultSettings(){try{const t=this.plugin.settings.lastVersion,o=this.plugin.settings.customCommands;this.plugin.settings={...Z,ai:{...Z.ai,customPromptHistory:[],customPromptTemplates:L()},lastVersion:t,customCommands:o},await this.plugin.saveSettings(),new e.Notice(v("Successfully restored default settings! (Custom commands preserved)")),this.reloadPlugin(this.plugin.manifest.id),this.close()}catch(t){console.error("恢复默认设置时出错:",t),new e.Notice(v("Error restoring default settings, please check the console for details"))}}onOpen(){const{contentEl:t}=this;t.createEl("h2",{text:`${this.plugin.manifest.name} v${this.plugin.manifest.version} · ${v("Tips")}`}),t.createEl("p",{text:v("Notice:")});const o=t.createEl("ul");o.createEl("li",{text:v("⚠️This update is not compatible with 2.x version command ids, please click [Repair command] to be compatible")}),o.createEl("li",{text:v("⚠️If you want to restore the default settings, please click [Restore default settings]")}),this.changelogContainer=t.createDiv({cls:"changelog-container"}),this.changelogContainer.createEl("h3",{text:v("Latest Changes")}),this.changelogContentEl=this.changelogContainer.createDiv({cls:"changelog-content"}),this.changelogContentEl.setText(v("Loading changelog...")),setTimeout(()=>{this.loadChangelog()},100),new e.Setting(t).setName(v("🔧Data repair")).setDesc(v("This update changed the ID of some commands, please click this button to repair the commands to ensure the toolbar works properly")).addButton(e=>e.setButtonText(v("Repair command ID")).onClick(async()=>{await this.fixCommandIds()})),new e.Setting(t).setName(v("🔄Restore default settings")).setDesc(v("This will reset all your custom configurations, but custom commands will be preserved")).addButton(e=>e.setButtonText(v("Restore default")).onClick(async()=>{No.show(this.app,{message:v("Are you sure you want to restore all settings to default? But custom commands will be preserved."),onConfirm:async()=>{await this.restoreDefaultSettings()}})})),new e.Setting(t).setName(v("📋View full changelog")).setDesc(v("Open the complete changelog in your browser")).addButton(e=>e.setButtonText(v("Open changelog")).onClick(()=>{window.open("https://github.com/PKM-er/obsidian-editing-toolbar/blob/master/CHANGELOG.md","_blank")})),new e.Setting(t).addButton(e=>e.setButtonText(v("Close")).onClick(()=>{this.close()})),t.createEl("style",{text:"\n .changelog-container {\n margin-top: 20px;\n margin-bottom: 20px;\n padding: 10px;\n border: 1px solid var(--background-modifier-border);\n border-radius: 5px;\n max-height: 200px;\n overflow-y: auto;\n }\n .changelog-content {\n padding: 0 10px;\n }\n .changelog-content a {\n text-decoration: underline;\n }\n "})}onClose(){const{contentEl:e}=this;e.empty()}}function Lo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}
+/*! Pickr 1.8.4 MIT | https://github.com/Simonwep/pickr */
+var Fo=function(e){var t={exports:{}};return e(t,t.exports),t.exports}(function(e,t){self,e.exports=(()=>{var e={d:(t,o)=>{for(var n in o)e.o(o,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:o[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.d(t,{default:()=>N});var o={};function n(e,t,o,n){let i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};t instanceof HTMLCollection||t instanceof NodeList?t=Array.from(t):Array.isArray(t)||(t=[t]),Array.isArray(o)||(o=[o]);for(const a of t)for(const t of o)a[e](t,n,{capture:!1,...i});return Array.prototype.slice.call(arguments,1)}e.r(o),e.d(o,{adjustableInputNumbers:()=>d,createElementFromString:()=>s,createFromTemplate:()=>r,eventPath:()=>l,off:()=>a,on:()=>i,resolveElement:()=>c});const i=n.bind(null,"addEventListener"),a=n.bind(null,"removeEventListener");function s(e){const t=document.createElement("div");return t.innerHTML=e.trim(),t.firstElementChild}function r(e){const t=(e,t)=>{const o=e.getAttribute(t);return e.removeAttribute(t),o},o=function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const i=t(e,":obj"),a=t(e,":ref"),s=i?n[i]={}:n;a&&(n[a]=e);for(const n of Array.from(e.children)){const e=t(n,":arr"),i=o(n,e?{}:s);e&&(s[e]||(s[e]=[])).push(Object.keys(i).length?i:n)}return n};return o(s(e))}function l(e){let t=e.path||e.composedPath&&e.composedPath();if(t)return t;let o=e.target.parentElement;for(t=[e.target,o];o=o.parentElement;)t.push(o);return t.push(document,window),t}function c(e){return e instanceof Element?e:"string"==typeof e?e.split(/>>/g).reduce((e,t,o,n)=>(e=e.querySelector(t),o1&&void 0!==arguments[1]?arguments[1]:e=>e;function o(o){const n=[.001,.01,.1][Number(o.shiftKey||2*o.ctrlKey)]*(o.deltaY<0?1:-1);let i=0,a=e.selectionStart;e.value=e.value.replace(/[\d.]+/g,(e,o)=>o<=a&&o+e.length>=a?(a=o,t(Number(e),n,i)):(i++,e)),e.focus(),e.setSelectionRange(a,a),o.preventDefault(),e.dispatchEvent(new Event("input"))}i(e,"focus",()=>i(window,"wheel",o,{passive:!1})),i(e,"blur",()=>a(window,"wheel",o))}const{min:u,max:m,floor:p,round:h}=Math;function g(e,t,o){t/=100,o/=100;const n=p(e=e/360*6),i=e-n,a=o*(1-t),s=o*(1-i*t),r=o*(1-(1-i)*t),l=n%6;return[255*[o,s,a,a,r,o][l],255*[r,o,o,s,a,a][l],255*[a,a,r,o,o,s][l]]}function f(e,t,o){return g(e,t,o).map(e=>h(e).toString(16).padStart(2,"0"))}function b(e,t,o){const n=g(e,t,o),i=n[0]/255,a=n[1]/255,s=n[2]/255,r=u(1-i,1-a,1-s);return[100*(1===r?0:(1-i-r)/(1-r)),100*(1===r?0:(1-a-r)/(1-r)),100*(1===r?0:(1-s-r)/(1-r)),100*r]}function y(e,t,o){const n=(2-(t/=100))*(o/=100)/2;return 0!==n&&(t=1===n?0:n<.5?t*o/(2*n):t*o/(2-2*n)),[e,100*t,100*n]}function C(e,t,o){const n=u(e/=255,t/=255,o/=255),i=m(e,t,o),a=i-n;let s,r;if(0===a)s=r=0;else{r=a/i;const n=((i-e)/6+a/2)/a,l=((i-t)/6+a/2)/a,c=((i-o)/6+a/2)/a;e===i?s=c-l:t===i?s=1/3+n-c:o===i&&(s=2/3+l-n),s<0?s+=1:s>1&&(s-=1)}return[360*s,100*r,100*i]}function v(e,t,o,n){return t/=100,o/=100,[...C(255*(1-u(1,(e/=100)*(1-(n/=100))+n)),255*(1-u(1,t*(1-n)+n)),255*(1-u(1,o*(1-n)+n)))]}function w(e,t,o){t/=100;const n=2*(t*=(o/=100)<.5?o:1-o)/(o+t)*100,i=100*(o+t);return[e,isNaN(n)?0:n,i]}function x(e){return C(...e.match(/.{2}/g).map(e=>parseInt(e,16)))}function k(e){e=e.match(/^[a-zA-Z]+$/)?function(e){if("black"===e.toLowerCase())return"#000";const t=document.createElement("canvas").getContext("2d");return t.fillStyle=e,"#000"===t.fillStyle?null:t.fillStyle}(e):e;const t={cmyk:/^cmyk[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)/i,rgba:/^((rgba)|rgb)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hsla:/^((hsla)|hsl)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hsva:/^((hsva)|hsv)[\D]+([\d.]+)[\D]+([\d.]+)[\D]+([\d.]+)[\D]*?([\d.]+|$)/i,hexa:/^#?(([\dA-Fa-f]{3,4})|([\dA-Fa-f]{6})|([\dA-Fa-f]{8}))$/i},o=e=>e.map(e=>/^(|\d+)\.\d+|\d+$/.test(e)?Number(e):void 0);let n;e:for(const i in t){if(!(n=t[i].exec(e)))continue;const a=e=>!!n[2]==("number"==typeof e);switch(i){case"cmyk":{const[,e,t,a,s]=o(n);if(e>100||t>100||a>100||s>100)break e;return{values:v(e,t,a,s),type:i}}case"rgba":{const[,,,e,t,s,r]=o(n);if(e>255||t>255||s>255||r<0||r>1||!a(r))break e;return{values:[...C(e,t,s),r],a:r,type:i}}case"hexa":{let[,e]=n;4!==e.length&&3!==e.length||(e=e.split("").map(e=>e+e).join(""));const t=e.substring(0,6);let o=e.substring(6);return o=o?parseInt(o,16)/255:void 0,{values:[...x(t),o],a:o,type:i}}case"hsla":{const[,,,e,t,s,r]=o(n);if(e>360||t>100||s>100||r<0||r>1||!a(r))break e;return{values:[...w(e,t,s),r],a:r,type:i}}case"hsva":{const[,,,e,t,s,r]=o(n);if(e>360||t>100||s>100||r<0||r>1||!a(r))break e;return{values:[e,t,s,r],a:r,type:i}}}}return{values:null,type:null}}function S(){const e=(e,t)=>function(){let o=arguments.length>0&&void 0!==arguments[0]?arguments[0]:-1;return t(~o?e.map(e=>Number(e.toFixed(o))):e)},t={h:arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,s:arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,v:arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a:arguments.length>3&&void 0!==arguments[3]?arguments[3]:1,toHSVA(){const o=[t.h,t.s,t.v,t.a];return o.toString=e(o,e=>`hsva(${e[0]}, ${e[1]}%, ${e[2]}%, ${t.a})`),o},toHSLA(){const o=[...y(t.h,t.s,t.v),t.a];return o.toString=e(o,e=>`hsla(${e[0]}, ${e[1]}%, ${e[2]}%, ${t.a})`),o},toRGBA(){const o=[...g(t.h,t.s,t.v),t.a];return o.toString=e(o,e=>`rgba(${e[0]}, ${e[1]}, ${e[2]}, ${t.a})`),o},toCMYK(){const o=b(t.h,t.s,t.v);return o.toString=e(o,e=>`cmyk(${e[0]}%, ${e[1]}%, ${e[2]}%, ${e[3]}%)`),o},toHEXA(){const e=f(t.h,t.s,t.v),o=t.a>=1?"":Number((255*t.a).toFixed(0)).toString(16).toUpperCase().padStart(2,"0");return o&&e.push(o),e.toString=()=>`#${e.join("").toUpperCase()}`,e},clone:()=>S(t.h,t.s,t.v,t.a)};return t}const T=e=>Math.max(Math.min(e,1),0);function E(e){const t={options:Object.assign({lock:null,onchange:()=>0,onstop:()=>0},e),_keyboard(e){const{options:o}=t,{type:n,key:i}=e;if(document.activeElement===o.wrapper){const{lock:o}=t.options,a="ArrowUp"===i,s="ArrowRight"===i,r="ArrowDown"===i,l="ArrowLeft"===i;if("keydown"===n&&(a||s||r||l)){let n=0,i=0;"v"===o?n=a||s?1:-1:"h"===o?n=a||s?-1:1:(i=a?-1:r?1:0,n=l?-1:s?1:0),t.update(T(t.cache.x+.01*n),T(t.cache.y+.01*i)),e.preventDefault()}else i.startsWith("Arrow")&&(t.options.onstop(),e.preventDefault())}},_tapstart(e){i(document,["mouseup","touchend","touchcancel"],t._tapstop),i(document,["mousemove","touchmove"],t._tapmove),e.cancelable&&e.preventDefault(),t._tapmove(e)},_tapmove(e){const{options:o,cache:n}=t,{lock:i,element:a,wrapper:s}=o,r=s.getBoundingClientRect();let l=0,c=0;if(e){const t=e&&e.touches&&e.touches[0];l=e?(t||e).clientX:0,c=e?(t||e).clientY:0,lr.left+r.width&&(l=r.left+r.width),cr.top+r.height&&(c=r.top+r.height),l-=r.left,c-=r.top}else n&&(l=n.x*r.width,c=n.y*r.height);"h"!==i&&(a.style.left=`calc(${l/r.width*100}% - ${a.offsetWidth/2}px)`),"v"!==i&&(a.style.top=`calc(${c/r.height*100}% - ${a.offsetHeight/2}px)`),t.cache={x:l/r.width,y:c/r.height};const d=T(l/r.width),u=T(c/r.height);switch(i){case"v":return o.onchange(d);case"h":return o.onchange(u);default:return o.onchange(d,u)}},_tapstop(){t.options.onstop(),a(document,["mouseup","touchend","touchcancel"],t._tapstop),a(document,["mousemove","touchmove"],t._tapmove)},trigger(){t._tapmove()},update(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;const{left:n,top:i,width:a,height:s}=t.options.wrapper.getBoundingClientRect();"h"===t.options.lock&&(o=e),t._tapmove({clientX:n+a*e,clientY:i+s*o})},destroy(){const{options:e,_tapstart:o,_keyboard:n}=t;a(document,["keydown","keyup"],n),a([e.wrapper,e.element],"mousedown",o),a([e.wrapper,e.element],"touchstart",o,{passive:!1})}},{options:o,_tapstart:n,_keyboard:s}=t;return i([o.wrapper,o.element],"mousedown",n),i([o.wrapper,o.element],"touchstart",n,{passive:!1}),i(document,["keydown","keyup"],s),t}function I(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};e=Object.assign({onchange:()=>0,className:"",elements:[]},e);const t=i(e.elements,"click",t=>{e.elements.forEach(o=>o.classList[t.target===o?"add":"remove"](e.className)),e.onchange(t),t.stopPropagation()});return{destroy:()=>a(...t)}}const A={variantFlipOrder:{start:"sme",middle:"mse",end:"ems"},positionFlipOrder:{top:"tbrl",right:"rltb",bottom:"btrl",left:"lrbt"},position:"bottom",margin:8},M=(e,t,o)=>{const{container:n,margin:i,position:a,variantFlipOrder:s,positionFlipOrder:r}={container:document.documentElement.getBoundingClientRect(),...A,...o},{left:l,top:c}=t.style;t.style.left="0",t.style.top="0";const d=e.getBoundingClientRect(),u=t.getBoundingClientRect(),m={t:d.top-u.height-i,b:d.bottom+i,r:d.right+i,l:d.left-u.width-i},p={vs:d.left,vm:d.left+d.width/2+-u.width/2,ve:d.left+d.width-u.width,hs:d.top,hm:d.bottom-d.height/2-u.height/2,he:d.bottom-u.height},[h,g="middle"]=a.split("-"),f=r[h],b=s[g],{top:y,left:C,bottom:v,right:w}=n;for(const e of f){const o="t"===e||"b"===e,n=m[e],[i,a]=o?["top","left"]:["left","top"],[s,r]=o?[u.height,u.width]:[u.width,u.height],[l,c]=o?[v,w]:[w,v],[d,h]=o?[y,C]:[C,y];if(!(nl))for(const s of b){const l=p[(o?"v":"h")+s];if(!(lc))return t.style[a]=l-u[a]+"px",t.style[i]=n-u[i]+"px",e+s}}return t.style.left=l,t.style.top=c,null};function P(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}class N{constructor(e){P(this,"_initializingActive",!0),P(this,"_recalc",!0),P(this,"_nanopop",null),P(this,"_root",null),P(this,"_color",S()),P(this,"_lastColor",S()),P(this,"_swatchColors",[]),P(this,"_setupAnimationFrame",null),P(this,"_eventListener",{init:[],save:[],hide:[],show:[],clear:[],change:[],changestop:[],cancel:[],swatchselect:[]}),this.options=e=Object.assign({...N.DEFAULT_OPTIONS},e);const{swatches:t,components:o,theme:n,sliders:i,lockOpacity:a,padding:s}=e;["nano","monolith"].includes(n)&&!i&&(e.sliders="h"),o.interaction||(o.interaction={});const{preview:r,opacity:l,hue:c,palette:d}=o;o.opacity=!a&&l,o.palette=d||r||l||c,this._preBuild(),this._buildComponents(),this._bindEvents(),this._finalBuild(),t&&t.length&&t.forEach(e=>this.addSwatch(e));const{button:u,app:m}=this._root;this._nanopop=((e,t,o)=>{const n="object"!=typeof e||e instanceof HTMLElement?{reference:e,popper:t,...o}:e;return{update(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:n;const{reference:t,popper:o}=Object.assign(n,e);if(!o||!t)throw new Error("Popper- or reference-element missing.");return M(t,o,n)}}})(u,m,{margin:s}),u.setAttribute("role","button"),u.setAttribute("aria-label",this._t("btn:toggle"));const p=this;this._setupAnimationFrame=requestAnimationFrame(function t(){if(!m.offsetWidth)return p._setupAnimationFrame=requestAnimationFrame(t);p.setColor(e.default),p._rePositioningPicker(),e.defaultRepresentation&&(p._representation=e.defaultRepresentation,p.setColorRepresentation(p._representation)),e.showAlways&&p.show(),p._initializingActive=!1,p._emit("init")})}_preBuild(){const{options:e}=this;for(const t of["el","container"])e[t]=c(e[t]);this._root=(e=>{const{components:t,useAsButton:o,inline:n,appClass:i,theme:a,lockOpacity:s}=e.options,l=e=>e?"":'style="display:none" hidden',c=t=>e._t(t),d=r(`\n \n\n ${o?"":''}\n\n \n \n \n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n \n \n\n \n\n \n \n\n \n \n \n \n \n\n \n \n \n \n \n \n `),u=d.interaction;return u.options.find(e=>!e.hidden&&!e.classList.add("active")),u.type=()=>u.options.find(e=>e.classList.contains("active")),d})(this),e.useAsButton&&(this._root.button=e.el),e.container.appendChild(this._root.root)}_finalBuild(){const e=this.options,t=this._root;if(e.container.removeChild(t.root),e.inline){const o=e.el.parentElement;e.el.nextSibling?o.insertBefore(t.app,e.el.nextSibling):o.appendChild(t.app)}else e.container.appendChild(t.app);e.useAsButton?e.inline&&e.el.remove():e.el.parentNode.replaceChild(t.root,e.el),e.disabled&&this.disable(),e.comparison||(t.button.style.transition="none",e.useAsButton||(t.preview.lastColor.style.transition="none")),this.hide()}_buildComponents(){const e=this,t=this.options.components,o=(e.options.sliders||"v").repeat(2),[n,i]=o.match(/^[vh]+$/g)?o:[],a=()=>this._color||(this._color=this._lastColor.clone()),s={palette:E({element:e._root.palette.picker,wrapper:e._root.palette.palette,onstop:()=>e._emit("changestop","slider",e),onchange(o,n){if(!t.palette)return;const i=a(),{_root:s,options:r}=e,{lastColor:l,currentColor:c}=s.preview;e._recalc&&(i.s=100*o,i.v=100-100*n,i.v<0&&(i.v=0),e._updateOutput("slider"));const d=i.toRGBA().toString(0);this.element.style.background=d,this.wrapper.style.background=`\n linear-gradient(to top, rgba(0, 0, 0, ${i.a}), transparent),\n linear-gradient(to left, hsla(${i.h}, 100%, 50%, ${i.a}), rgba(255, 255, 255, ${i.a}))\n `,r.comparison?r.useAsButton||e._lastColor||l.style.setProperty("--pcr-color",d):(s.button.style.setProperty("--pcr-color",d),s.button.classList.remove("clear"));const u=i.toHEXA().toString();for(const{el:t,color:o}of e._swatchColors)t.classList[u===o.toHEXA().toString()?"add":"remove"]("pcr-active");c.style.setProperty("--pcr-color",d)}}),hue:E({lock:"v"===i?"h":"v",element:e._root.hue.picker,wrapper:e._root.hue.slider,onstop:()=>e._emit("changestop","slider",e),onchange(o){if(!t.hue||!t.palette)return;const n=a();e._recalc&&(n.h=360*o),this.element.style.backgroundColor=`hsl(${n.h}, 100%, 50%)`,s.palette.trigger()}}),opacity:E({lock:"v"===n?"h":"v",element:e._root.opacity.picker,wrapper:e._root.opacity.slider,onstop:()=>e._emit("changestop","slider",e),onchange(o){if(!t.opacity||!t.palette)return;const n=a();e._recalc&&(n.a=Math.round(100*o)/100),this.element.style.background=`rgba(0, 0, 0, ${n.a})`,s.palette.trigger()}}),selectable:I({elements:e._root.interaction.options,className:"active",onchange(t){e._representation=t.target.getAttribute("data-type").toUpperCase(),e._recalc&&e._updateOutput("swatch")}})};this._components=s}_bindEvents(){const{_root:e,options:t}=this,o=[i(e.interaction.clear,"click",()=>this._clearColor()),i([e.interaction.cancel,e.preview.lastColor],"click",()=>{this.setHSVA(...(this._lastColor||this._color).toHSVA(),!0),this._emit("cancel")}),i(e.interaction.save,"click",()=>{!this.applyColor()&&!t.showAlways&&this.hide()}),i(e.interaction.result,["keyup","input"],e=>{this.setColor(e.target.value,!0)&&!this._initializingActive&&(this._emit("change",this._color,"input",this),this._emit("changestop","input",this)),e.stopImmediatePropagation()}),i(e.interaction.result,["focus","blur"],e=>{this._recalc="blur"===e.type,this._recalc&&this._updateOutput(null)}),i([e.palette.palette,e.palette.picker,e.hue.slider,e.hue.picker,e.opacity.slider,e.opacity.picker],["mousedown","touchstart"],()=>this._recalc=!0,{passive:!0})];if(!t.showAlways){const n=t.closeWithKey;o.push(i(e.button,"click",()=>this.isOpen()?this.hide():this.show()),i(document,"keyup",e=>this.isOpen()&&(e.key===n||e.code===n)&&this.hide()),i(document,["touchstart","mousedown"],t=>{this.isOpen()&&!l(t).some(t=>t===e.app||t===e.button)&&this.hide()},{capture:!0}))}if(t.adjustableNumbers){const t={rgba:[255,255,255,1],hsva:[360,100,100,1],hsla:[360,100,100,1],cmyk:[100,100,100,100]};d(e.interaction.result,(e,o,n)=>{const i=t[this.getColorRepresentation().toLowerCase()];if(i){const t=i[n],a=e+(t>=100?1e3*o:o);return a<=0?0:Number((a{n.isOpen()&&(t.closeOnScroll&&n.hide(),null===e?(e=setTimeout(()=>e=null,100),requestAnimationFrame(function t(){n._rePositioningPicker(),null!==e&&requestAnimationFrame(t)})):(clearTimeout(e),e=setTimeout(()=>e=null,100)))},{capture:!0}))}this._eventBindings=o}_rePositioningPicker(){const{options:e}=this;if(!e.inline&&!this._nanopop.update({container:document.body.getBoundingClientRect(),position:e.position})){const e=this._root.app,t=e.getBoundingClientRect();e.style.top=(window.innerHeight-t.height)/2+"px",e.style.left=(window.innerWidth-t.width)/2+"px"}}_updateOutput(e){const{_root:t,_color:o,options:n}=this;if(t.interaction.type()){const e=`to${t.interaction.type().getAttribute("data-type")}`;t.interaction.result.value="function"==typeof o[e]?o[e]().toString(n.outputPrecision):""}!this._initializingActive&&this._recalc&&this._emit("change",o,e,this)}_clearColor(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];const{_root:t,options:o}=this;o.useAsButton||t.button.style.setProperty("--pcr-color","rgba(0, 0, 0, 0.15)"),t.button.classList.add("clear"),o.showAlways||this.hide(),this._lastColor=null,this._initializingActive||e||(this._emit("save",null),this._emit("clear"))}_parseLocalColor(e){const{values:t,type:o,a:n}=k(e),{lockOpacity:i}=this.options,a=void 0!==n&&1!==n;return t&&3===t.length&&(t[3]=void 0),{values:!t||i&&a?null:t,type:o}}_t(e){return this.options.i18n[e]||N.I18N_DEFAULTS[e]}_emit(e){for(var t=arguments.length,o=new Array(t>1?t-1:0),n=1;ne(...o,this))}on(e,t){return this._eventListener[e].push(t),this}off(e,t){const o=this._eventListener[e]||[],n=o.indexOf(t);return~n&&o.splice(n,1),this}addSwatch(e){const{values:t}=this._parseLocalColor(e);if(t){const{_swatchColors:e,_root:o}=this,n=S(...t),a=s(``);return o.swatches.appendChild(a),e.push({el:a,color:n}),this._eventBindings.push(i(a,"click",()=>{this.setHSVA(...n.toHSVA(),!0),this._emit("swatchselect",n),this._emit("change",n,"swatch",this)})),!0}return!1}removeSwatch(e){const t=this._swatchColors[e];if(t){const{el:o}=t;return this._root.swatches.removeChild(o),this._swatchColors.splice(e,1),!0}return!1}applyColor(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];const{preview:t,button:o}=this._root,n=this._color.toRGBA().toString(0);return t.lastColor.style.setProperty("--pcr-color",n),this.options.useAsButton||o.style.setProperty("--pcr-color",n),o.classList.remove("clear"),this._lastColor=this._color.clone(),this._initializingActive||e||this._emit("save",this._color),this}destroy(){cancelAnimationFrame(this._setupAnimationFrame),this._eventBindings.forEach(e=>a(...e)),Object.keys(this._components).forEach(e=>this._components[e].destroy())}destroyAndRemove(){this.destroy();const{root:e,app:t}=this._root;e.parentElement&&e.parentElement.removeChild(e),t.parentElement.removeChild(t),Object.keys(this).forEach(e=>this[e]=null)}hide(){return!!this.isOpen()&&(this._root.app.classList.remove("visible"),this._emit("hide"),!0)}show(){return!this.options.disabled&&!this.isOpen()&&(this._root.app.classList.add("visible"),this._rePositioningPicker(),this._emit("show",this._color),this)}isOpen(){return this._root.app.classList.contains("visible")}setHSVA(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:360,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1,i=arguments.length>4&&void 0!==arguments[4]&&arguments[4];const a=this._recalc;if(this._recalc=!1,e<0||e>360||t<0||t>100||o<0||o>100||n<0||n>1)return!1;this._color=S(e,t,o,n);const{hue:s,opacity:r,palette:l}=this._components;return s.update(e/360),r.update(n),l.update(t/100,1-o/100),i||this.applyColor(),a&&this._updateOutput(),this._recalc=a,!0}setColor(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(null===e)return this._clearColor(t),!0;const{values:o,type:n}=this._parseLocalColor(e);if(o){const e=n.toUpperCase(),{options:i}=this._root.interaction,a=i.find(t=>t.getAttribute("data-type")===e);if(a&&!a.hidden)for(const e of i)e.classList[e===a?"add":"remove"]("active");return!!this.setHSVA(...o,t)&&this.setColorRepresentation(e)}return!1}setColorRepresentation(e){return e=e.toUpperCase(),!!this._root.interaction.options.find(t=>t.getAttribute("data-type").startsWith(e)&&!t.click())}getColorRepresentation(){return this._representation}getColor(){return this._color}getSelectedColor(){return this._lastColor}getRoot(){return this._root}disable(){return this.hide(),this.options.disabled=!0,this._root.button.classList.add("disabled"),this}enable(){return this.options.disabled=!1,this._root.button.classList.remove("disabled"),this}}return P(N,"utils",o),P(N,"version","1.8.4"),P(N,"I18N_DEFAULTS",{"ui:dialog":"color picker dialog","btn:toggle":"toggle color picker dialog","btn:swatch":"color swatch","btn:last-color":"use previous color","btn:save":"Save","btn:cancel":"Cancel","btn:clear":"Clear","aria:btn:save":"save and close","aria:btn:cancel":"cancel and close","aria:btn:clear":"clear and close","aria:input":"color input field","aria:palette":"color selection area","aria:hue":"hue selection slider","aria:opacity":"selection slider"}),P(N,"DEFAULT_OPTIONS",{appClass:null,theme:"classic",useAsButton:!1,padding:8,disabled:!1,comparison:!0,closeOnScroll:!1,outputPrecision:0,lockOpacity:!1,autoReposition:!0,container:"body",components:{interaction:{}},i18n:{},swatches:null,inline:!1,sliders:null,default:"#42445a",defaultRepresentation:null,position:"bottom-middle",adjustableNumbers:!0,showAlways:!1,closeWithKey:"Escape"}),P(N,"create",e=>new N(e)),t.default})()}),Oo=Lo(Fo),Ro=[],qo=[];!function(e,t){if(e&&"undefined"!=typeof document){var o,n=!0===t.prepend?"prepend":"append",i=!0===t.singleTag,a="string"==typeof t.container?document.querySelector(t.container):document.getElementsByTagName("head")[0];if(i){var s=Ro.indexOf(a);-1===s&&(s=Ro.push(a)-1,qo[s]={}),o=qo[s]&&qo[s][n]?qo[s][n]:qo[s][n]=r()}else o=r();65279===e.charCodeAt(0)&&(e=e.substring(1)),o.styleSheet?o.styleSheet.cssText+=e:o.appendChild(document.createTextNode(e))}function r(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),t.attributes)for(var o=Object.keys(t.attributes),i=0;i \') no-repeat center;background-size:0;transition:all 0.3s}.pickr .pcr-button::before{position:absolute;content:\'\';top:0;left:0;width:100%;height:100%;background:url(\'data:image/svg+xml;utf8, \');background-size:.5em;border-radius:.15em;z-index:-1}.pickr .pcr-button::before{z-index:initial}.pickr .pcr-button::after{position:absolute;content:\'\';top:0;left:0;height:100%;width:100%;transition:background 0.3s;background:var(--pcr-color);border-radius:.15em}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear::before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pickr *,.pcr-app *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr input:focus,.pickr input.pcr-active,.pickr button:focus,.pickr button.pcr-active,.pcr-app input:focus,.pcr-app input.pcr-active,.pcr-app button:focus,.pcr-app button.pcr-active{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-palette,.pickr .pcr-slider,.pcr-app .pcr-palette,.pcr-app .pcr-slider{transition:box-shadow 0.3s}.pickr .pcr-palette:focus,.pickr .pcr-slider:focus,.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(0,0,0,0.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:0.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity 0.3s, visibility 0s 0.3s;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;box-shadow:0 0.15em 1.5em 0 rgba(0,0,0,0.1),0 0 1em 0 rgba(0,0,0,0.03);left:0;top:0}.pcr-app.visible{transition:opacity 0.3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:0.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports (display: grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit, 1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:0.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all 0.15s;overflow:hidden;background:transparent;z-index:1}.pcr-app .pcr-swatches>button::before{position:absolute;content:\'\';top:0;left:0;width:100%;height:100%;background:url(\'data:image/svg+xml;utf8, \');background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button::after{content:\'\';position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,0.05);border-radius:0.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -0.2em 0 -0.2em}.pcr-app .pcr-interaction>*{margin:0 0.2em}.pcr-app .pcr-interaction input{letter-spacing:0.07em;font-size:0.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all 0.15s;padding:0.45em 0.5em;margin-top:0.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(0.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(66,133,244,0.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all 0.2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff;width:auto}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff}.pcr-app .pcr-interaction .pcr-save:hover,.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover{filter:brightness(0.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-cancel{background:#f44250}.pcr-app .pcr-interaction .pcr-clear:focus,.pcr-app .pcr-interaction .pcr-cancel:focus{box-shadow:0 0 0 1px rgba(255,255,255,0.85),0 0 0 3px rgba(244,66,80,0.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-palette,.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-palette:active,.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme=\'nano\']{width:14.25em;max-width:95vw}.pcr-app[data-theme=\'nano\'] .pcr-swatches{margin-top:.6em;padding:0 .6em}.pcr-app[data-theme=\'nano\'] .pcr-interaction{padding:0 .6em .6em .6em}.pcr-app[data-theme=\'nano\'] .pcr-selection{display:grid;grid-gap:.6em;grid-template-columns:1fr 4fr;grid-template-rows:5fr auto auto;align-items:center;height:10.5em;width:100%;align-self:flex-start}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-preview{grid-area:2 / 1 / 4 / 1;height:100%;width:100%;display:flex;flex-direction:row;justify-content:center;margin-left:.6em}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-preview .pcr-last-color{display:none}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-preview .pcr-current-color{position:relative;background:var(--pcr-color);width:2em;height:2em;border-radius:50em;overflow:hidden}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-preview .pcr-current-color::before{position:absolute;content:\'\';top:0;left:0;width:100%;height:100%;background:url(\'data:image/svg+xml;utf8, \');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-palette{grid-area:1 / 1 / 2 / 3;width:100%;height:100%;z-index:1}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-palette .pcr-palette{border-radius:.15em;width:100%;height:100%}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-palette .pcr-palette::before{position:absolute;content:\'\';top:0;left:0;width:100%;height:100%;background:url(\'data:image/svg+xml;utf8, \');background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-chooser{grid-area:2 / 2 / 2 / 2}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-opacity{grid-area:3 / 2 / 3 / 2}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-opacity{height:0.5em;margin:0 .6em}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-opacity .pcr-picker{top:50%;transform:translateY(-50%)}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-opacity .pcr-slider{flex-grow:1;border-radius:50em}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(to right, red, #ff0, lime, cyan, blue, #f0f, red)}.pcr-app[data-theme=\'nano\'] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(to right, transparent, black),url(\'data:image/svg+xml;utf8, \');background-size:100%, 0.25em}\n\n',{});class Bo extends e.Modal{constructor(e,t,o){if(super(e),this.plugin=t,this.commandIndex=o,null!==o){const e=t.settings.customCommands[o];this.commandId=e.id,this.commandName=e.name,this.icon=e.icon||"",this.regexPattern=e.regexPattern||"",this.regexReplacement=e.regexReplacement||"",this.regexCaseInsensitive=e.regexCaseInsensitive||!1,this.regexGlobal=!1!==e.regexGlobal,this.regexMultiline=e.regexMultiline||!1,this.useCondition=e.useCondition||!1,this.conditionPattern=e.conditionPattern||""}else this.commandId="",this.commandName="",this.icon="",this.regexPattern="",this.regexReplacement="",this.regexCaseInsensitive=!1,this.regexGlobal=!0,this.regexMultiline=!1,this.useCondition=!1,this.conditionPattern=""}onOpen(){const{contentEl:t}=this;this.modalEl.addClass("custom-commands-modal"),t.empty(),t.createEl("h2",{text:null!==this.commandIndex?v("Edit regular expression command"):v("Add regular expression command")});const o=t.createDiv("basic-settings-container");new e.Setting(o).setName(v("Command ID")).setDesc(v('Unique identifier, no spaces, e.g.: "my-custom-format"')).addText(e=>(e.setValue(this.commandId),null!==this.commandIndex?(e.setDisabled(!0),e.inputEl.addClass("id-is-disabled")):e.onChange(e=>{this.commandId=e;const o=t.querySelector(".setting-item:nth-child(2) input");o instanceof HTMLInputElement&&(o.value=e,this.commandName=e)}),e)),new e.Setting(o).setName(v("Command Name")).setDesc(v("Displayed name in toolbar and menu")).addText(e=>e.setValue(this.commandName).onChange(e=>this.commandName=e));const n=t.createDiv("regex-settings");n.style.border="1px solid var(--background-modifier-border)",n.style.padding="10px",n.style.borderRadius="5px",n.style.marginBottom="10px";const i=n.createEl("details",{cls:"ai-help-container"});i.style.marginBottom="10px",i.style.borderRadius="5px",i.style.overflow="hidden";const a=i.createEl("summary",{text:v("How to use AI to get regular expressions?")});a.style.padding="8px 12px",a.style.backgroundColor="var(--background-secondary)",a.style.cursor="pointer",a.style.fontWeight="bold",a.style.borderRadius="4px",a.style.userSelect="none";const s=i.createDiv("ai-help-content");s.style.padding="6px",s.style.backgroundColor="var(--background-secondary-alt)",s.style.borderBottomLeftRadius="5px",s.style.borderBottomRightRadius="5px",s.style.marginTop="1px",s.setAttribute("contenteditable","false"),s.style.userSelect="text",s.innerHTML=`\n ${v("AI question template:")}
\n ${v("[Description]")}:\n ${v("I need to convert the url to a markdown format link")}\n
\n ${v("[Example]")}: \n ${v("For example, convert https://example.com to [https://example.com](https://example.com)")}\n
\n ${v("[Requirements]")}: \n ${v("Use js regular expression to implement, and output the parameters in the following format (the result does not need to be escaped with json)")}\n
\n ${v("[Output]")}:\n
\n "name": "[Descriptive Name]",
\n "pattern": "[Regex Pattern]",
\n "replacement": "[Replacement Pattern, if applicable]",
\n "flags": "[Regex Flags]"
\n
\n `,new e.Setting(n).setName(v("Matching pattern")).setDesc(v("Regex pattern to match")).addText(e=>this.regexPatternInput=e.setValue(this.regexPattern).onChange(e=>{this.regexPattern=e,this.updatePreview()})),new e.Setting(n).setName(v("Replacement pattern")).setDesc(v("Replacement pattern (use $1, $2, etc. to reference capture groups)")+v("Use \\n to represent line breaks")).addText(e=>this.regexReplacementInput=e.setValue(this.regexReplacement.replace(/\n/g,"\\n")).onChange(e=>{this.regexReplacement=e.replace(/\\n/g,"\n"),this.updatePreview()}));const r=n.createDiv("regex-options");r.style.display="flex",r.style.gap="8px",new e.Setting(r).setName(v("Ignore case")).setDesc(v("Match case-insensitive")).addToggle(e=>e.setValue(this.regexCaseInsensitive).onChange(e=>{this.regexCaseInsensitive=e,this.updatePreview()})),new e.Setting(r).setName(v("Global replace")).setDesc(v("Replace all matches")).addToggle(e=>e.setValue(this.regexGlobal).onChange(e=>{this.regexGlobal=e,this.updatePreview()})),new e.Setting(r).setName(v("Multiline mode")).setDesc(v("^ and $ match the start and end of each line")).addToggle(e=>this.regexMultilineToggle=e.setValue(this.regexMultiline).onChange(e=>{this.regexMultiline=e,this.updatePreview()}));const l=n.createDiv("condition-container");new e.Setting(l).setName(v("Use condition")).setDesc(v("Only apply custom command when text matches the condition")).addToggle(e=>this.useConditionToggle=e.setValue(this.useCondition).onChange(e=>{this.useCondition=e,c.style.display=e?"block":"none"}));const c=l.createDiv("condition-settings");c.style.display=this.useCondition?"block":"none",c.style.border="1px solid var(--background-modifier-border)",c.style.padding="10px",c.style.borderRadius="5px",c.style.marginBottom="15px",new e.Setting(c).setName(v("Condition pattern")).setDesc(v("Must exist regular expression or text")).addText(e=>this.conditionPatternInput=e.setValue(this.conditionPattern).onChange(e=>{this.conditionPattern=e}));const d=new e.Setting(n).setName(v("Icon")).setDesc(v("Command icon (click to select)"));if(this.iconDisplay=d.controlEl.createDiv("editingToolbarSettingsIcon"),this.icon)try{e.setIcon(this.iconDisplay,this.icon)}catch(e){this.iconDisplay.setText(this.icon)}d.addButton(t=>t.setButtonText(v("Choose icon")).onClick(()=>{const t={id:this.commandId,name:this.commandName,icon:this.icon};new w(this.plugin,t,!1,t=>{if(this.icon=t,this.iconDisplay.empty(),this.icon)try{e.setIcon(this.iconDisplay,this.icon)}catch(e){this.iconDisplay.setText(this.icon)}const o=d.controlEl.querySelector("input");o&&(o.value=this.icon)}).open()})),n.createSpan("regex-help");const u=n.createEl("details",{cls:"regex-examples-container"});u.style.marginTop="15px",u.style.borderRadius="5px",u.style.overflow="hidden";const m=u.createEl("summary",{text:v("Regular expression examples")});m.style.padding="8px 12px",m.style.backgroundColor="var(--background-secondary)",m.style.cursor="pointer",m.style.fontWeight="bold",m.style.borderRadius="4px",m.style.userSelect="none";const p=u.createDiv("examples-content");p.style.padding="10px",p.style.backgroundColor="var(--background-secondary-alt)",p.style.borderBottomLeftRadius="5px",p.style.borderBottomRightRadius="5px",p.style.marginTop="1px";const h=p.createEl("ul");h.style.paddingLeft="20px",h.style.margin="0";[{name:v("URL to Markdown link"),pattern:"(https?://\\S+)",replacement:"[$1]($1)"},{name:v("Convert MM/DD/YYYY to YYYY-MM-DD"),pattern:"(\\d{1,2})/(\\d{1,2})/(\\d{4})",replacement:"$3-$1-$2"},{name:v("Add bold to keywords"),pattern:"\\b(important|critical|urgent)\\b",replacement:"**$1**"},{name:v("Format phone number"),pattern:"(\\d{3})(\\d{3})(\\d{4})",replacement:"($1) $2-$3"},{name:v("Remove extra spaces"),pattern:"\\s{2,}",replacement:" "},{name:v("Convert HTML bold tags to Markdown format"),pattern:"(.*?)",replacement:"**$1**"},{name:v("Convert quoted text to quote block"),pattern:'"([^"]+)"',replacement:"> $1"},{name:v("Add uniform alias to Markdown links"),pattern:"\\[([^\\]]+)\\]\\(([^\\)]+)\\)",replacement:"[$1|alias]($2)"},{name:v("Delete empty lines (multiline mode)"),pattern:"^\\s*$\\n",replacement:"",toggleMultiline:!0},{name:v("Add list symbol to each line (multiline mode)"),pattern:"^(.+)$",replacement:"- $1",toggleMultiline:!0},{name:v("If the text contains important, set the text highlight (conditional format)"),pattern:"(.+)",replacement:"==$1==",useCondition:!0,conditionPattern:"important"}].forEach(e=>{const t=h.createEl("li");t.style.marginBottom="8px";const o=t.createEl("a",{text:e.name,href:"#"});o.style.color="var(--text-accent)",o.style.textDecoration="none",o.addEventListener("mouseenter",()=>{o.style.textDecoration="underline"}),o.addEventListener("mouseleave",()=>{o.style.textDecoration="none"}),o.addEventListener("click",t=>{t.preventDefault(),this.regexPattern=e.pattern,this.regexReplacement=e.replacement,this.regexPatternInput.setValue(e.pattern),this.regexReplacementInput.setValue(e.replacement),e.useCondition?(this.useCondition=!0,this.conditionPattern=e.conditionPattern||"",this.useConditionToggle.setValue(!0),this.conditionPatternInput.setValue(this.conditionPattern),c.style.display="block"):(this.useCondition=!1,this.useConditionToggle.setValue(!1),this.conditionPatternInput.setValue(""),c.style.display="none"),e.toggleMultiline?this.regexMultilineToggle.setValue(!0):this.regexMultilineToggle.setValue(!1),this.updatePreview(),u.removeAttribute("open")})});const g=t.createDiv("preview-container");g.style.marginTop="20px",g.style.marginBottom="20px",g.style.border="1px solid var(--background-modifier-border)",g.style.padding="10px",g.style.borderRadius="5px",g.createEl("label",{text:v("Preview")});const f=g.createDiv("preview-input-container");f.style.marginBottom="10px";const b=f.createEl("label",{text:v("Example text:")});b.style.display="block",b.style.marginBottom="5px",this.previewInput=f.createEl("textarea",{attr:{placeholder:v("Input example text to view the formatting effect of the command...")}}),this.previewInput.style.height="auto",this.previewInput.style.width="100%",this.previewInput.style.padding="8px",this.previewInput.style.borderRadius="4px",this.previewInput.style.border="1px solid var(--background-modifier-border)",this.previewInput.value="Sample text https://example.com important text 1234567890",this.previewInput.addEventListener("input",()=>{this.updatePreview()});const y=g.createDiv("preview-output-container"),C=y.createEl("label",{text:v("Result: ")});C.style.display="block",C.style.marginBottom="5px",this.previewOutput=y.createDiv("preview-output"),this.previewOutput.style.padding="8px",this.previewOutput.style.borderRadius="4px",this.previewOutput.style.border="1px solid var(--background-modifier-border)",this.previewOutput.style.backgroundColor="var(--background-secondary)",this.previewOutput.style.minHeight="3em",this.updatePreview(),new e.Setting(t).addButton(t=>t.setButtonText("保存").setCta().onClick(()=>{if(!this.commandId||!this.commandName)return void new e.Notice(v("Command ID and command name cannot be empty"));if(this.commandId.includes(" "))return void new e.Notice(v("Command ID cannot contain spaces"));if(!this.regexPattern)return void new e.Notice(v("Regex pattern cannot be empty"));const t=null===this.commandIndex?`custom-${this.commandId}`:this.commandId;if(null===this.commandIndex){if(this.plugin.settings.customCommands.findIndex(e=>e.id===t)>=0)return void new e.Notice(v("Command")+" "+this.commandId+" "+v("already exists"),8e3)}const o={id:t,name:this.commandName,icon:this.icon,useRegex:!0,regexPattern:this.regexPattern,regexReplacement:this.regexReplacement.replace(/\\n/g,"\n"),regexCaseInsensitive:this.regexCaseInsensitive,regexGlobal:this.regexGlobal,regexMultiline:this.regexMultiline,useCondition:this.useCondition,conditionPattern:this.conditionPattern,prefix:"",suffix:"",char:0,line:0,islinehead:!1};null!==this.commandIndex?this.plugin.settings.customCommands[this.commandIndex]=o:this.plugin.settings.customCommands.push(o),this.plugin.saveSettings().then(()=>{this.close(),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand")),this.plugin.reloadCustomCommands()},100)})})).addButton(e=>e.setButtonText(v("Cancel")).onClick(()=>this.close()))}onClose(){const{contentEl:e}=this;e.empty()}updatePreview(){const e=this.previewInput.value;let t=e;try{if(this.regexPattern){let o="";this.regexGlobal&&(o+="g"),this.regexCaseInsensitive&&(o+="i"),this.regexMultiline&&(o+="m");const n=new RegExp(this.regexPattern,o),i=this.regexReplacement.replace(/\\n/g,"\n");t=e.replace(n,i),this.showCompleteRegexCode(o)}this.previewOutput.empty(),t.split("\n").forEach((e,t,o)=>{this.previewOutput.createSpan({text:e}),t{navigator.clipboard.writeText(i).then(()=>{a.textContent=v("Copied!"),setTimeout(()=>{a.textContent=v("Copy code")},2e3)}).catch(e=>{console.error("Failed to copy code: ",e)})})}escapeRegexForDisplay(e){return e.replace(/\\/g,"\\\\")}escapeStringForDisplay(e){return e.replace(/"/g,'\\"')}}class $o extends e.Modal{constructor(e,t,o){if(super(e),this.plugin=t,this.commandIndex=o,null!==o){const e=t.settings.customCommands[o];this.commandId=e.id,this.commandName=e.name,this.prefix=e.prefix,this.suffix=e.suffix,this.char=e.char,this.line=e.line,this.islinehead=e.islinehead,this.icon=e.icon||""}else this.commandId="",this.commandName="",this.prefix="",this.suffix="",this.char=0,this.line=0,this.islinehead=!1,this.icon=""}onOpen(){const{contentEl:t}=this;this.modalEl.addClass("custom-commands-modal"),t.empty(),t.createEl("h2",{text:null!==this.commandIndex?v("Edit Custom Command"):v("Add Custom Command")});const o=t.createDiv("switch-to-regex-container");o.style.marginBottom="20px",o.style.textAlign="center";const n=o.createEl("button",{text:v("Switch Regex Command Window")});n.addClass("mod-cta"),n.addEventListener("click",()=>{this.close(),new Bo(this.app,this.plugin,null).open()}),new e.Setting(t).setName(v("Command ID")).setDesc(v('Unique identifier, no spaces, e.g.: "my-custom-format"')).addText(e=>(this.commandIdInput=e,e.setValue(this.commandId),null!==this.commandIndex?(e.setDisabled(!0),e.inputEl.addClass("id-is-disabled")):e.onChange(e=>{this.commandId=e,this.commandNameInput&&(this.commandNameInput.setValue(e),this.commandName=e)}),e)),new e.Setting(t).setName(v("Command Name")).setDesc(v("Displayed name in toolbar and menu")).addText(e=>this.commandNameInput=e.setValue(this.commandName).onChange(e=>this.commandName=e));const i={"\n":"↵","\t":"⇥"},a=Object.fromEntries(Object.entries(i).map(([e,t])=>[t,e]));function s(e){let t=e;for(const[e,o]of Object.entries(i))t=t.replace(new RegExp(e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g"),o);return t}function r(e){let t=e;for(const[e,o]of Object.entries(a))t=t.replace(new RegExp(e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),"g"),o);return t}const l=Object.entries(i).map(([e,t])=>{let o=t;switch(e){case"\n":o+=" (New Line)";break;case"\t":o+=" (Tab)"}return{placeholder:t,label:o}});function c(e,t){t.setAttribute("title","点击可选择并复制文本");const o=e.controlEl.createDiv({cls:"special-char-buttons"});o.style.display="flex",o.style.flexWrap="wrap",o.style.gap="5px",o.style.marginTop="5px",l.forEach(({placeholder:e,label:t})=>{const n=o.createDiv({cls:"char-copy-container"});n.style.position="relative",n.style.display="inline-block";const i=n.createEl("button",{text:t});i.style.padding="2px 6px",i.style.fontSize="12px",i.style.minWidth="auto",i.style.border="1px solid var(--background-modifier-border)",i.style.cursor="pointer",i.setAttribute("data-char",e),i.addEventListener("click",t=>{t.preventDefault(),navigator.clipboard.writeText(e).then(()=>{const e=n.createDiv({cls:"copy-tooltip"});e.style.position="absolute",e.style.bottom="100%",e.style.left="50%",e.style.transform="translateX(-50%)",e.style.backgroundColor="var(--background-modifier-success)",e.style.color="white",e.style.padding="2px 6px",e.style.borderRadius="4px",e.style.fontSize="12px",e.style.pointerEvents="none",e.style.whiteSpace="nowrap",e.style.zIndex="100",e.textContent="已复制!",setTimeout(()=>{e.remove()},2e3)}).catch(e=>{console.error("无法复制文本: ",e)})}),i.addEventListener("mouseenter",()=>{i.style.backgroundColor="var(--interactive-accent)",i.style.color="var(--text-on-accent)"}),i.addEventListener("mouseleave",()=>{i.style.backgroundColor="",i.style.color=""})})}const d=new e.Setting(t).setName(v("Prefix")).setDesc(v("Add content before selected text")+v("Use ↵ to represent line breaks")).addText(e=>e.setValue(s(this.prefix)).onChange(e=>{this.prefix=r(e);const t=this.getMirrorText(e);t&&(this.suffix=r(t),this.suffixInput.setValue(t))}));c(d,d.controlEl.querySelector("input"));const u=new e.Setting(t).setName(v("Suffix")).setDesc(v("Add content after selected text")).addText(e=>{this.suffixInput=e,e.setValue(s(this.suffix)).onChange(e=>{this.suffix=r(e)})});c(u,u.controlEl.querySelector("input")),new e.Setting(t).setName(v("Cursor Position Offset")).setDesc(v("Default 0, format will keep the text selected")).addText(e=>e.setValue(this.char.toString()).onChange(e=>this.char=parseInt(e)||0)),new e.Setting(t).setName(v("Line Offset")).setDesc(v("Line offset of cursor after formatting")).addText(e=>e.setValue(this.line.toString()).onChange(e=>this.line=parseInt(e)||0)),new e.Setting(t).setName(v("Line Head Format")).setDesc(v("Whether to insert at the beginning of the next line")).addToggle(e=>e.setValue(this.islinehead).onChange(e=>this.islinehead=e));const m=new e.Setting(t).setName(v("Icon")).setDesc(v("Command icon (click to select)"));if(this.iconDisplay=m.controlEl.createDiv("editingToolbarSettingsIcon"),this.icon)try{e.setIcon(this.iconDisplay,this.icon)}catch(e){this.iconDisplay.setText(this.icon)}m.addButton(t=>t.setButtonText(v("Choose Icon")).onClick(()=>{const t={id:this.commandId,name:this.commandName,icon:this.icon};new w(this.plugin,t,!1,t=>{if(this.icon=t,this.iconDisplay.empty(),this.icon)try{e.setIcon(this.iconDisplay,this.icon)}catch(e){this.iconDisplay.setText(this.icon)}const o=m.controlEl.querySelector("input");o&&(o.value=this.icon)}).open()})),new e.Setting(t).addButton(t=>t.setButtonText(v("Save")).setCta().onClick(()=>{if(!this.commandId||!this.commandName)return void new e.Notice(v("Command ID and command name cannot be empty"));if(this.commandId.includes(" "))return void new e.Notice(v("Command ID cannot contain spaces"));const t=null===this.commandIndex?`custom-${this.commandId}`:this.commandId;if(null===this.commandIndex){if(this.plugin.settings.customCommands.findIndex(e=>e.id===t)>=0)return void new e.Notice(v("The command")+this.commandId+v("already exists"),8e3)}const o={id:t,name:this.commandName,prefix:this.prefix,suffix:this.suffix,char:this.char,line:this.line,islinehead:this.islinehead,icon:this.icon};if(null!==this.commandIndex){if(this.plugin.settings.customCommands[this.commandIndex].icon!==this.icon){const e=`editing-toolbar:${t}`;this.updateCommandIcon(this.plugin.settings.menuCommands,e),this.plugin.settings.enableMultipleConfig&&(this.updateCommandIcon(this.plugin.settings.followingCommands,e),this.updateCommandIcon(this.plugin.settings.topCommands,e),this.updateCommandIcon(this.plugin.settings.fixedCommands,e),this.plugin.settings.isLoadOnMobile&&this.updateCommandIcon(this.plugin.settings.mobileCommands,e))}this.plugin.settings.customCommands[this.commandIndex]=o}else this.plugin.settings.customCommands.push(o);this.plugin.saveSettings().then(()=>{this.close(),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand")),this.plugin.reloadCustomCommands()},100)})})).addButton(e=>e.setButtonText(v("Cancel")).onClick(()=>this.close())),setTimeout(()=>{this.commandIndex?this.commandIdInput.inputEl.focus():this.commandNameInput.inputEl.focus()},10)}updateCommandIcon(e,t){e&&e.forEach(e=>{e.id===t&&(e.icon=this.icon),e.SubmenuCommands&&this.updateCommandIcon(e.SubmenuCommands,t)})}onClose(){const{contentEl:e}=this;e.empty()}getMirrorText(e){const t={"**":"**","*":"*",__:"__",_:"_","~~":"~~","`":"`","```":"```",$:"$",$$:"$$","(":")","[":"]","{":"}","<":">","==":"==","*==":"==*","**==":"==**","***==":"==***"};if(!e)return"";if(e in t)return t[e];const o=e.match(/^<(\w+)([^>]*)>$/);return o?`${o[1]}>`:""}}class _o extends e.Modal{constructor(e,t,o){super(e),this.deployOptions=[],this.plugin=t,this.command=o,this.deployOptions=[{id:"following",name:v("Following Style"),enabled:!0},{id:"top",name:v("Top Style"),enabled:!0},{id:"fixed",name:v("Fixed Style"),enabled:!0}],this.plugin.settings.isLoadOnMobile&&this.deployOptions.push({id:"mobile",name:v("Mobile Style"),enabled:!0})}onOpen(){const{contentEl:t}=this;t.empty(),t.createEl("h3",{text:v("Deploy command to configurations")}),t.createDiv("deploy-option");const o=t.createDiv("deploy-options");this.deployOptions.forEach(t=>{new e.Setting(o).setName(t.name).addToggle(e=>e.setValue(t.enabled).onChange(e=>{t.enabled=e})).settingEl.addClass("deploy-option")});const n=t.createDiv("deploy-buttons");new e.Setting(n).addButton(e=>e.setButtonText(v("Deploy")).setCta().onClick(()=>{this.deployCommand(),this.close()})).addButton(e=>e.setButtonText(v("Cancel")).onClick(()=>{this.close()}))}deployCommand(){const t={id:`editing-toolbar:${this.command.id}`,name:this.command.name,icon:this.command.icon||"obsidian-new"};this.plugin.settings.menuCommands.some(e=>e.id===t.id)||this.plugin.settings.menuCommands.push({...t});let o=0;this.deployOptions.forEach(e=>{if(e.enabled){let n;switch(e.id){case"mobile":n=this.plugin.settings.mobileCommands;break;case"following":n=this.plugin.settings.followingCommands;break;case"top":n=this.plugin.settings.topCommands;break;case"fixed":n=this.plugin.settings.fixedCommands}n&&!n.some(e=>e.id===t.id)&&(n.push({...t}),o++)}}),this.plugin.saveSettings().then(()=>{let t="";if(o>0){const e=this.deployOptions.filter(e=>e.enabled).map(e=>e.name).join(", ");t=v("Command deployed to: ")+e}else t=v("Command already exists in selected configurations");new e.Notice(t),dispatchEvent(new Event("editingToolbar-NewCommand")),this.plugin.reloadCustomCommands()})}}class zo extends e.Modal{constructor(e,t,o){super(e),this.plugin=t,this.mode=o,this.exportType="all",this.importMode="update"}onOpen(){const{contentEl:t}=this;if(t.addClass("editing-toolbar-import-export-modal"),t.createEl("h2",{text:"import"===this.mode?v("Import Configuration"):v("Export Configuration"),cls:"import-export-title"}),"export"===this.mode){new e.Setting(t).setName(v("Export Type")).setDesc(v("Choose what to export")).addDropdown(e=>{e.addOption("all",v("All Settings")).addOption("All commands",v("All Toolbar Commands")).addOption("custom",v("Custom Commands Only")),this.plugin.settings.enableMultipleConfig&&e.addOption("following",v("Following Style Only")).addOption("top",v("Top Style Only")).addOption("fixed",v("Fixed Style Only")).addOption("mobile",v("Mobile Style Only")),e.setValue(this.exportType).onChange(e=>{this.exportType=e,this.updateExportContent()})});const o=t.createDiv("export-container");o.style.border="1px solid var(--background-modifier-border)",o.style.padding="10px",o.style.borderRadius="5px",this.textArea=new e.TextAreaComponent(o),this.textArea.setValue("").setPlaceholder(v("Loading...")).then(e=>{e.inputEl.style.width="100%",e.inputEl.style.height="200px",e.inputEl.style.fontFamily="monospace",e.inputEl.style.fontSize="12px",e.inputEl.style.padding="8px",e.inputEl.style.border="1px solid var(--background-modifier-border)",e.inputEl.style.borderRadius="4px"}),this.updateExportContent();const n=t.createDiv("import-export-button-container");n.style.display="flex",n.style.justifyContent="flex-end",n.style.marginTop="16px";n.createEl("button",{text:v("Copy to Clipboard"),cls:"mod-cta"}).addEventListener("click",()=>{navigator.clipboard.writeText(this.textArea.getValue()).then(()=>{new e.Notice(v("Configuration copied to clipboard"))}).catch(t=>{console.error("Failed to copy: ",t),new e.Notice(v("Failed to copy configuration"))})})}else{new e.Setting(t).setName(v("Import Mode")).setDesc(v("Choose how to import the configuration")).addDropdown(e=>{e.addOption("update",v("Update Mode (Add new items and update existing ones)")).addOption("overwrite",v("Overwrite Mode (Replace settings with imported ones)")).setValue(this.importMode).onChange(e=>{this.importMode=e,this.importButton.setButtonText("overwrite"===this.importMode?v("Overwrite Import"):v("Update Import")),this.warningContent.setText("overwrite"===this.importMode?v("Warning: Overwrite mode will replace existing settings with imported ones."):v("Warning: Update mode will add new items and update existing ones."))})});const o=t.createDiv("import-container");o.style.border="1px solid var(--background-modifier-border)",o.style.padding="10px",o.style.borderRadius="5px",this.textArea=new e.TextAreaComponent(o),this.textArea.setValue("").setPlaceholder(v("Paste configuration here...")).then(e=>{e.inputEl.style.width="100%",e.inputEl.style.height="200px",e.inputEl.style.fontFamily="monospace",e.inputEl.style.fontSize="12px",e.inputEl.style.padding="8px",e.inputEl.style.border="1px solid var(--background-modifier-border)",e.inputEl.style.borderRadius="4px"});const n=t.createDiv("import-export-button-container");n.style.display="flex",n.style.justifyContent="flex-end",n.style.marginTop="16px",new e.Setting(n).addButton(e=>{this.importButton=e.setIcon("import").setButtonText(v("Import Configuration")).onClick(()=>{this.importConfiguration()})});const i=t.createDiv("import-export-warning");i.style.marginTop="16px",i.style.padding="8px 12px",i.style.backgroundColor="rgba(var(--color-red-rgb), 0.1)",i.style.borderRadius="4px",i.style.border="1px solid rgba(var(--color-red-rgb), 0.3)";const a=i.createEl("p",{text:v("Warning: Update mode will add new items and update existing ones."),cls:"warning-text"});a.style.margin="0",this.warningContent=a}}updateExportContent(){let e={_exportInfo:{version:this.plugin.manifest.version,exportType:this.exportType,exportTime:(new Date).toISOString(),pluginId:this.plugin.manifest.id}};switch(this.exportType){case"all":e={...e,menuCommands:this.plugin.settings.menuCommands||[],followingCommands:this.plugin.settings.followingCommands||[],topCommands:this.plugin.settings.topCommands||[],fixedCommands:this.plugin.settings.fixedCommands||[],mobileCommands:this.plugin.settings.mobileCommands||[],customCommands:this.plugin.settings.customCommands||[],enableMultipleConfig:this.plugin.settings.enableMultipleConfig,positionStyle:this.plugin.settings.positionStyle,aestheticStyle:this.plugin.settings.aestheticStyle,appendMethod:this.plugin.settings.appendMethod,autohide:this.plugin.settings.autohide,isLoadOnMobile:this.plugin.settings.isLoadOnMobile,cMenuNumRows:this.plugin.settings.cMenuNumRows,custom_bg1:this.plugin.settings.custom_bg1,custom_bg2:this.plugin.settings.custom_bg2,custom_bg3:this.plugin.settings.custom_bg3,custom_bg4:this.plugin.settings.custom_bg4,custom_bg5:this.plugin.settings.custom_bg5,custom_fc1:this.plugin.settings.custom_fc1,custom_fc2:this.plugin.settings.custom_fc2,custom_fc3:this.plugin.settings.custom_fc3,custom_fc4:this.plugin.settings.custom_fc4,custom_fc5:this.plugin.settings.custom_fc5,toolbarBackgroundColor:this.plugin.settings.toolbarBackgroundColor,toolbarIconColor:this.plugin.settings.toolbarIconColor,toolbarIconSize:this.plugin.settings.toolbarIconSize};break;case"All commands":e={...e,menuCommands:this.plugin.settings.menuCommands||[],followingCommands:this.plugin.settings.followingCommands||[],topCommands:this.plugin.settings.topCommands||[],fixedCommands:this.plugin.settings.fixedCommands||[],mobileCommands:this.plugin.settings.mobileCommands||[],enableMultipleConfig:this.plugin.settings.enableMultipleConfig};break;case"custom":e={...e,customCommands:this.plugin.settings.customCommands||[]};break;case"following":e={...e,followingCommands:this.plugin.settings.followingCommands||[]};break;case"top":e={...e,topCommands:this.plugin.settings.topCommands||[]};break;case"fixed":e={...e,fixedCommands:this.plugin.settings.fixedCommands||[]};break;case"mobile":e={...e,mobileCommands:this.plugin.settings.mobileCommands||[]}}this.validateExportContent(e),this.textArea.setValue(JSON.stringify(e,null,2))}validateExportContent(e){["menuCommands","followingCommands","topCommands","fixedCommands","mobileCommands","customCommands"].forEach(t=>{t in e&&!e[t]&&(e[t]=[])}),"enableMultipleConfig"in e&&void 0===e.enableMultipleConfig&&(e.enableMultipleConfig=!1),"autohide"in e&&void 0===e.autohide&&(e.autohide=!1),"Iscentered"in e&&void 0===e.Iscentered&&(e.Iscentered=!1),"isLoadOnMobile"in e&&void 0===e.isLoadOnMobile&&(e.isLoadOnMobile=!0),"positionStyle"in e&&!e.positionStyle&&(e.positionStyle="top"),"aestheticStyle"in e&&!e.aestheticStyle&&(e.aestheticStyle="default"),"appendMethod"in e&&!e.appendMethod&&(e.appendMethod="workspace"),"cMenuNumRows"in e&&void 0===e.cMenuNumRows&&(e.cMenuNumRows=1)}async importConfiguration(){try{const t=this.textArea.getValue();if(!t.trim())return void new e.Notice(v("Please paste configuration data first"));const o=JSON.parse(t);if(!o||"object"!=typeof o)return void new e.Notice(v("Invalid import data format"));const n="menuCommands"in o,i="customCommands"in o,a="followingCommands"in o,s="topCommands"in o,r="fixedCommands"in o,l="mobileCommands"in o,c="positionStyle"in o||"aestheticStyle"in o,d="enableMultipleConfig"in o,u=o.positionStyle,m=n&&Array.isArray(o.menuCommands)&&o.menuCommands.length>0,p=i&&Array.isArray(o.customCommands)&&o.customCommands.length>0,h=a&&Array.isArray(o.followingCommands)&&o.followingCommands.length>0,g=s&&Array.isArray(o.topCommands)&&o.topCommands.length>0,f=r&&Array.isArray(o.fixedCommands)&&o.fixedCommands.length>0,b=l&&Array.isArray(o.mobileCommands)&&o.mobileCommands.length>0,y=n&&(!Array.isArray(o.menuCommands)||0===o.menuCommands.length),C=i&&(!Array.isArray(o.customCommands)||0===o.customCommands.length),w=a&&(!Array.isArray(o.followingCommands)||0===o.followingCommands.length),x=s&&(!Array.isArray(o.topCommands)||0===o.topCommands.length),k=r&&(!Array.isArray(o.fixedCommands)||0===o.fixedCommands.length),S=l&&(!Array.isArray(o.mobileCommands)||0===o.mobileCommands.length);let T=v("This import will:")+"\n";if(c&&(T+="• "+v("Update general settings")+"\n"),m&&(T+="• "+v("Update Main Menu Commands")+" ("+o.menuCommands.length+" )\n"),p&&(T+="• "+v("Update Custom Commands")+" ("+o.customCommands.length+" )\n"),h&&(T+="• "+v("Update Following Style Commands")+" ("+o.followingCommands.length+" )\n"),g&&(T+="• "+v("Update Top Style Commands")+" ("+o.topCommands.length+" )\n"),f&&(T+="• "+v("Update Fixed Style Commands")+" ("+o.fixedCommands.length+" )\n"),b&&(T+="• "+v("Update Mobile Style Commands")+" ("+o.mobileCommands.length+" )\n"),"overwrite"===this.importMode&&(y&&(T+="• "+v("Clear all Main Menu Commands")+" ⚠️\n"),C&&(T+="• "+v("Clear all Custom Commands")+" ⚠️\n"),w&&(T+="• "+v("Clear all Following Style Commands")+" ⚠️\n"),x&&(T+="• "+v("Clear all Top Style Commands")+" ⚠️\n"),k&&(T+="• "+v("Clear all Fixed Style Commands")+" ⚠️\n"),S&&(T+="• "+v("Clear all Mobile Style Commands")+" ⚠️\n")),d){const e=o.enableMultipleConfig?v("Enable"):v("Disable");T+="• "+v("Set Multiple Config to:")+" "+e+"\n"}if(u&&(T+="• "+v("Set Position Style to:")+" "+this.getPositionStyleName(u)+"\n"),!(m||p||h||g||f||b||y||C||w||x||k||S||c||d))return void new e.Notice(v("No valid configuration found in import data"));"overwrite"===this.importMode?T+="\n"+v("⚠️ Overwrite mode will replace existing settings with imported ones."):T+="\n"+v("ℹ️ Update mode will merge imported settings with existing ones."),No.show(this.app,{message:T+"\n"+v("Do you want to continue?"),onConfirm:async()=>{const t={positionStyle:this.plugin.settings.positionStyle,menuCommands:[...this.plugin.settings.menuCommands],customCommands:[...this.plugin.settings.customCommands],followingCommands:[...this.plugin.settings.followingCommands],topCommands:[...this.plugin.settings.topCommands],fixedCommands:[...this.plugin.settings.fixedCommands],mobileCommands:[...this.plugin.settings.mobileCommands]};try{"overwrite"===this.importMode?this.performOverwriteImport(o):this.performUpdateImport(o),this.fixImportedCommandIds(),await this.plugin.saveSettings(),this.plugin.reloadCustomCommands(),dispatchEvent(new Event("editingToolbar-NewCommand")),new e.Notice(v("Configuration imported successfully")),this.close()}catch(e){throw this.restoreBackup(t),e}}})}catch(t){console.error("Import error: ",t),new e.Notice(v("Error: ")+" "+t.message)}}performOverwriteImport(e){this.importGeneralSettings(e),e.menuCommands&&(this.plugin.settings.menuCommands=e.menuCommands),e.customCommands&&(this.plugin.settings.customCommands=e.customCommands),e.followingCommands&&(this.plugin.settings.followingCommands=e.followingCommands),e.topCommands&&(this.plugin.settings.topCommands=e.topCommands),e.fixedCommands&&(this.plugin.settings.fixedCommands=e.fixedCommands),e.mobileCommands&&(this.plugin.settings.mobileCommands=e.mobileCommands)}performUpdateImport(e){this.importGeneralSettings(e),e.menuCommands&&this.updateCommandArray(this.plugin.settings.menuCommands,e.menuCommands),e.customCommands&&this.updateCommandArray(this.plugin.settings.customCommands,e.customCommands),e.followingCommands&&this.updateCommandArray(this.plugin.settings.followingCommands,e.followingCommands),e.topCommands&&this.updateCommandArray(this.plugin.settings.topCommands,e.topCommands),e.fixedCommands&&this.updateCommandArray(this.plugin.settings.fixedCommands,e.fixedCommands),e.mobileCommands&&this.updateCommandArray(this.plugin.settings.mobileCommands,e.mobileCommands)}updateCommandArray(e,t){return e?(t.forEach(t=>{const o=e.findIndex(e=>e.id===t.id);o>=0?e[o]=t:e.push(t),t.SubmenuCommands&&e[o]?.SubmenuCommands&&this.updateCommandArray(e[o].SubmenuCommands,t.SubmenuCommands)}),e):t.slice()}importGeneralSettings(e){["positionStyle","aestheticStyle","appendMethod","autohide","Iscentered","isLoadOnMobile","cMenuNumRows","enableMultipleConfig","custom_bg1","custom_bg2","custom_bg3","custom_bg4","custom_bg5","custom_fc1","custom_fc2","custom_fc3","custom_fc4","custom_fc5","toolbarBackgroundColor","toolbarIconColor","toolbarIconSize"].forEach(t=>{void 0!==e[t]&&(this.plugin.settings[t]=e[t])})}fixImportedCommandIds(){const e={"editor:toggle-numbered-list":"editing-toolbar:toggle-numbered-list","editor:toggle-bullet-list":"editing-toolbar:toggle-bullet-list","editor:toggle-highlight":"editing-toolbar:toggle-highlight","toggle-highlight":"editing-toolbar:toggle-highlight","editing-toolbar:editor:toggle-bold":"editing-toolbar:toggle-bold","editing-toolbar:editor:toggle-italics":"editing-toolbar:toggle-italics","editing-toolbar:editor:toggle-strikethrough":"editing-toolbar:toggle-strikethrough","editing-toolbar:editor:toggle-inline-math":"editing-toolbar:toggle-inline-math","editing-toolbar:editor:insert-callout":"editing-toolbar:insert-callout","editing-toolbar:editor:insert-link":"editing-toolbar:insert-link","cMenuToolbar-Divider-Line":"editingToolbar-Divider-Line"},t=o=>{o&&Array.isArray(o)&&o.forEach(o=>{o.id&&e[o.id]&&(o.id=e[o.id]),o.SubmenuCommands&&Array.isArray(o.SubmenuCommands)&&t(o.SubmenuCommands)})};t(this.plugin.settings.menuCommands),t(this.plugin.settings.customCommands),t(this.plugin.settings.followingCommands),t(this.plugin.settings.topCommands),t(this.plugin.settings.fixedCommands),t(this.plugin.settings.mobileCommands)}restoreBackup(e){this.plugin.settings.positionStyle=e.positionStyle,this.plugin.settings.menuCommands=e.menuCommands,this.plugin.settings.customCommands=e.customCommands,this.plugin.settings.followingCommands=e.followingCommands,this.plugin.settings.topCommands=e.topCommands,this.plugin.settings.fixedCommands=e.fixedCommands,this.plugin.settings.mobileCommands=e.mobileCommands}onClose(){const{contentEl:e}=this;e.empty()}getPositionStyleName(e){switch(e){case"following":return v("Following Style");case"top":return v("Top Style");case"fixed":return v("Fixed Style");default:return e}}}const Uo=new Map;class Vo extends Error{constructor(e,t=8e3){super(e),this.name="AIUserNoticeError",this.noticeDuration=t}}function Ho(e){const t=e?.status;if("number"==typeof t)return t;const o=e?.response?.status;return"number"==typeof o?o:null}function Wo(e){if(e instanceof Error&&e.message.trim())return e.message.trim();if("string"==typeof e&&e.trim())return e.trim();const t=e?.response,o=t?.json?.error?.message||t?.json?.message||t?.text;return"string"==typeof o&&o.trim()?o.trim():v("Unknown connection error.")}function jo(t){if(!(t instanceof Vo))return!1;const o=Date.now();return o-(Uo.get(t.message)??0)<6e3||(Uo.set(t.message,o),new e.Notice(t.message,t.noticeDuration)),!0}const Ko="https://pkmer.cn/products/UserProfile/",Yo=`${Ko}#tab-ai-token`,Go="https://pkmer.net/en/products/ai-token";const Jo=[{id:"general",name:v("General"),icon:"gear"},{id:"appearance",name:v("Appearance"),icon:"brush"},{id:"customcommands",name:v("Custom Commands"),icon:"lucide-rectangle-ellipsis"},{id:"commands",name:v("Toolbar Commands"),icon:"lucide-command"},{id:"ai",name:v("AI"),icon:"lucide-sparkles"},{id:"importexport",name:v("Import/Export"),icon:"lucide-import"}];function Zo(e){const{el:t,containerEl:o,swatches:n,opacity:i,defaultColor:a}=e;return{el:t,container:o,theme:"nano",swatches:n,lockOpacity:!i,default:a,position:"left-middle",components:{preview:!0,hue:!0,opacity:!!i,interaction:{hex:!0,rgba:!1,hsla:!1,input:!0,cancel:!0,save:!0}}}}function Xo(e,t){if(!t||!Array.isArray(t))return-1;return t.findIndex(t=>t?.id===e)}class Qo extends e.PluginSettingTab{getLocalizedCommandName(e){return v(e)}constructor(e,t){super(e,t),this.pickrs=[],this.activeTab="general",this.cachedCustomOllamaModels=[],this.cachedCustomOllamaModelsBaseUrl="",this.cachedCustomOllamaModelsError="",this.aestheticStyleMap={default:"editingToolbarDefaultAesthetic",tiny:"editingToolbarTinyAesthetic",glass:"editingToolbarGlassAesthetic",custom:"editingToolbarCustomAesthetic",top:"top",following:"editingToolbarFlex",fixed:"fixed"},this.plugin=t,this.currentEditingConfig=this.plugin.settings.positionStyle,addEventListener("editingToolbar-NewCommand",()=>{se(this.plugin),Ie(e,this.plugin),this.display()})}async refreshCustomOllamaModels(){const t=this.plugin.settings.ai.customModel.baseUrl.trim();try{const o=await this.plugin.aiManager.listCustomOllamaModels();this.cachedCustomOllamaModels=o,this.cachedCustomOllamaModelsBaseUrl=t,this.cachedCustomOllamaModelsError="",0===o.length&&new e.Notice(v("No Ollama models found at this endpoint."))}catch(o){this.cachedCustomOllamaModels=[],this.cachedCustomOllamaModelsBaseUrl=t,this.cachedCustomOllamaModelsError=Wo(o),new e.Notice(`${v("Failed to load Ollama models:")} ${this.cachedCustomOllamaModelsError}`)}this.display()}display(){this.destroyPickrs();const{containerEl:t}=this;t.empty(),this.createHeader(t);const o=t.createEl("div",{cls:"editing-toolbar-tabs"});Jo.forEach(t=>{const n=o.createEl("div",{cls:"editing-toolbar-tab "+(this.activeTab===t.id?"active":"")});e.setIcon(n,t.icon),n.createEl("span",{text:t.name}),n.addEventListener("click",()=>{this.activeTab=t.id,this.display()})});const n=t.createEl("div",{cls:"editing-toolbar-content"});switch(this.activeTab){case"general":this.displayGeneralSettings(n);break;case"appearance":this.displayAppearanceSettings(n);break;case"customcommands":this.displayCustomCommandSettings(n);break;case"commands":this.displayCommandSettings(n);break;case"ai":this.displayAISettings(n);break;case"importexport":this.displayImportExportSettings(n)}}createDeleteButton(e,t,o=v("Delete")){let n,i=!1;e.setIcon("editingToolbarDelete").setTooltip(o).onClick(async()=>{i?(clearTimeout(n),e.setIcon("editingToolbarDelete").setTooltip(o),e.buttonEl.removeClass("mod-warning"),i=!1,await t()):(i=!0,e.setTooltip(v("Confirm Delete?")).setButtonText(v("Confirm Delete?")),e.buttonEl.addClass("mod-warning"),n=setTimeout(()=>{e.setIcon("editingToolbarDelete").setTooltip(o),e.buttonEl.removeClass("mod-warning"),i=!1},3500))})}displayGeneralSettings(t){const o=t.createDiv("generalSetting-container");o.style.padding="16px",o.style.borderRadius="8px",o.style.backgroundColor="var(--background-secondary)",o.style.marginBottom="20px",new e.Setting(o).setName(v("Editing Toolbar Append Method")).setDesc(v("Choose where Editing Toolbar will append upon regeneration.")).addDropdown(e=>{let t={};Y.map(e=>t[e]=e),e.addOptions(t),e.setValue(this.plugin.settings.appendMethod).onChange(e=>{this.plugin.settings.appendMethod=e,this.plugin.saveSettings()})}),new e.Setting(o).setName(v("Enable Multiple Configurations")).setDesc(v("Enable different command configurations for each position style (following, top, fixed).")).addToggle(e=>e.setValue(this.plugin.settings.enableMultipleConfig||!1).onChange(async e=>{this.plugin.settings.enableMultipleConfig=e,this.plugin.onPositionStyleChange(this.plugin.positionStyle),await this.plugin.saveSettings(),this.display()})),new e.Setting(o).setName(v("Top Toolbar")).setDesc(v("Enable the toolbar positioned at the top.")).addToggle(e=>{e.setValue(this.plugin.settings.enableTopToolbar||!1).onChange(async e=>{const t=this.plugin.settings,o=this.plugin.positionStyle;t.enableTopToolbar=e;let n=null;e?n="top":"top"===o&&(n=t.enableFollowingToolbar?"following":t.enableFixedToolbar?"fixed":null),n&&n!==o&&this.plugin.onPositionStyleChange(n),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar(),this.display()})}),new e.Setting(o).setName(v("Following Toolbar")).setDesc(v("Enable the toolbar that appears upon text selection.")).addToggle(e=>{e.setValue(this.plugin.settings.enableFollowingToolbar||!1).onChange(async e=>{const t=this.plugin.settings,o=this.plugin.positionStyle;t.enableFollowingToolbar=e;let n=null;e?n="following":"following"===o&&(n=t.enableTopToolbar?"top":t.enableFixedToolbar?"fixed":null),n&&n!==o&&this.plugin.onPositionStyleChange(n),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar(),this.display()})}),new e.Setting(o).setName(v("Fixed Toolbar")).setDesc(v("Enable the toolbar whose position may be fixed where you please.")).addToggle(e=>{e.setValue(this.plugin.settings.enableFixedToolbar||!1).onChange(async e=>{const t=this.plugin.settings,o=this.plugin.positionStyle;t.enableFixedToolbar=e;let n=null;e?n="fixed":"fixed"===o&&(n=t.enableTopToolbar?"top":t.enableFollowingToolbar?"following":null),n&&n!==o&&this.plugin.onPositionStyleChange(n),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar(),this.display()})}),new e.Setting(o).setName(v("Mobile Enabled or Not")).setDesc(v("Whether to enable on mobile devices with device width less than 768px.")).addToggle(e=>e.setValue(this.plugin.settings?.isLoadOnMobile??!1).onChange(e=>{this.plugin.settings.isLoadOnMobile=e,this.plugin.saveSettings(),this.triggerRefresh()}));const n=t.createDiv("custom-paintbrush-container");n.style.padding="16px",n.style.borderRadius="8px",n.style.backgroundColor="var(--background-secondary)",n.style.marginBottom="20px",new e.Setting(n).setName(v("🎨 Set Custom Background")).setDesc(v("Click on the picker to adjust the color")).setClass("custom_bg").then(e=>{const t=e.controlEl.createDiv({cls:"pickr-container"});for(let e=0;e<5;e++){const o=t.createDiv({cls:"picker"}),n=Oo.create(Zo({isView:!1,el:o,containerEl:t,swatches:["#FFB78B8C","#CDF4698C","#A0CCF68C","#F0A7D88C","#ADEFEF8C"],opacity:!0,defaultColor:this.plugin.settings[`custom_bg${e+1}`]||"#000000"}));this.setupPickrEvents(n,`custom_bg${e+1}`,"background-color"),this.pickrs.push(n)}}),new e.Setting(n).setName(v("🖌️ Set Custom Font Color")).setDesc(v("Click on the picker to adjust the color")).setClass("custom_font").then(e=>{const t=e.controlEl.createDiv({cls:"pickr-container"});for(let e=0;e<5;e++){const o=t.createDiv({cls:"picker"}),n=Oo.create(Zo({isView:!1,el:o,containerEl:t,swatches:["#D83931","#DE7802","#245BDB","#6425D0","#646A73"],opacity:!0,defaultColor:this.plugin.settings[`custom_fc${e+1}`]||"#000000"}));this.setupPickrEvents(n,`custom_fc${e+1}`,"color"),this.pickrs.push(n)}})}displayAppearanceSettings(t){const o=t.createDiv("appearanceSetting-container");o.style.padding="16px",o.style.borderRadius="8px",o.style.backgroundColor="var(--background-secondary)",o.style.marginBottom="20px";const n=this.plugin.appearanceEditStyle||this.plugin.settings.positionStyle||"top";this.plugin.appearanceEditStyle=n,new e.Setting(o).setName(v("Toolbar Settings")).setDesc(v("Choose which toolbar style's appearance you want to edit.")).addDropdown(e=>{const t={};J.map(e=>t[e]=e),e.addOptions(t).setValue(n).onChange(async e=>{const t=e;this.plugin.appearanceEditStyle=t,this.plugin.settings.positionStyle=t,await this.plugin.saveSettings(),this.display()})}),"top"===n&&(new e.Setting(o).setName(v("Editing Toolbar Auto-hide")).setDesc(v("The toolbar is displayed when the mouse moves over it, otherwise it is automatically hidden")).addToggle(e=>e.setValue(this.plugin.settings?.autohide).onChange(e=>{this.plugin.settings.autohide=e,this.plugin.saveSettings(),this.triggerRefresh()})),new e.Setting(o).setName(v("Editing Toolbar Centred Display")).setDesc(v("Whether the toolbar is centred or full-width, the default is full-width.")).addToggle(e=>e.setValue(this.plugin.settings?.Iscentered).onChange(e=>{this.plugin.settings.Iscentered=e,this.plugin.saveSettings(),this.triggerRefresh()}))),"fixed"===n&&(new e.Setting(o).setName(v("Editing Toolbar Columns")).setDesc(v("Choose the number of columns per row to display on Editing Toolbar.")).addSlider(t=>{t.setLimits(1,32,1).setValue(this.plugin.settings.cMenuNumRows).onChange(e.debounce(async e=>{this.plugin.settings.cMenuNumRows=e,await this.plugin.saveSettings(),this.triggerRefresh()},100,!0)).setDynamicTooltip()}),new e.Setting(o).setName(v("Fixed Position Offset")).setDesc(v("Choose the offset of the Editing Toolbar in the fixed position.")).addButton(e=>e.setButtonText(v("Settings")).onClick(()=>{new T(this.app,this.plugin).open()}))),this.createColorSettings(t)}displayCommandSettings(t){const o=t.createDiv("commandSetting-container");if(o.style.padding="16px",o.style.borderRadius="8px",o.style.backgroundColor="var(--background-secondary)",o.style.marginBottom="20px",this.plugin.settings.enableMultipleConfig&&new e.Setting(o).setName(v("Current Configuration")).setDesc(v("Switch between different command configurations.")).addDropdown(e=>{e.addOption("top",v("Top Style")),e.addOption("fixed",v("Fixed Style")),e.addOption("following",v("Following Style")),this.plugin.settings.isLoadOnMobile&&e.addOption("mobile",v("Mobile Style")),e.setValue(this.currentEditingConfig),e.onChange(async e=>{this.currentEditingConfig=e,this.display()})}),this.plugin.settings.enableMultipleConfig){const o=this.currentEditingConfig;this.getCommandsArrayByType(o);const n=t.createDiv("command-buttons-container");n.style.display="flex",n.style.flexDirection="column",n.style.gap="10px",n.style.padding="16px",n.style.borderRadius="8px",n.style.backgroundColor="var(--background-secondary)";const i=new e.Setting(n).setName(v("Import From")).setDesc(v("Copy commands from another style configuration."));let a="Main menu";const s=new e.Setting(n);s.addDropdown(e=>{e.addOption("Main menu","Main Menu Commands"),"following"!==o&&this.plugin.settings.followingCommands&&e.addOption("following",v("Following Style")),"top"!==o&&this.plugin.settings.topCommands&&e.addOption("top",v("Top Style")),"fixed"!==o&&this.plugin.settings.fixedCommands&&e.addOption("fixed",v("Fixed Style")),"mobile"!==o&&this.plugin.settings.mobileCommands&&e.addOption("mobile",v("Mobile Style")),e.setValue(a).onChange(e=>{a=e})}),s.addExtraButton(e=>e.setIcon("arrow-right")),s.addButton(t=>t.setButtonText(this.currentEditingConfig+" "+v("Import")).setTooltip(v("Copy commands from selected style.")).onClick(async()=>{const t=this.getCommandsArrayByType(a);if(!t||0===t.length)return void new e.Notice("The selected style has no commands to import.");const n=`Import commands from "${a}" to "${this.currentEditingConfig}" `+v("configuration")+"?";No.show(this.app,{message:n,onConfirm:async()=>{switch(o){case"Main menu":this.plugin.settings.menuCommands=[...t];break;case"following":this.plugin.settings.followingCommands=[...t];break;case"top":this.plugin.settings.topCommands=[...t];break;case"fixed":this.plugin.settings.fixedCommands=[...t];break;case"mobile":this.plugin.settings.mobileCommands=[...t]}await this.plugin.saveSettings(),new e.Notice(`Commands imported successfully from "${a}" to "${this.currentEditingConfig}" `+v("configuration")),this.display()}})})),i.addButton(t=>t.setButtonText(v("Clear")+" "+`${this.currentEditingConfig}`).setTooltip(v("Remove all commands from this configuration.")).setWarning().onClick(async()=>{No.show(this.app,{message:v("Are you sure you want to clear all commands under the current style?"),onConfirm:async()=>{switch(o){case"following":this.plugin.settings.followingCommands=[];break;case"top":this.plugin.settings.topCommands=[];break;case"fixed":this.plugin.settings.fixedCommands=[];break;case"mobile":this.plugin.settings.mobileCommands=[]}await this.plugin.saveSettings(),new e.Notice("All commands have been removed."),this.display()}})}))}else{o.createDiv("command-buttons-container").createEl("button",{text:v("One-click Clear"),cls:"mod-warning"}).addEventListener("click",async()=>{No.show(this.app,{message:v("Are you sure you want to clear all commands under the current style?"),onConfirm:async()=>{this.plugin.settings.menuCommands=[],await this.plugin.saveSettings(),new e.Notice(v("All commands have been removed.")),this.display()}})})}const n=t.createDiv("command-lists-container");n.style.padding="16px",n.style.borderRadius="8px",n.addClass(`${this.currentEditingConfig}`),this.plugin.settings.enableMultipleConfig&&n.createEl("div",{cls:`position-style-info ${this.currentEditingConfig}`,text:v("Currently editing commands for")+` "${this.currentEditingConfig} Style" `+v("configuration")}),new e.Setting(n).setName(v("Editing Toolbar Commands")).setDesc(v("Add a command onto Editing Toolbar from Obsidian's commands library. To reorder the commands, drag and drop the command items. To delete them, use the delete buttom to the right of the command item. Editing Toolbar will not automaticaly refresh after reordering commands. Use the refresh button above.")).addButton(e=>{e.setIcon("plus").setTooltip(v("Add")).onClick(()=>{new k(this.plugin,this.currentEditingConfig).open(),this.triggerRefresh()})}),this.createCommandList(n)}displayCustomCommandSettings(t){t.empty();const o=t.createDiv("custom-commands-container");o.createEl("p",{text:v("Add, edit or delete custom format commands.")}),new e.Setting(o).setName(v("Use current line for regex commands")).setDesc(v("When no text is selected, regex commands will use the current line instead of clipboard content")).addToggle(e=>e.setValue(this.plugin.settings?.useCurrentLineForRegex??!1).onChange(async e=>{this.plugin.settings.useCurrentLineForRegex=e,await this.plugin.saveSettings()}));const n=o.createDiv("command-list-container");n.style.padding="16px",n.style.borderRadius="8px",n.style.backgroundColor="var(--background-secondary)",n.style.marginBottom="20px",n.style.marginTop="20px";const i=o.createDiv("add-command-button-container");i.style.padding="16px",i.style.borderRadius="8px",i.style.backgroundColor="var(--background-secondary)",i.style.marginBottom="20px",i.style.marginTop="20px",i.style.display="flex",i.style.gap="10px";const a=i.createEl("button",{text:v("Add Format Command")});a.addClass("mod-cta"),a.addEventListener("click",()=>{new $o(this.app,this.plugin,null).open()});const s=i.createEl("button",{text:v("Add Regex Command")});s.addClass("mod-cta"),s.addEventListener("click",()=>{new Bo(this.app,this.plugin,null).open()}),this.plugin.settings.customCommands.forEach((t,o)=>{const i=new e.Setting(n).setName(t.name),a=createFragment();let s=`${v("ID")}: ${t.id}`;t.useRegex?s+=`, ${v("Pattern")}: ${t.regexPattern}`:s+=`, ${v("Prefix")}: ${t.prefix}, ${v("Suffix")}: ${t.suffix}`,a.createSpan({text:s});const r=a.createSpan({cls:"command-type-badge"});if(t.useRegex?(r.addClass("regex"),r.setText(v("Regex"))):r.setText(v("Prefix/Suffix")),i.descEl.appendChild(a),i.addButton(o=>o.setButtonText(v("Add to Toolbar")).setTooltip(v("Add this command to the toolbar.")).setButtonText(v("Add to Toolbar")).setTooltip(v("Add this command to the toolbar.")).onClick(()=>{if(this.plugin.settings.enableMultipleConfig)new _o(this.app,this.plugin,t).open();else{if(this.plugin.settings.menuCommands.some(e=>e.id===`editing-toolbar:${t.id}`))return void new e.Notice(v("This command is already in the toolbar."));const o={id:`editing-toolbar:${t.id}`,name:t.name,icon:t.icon||"obsidian-new"};this.plugin.settings.menuCommands.push(o),this.plugin.saveSettings().then(()=>{new e.Notice(v("Command added to toolbar")),dispatchEvent(new Event("editingToolbar-NewCommand")),this.plugin.reloadCustomCommands()})}})).addExtraButton(e=>{e.setIcon("pencil").setTooltip(v("Edit")).onClick(()=>{t.useRegex?new Bo(this.app,this.plugin,o).open():new $o(this.app,this.plugin,o).open()})}).addButton(t=>this.createDeleteButton(t,async()=>{const t=`editing-toolbar:${this.plugin.settings.customCommands[o].id}`;this.removeCommandFromConfig(this.plugin.settings.menuCommands,t),this.plugin.settings.enableMultipleConfig&&(this.removeCommandFromConfig(this.plugin.settings.followingCommands,t),this.removeCommandFromConfig(this.plugin.settings.topCommands,t),this.removeCommandFromConfig(this.plugin.settings.fixedCommands,t),this.plugin.settings.isLoadOnMobile&&this.removeCommandFromConfig(this.plugin.settings.mobileCommands,t)),this.plugin.settings.customCommands.splice(o,1),await this.plugin.saveSettings(),this.plugin.reloadCustomCommands(),this.display(),new e.Notice(v("Command Deleted"))})),t.icon)try{const o=i.nameEl.createSpan({cls:"editingToolbarSettingsIcon"});o.style.marginRight="8px",ue(t.icon)?o.innerHTML=t.icon:e.setIcon(o,t.icon)}catch(e){console.error("Failed to set icon:",e)}})}triggerRefresh(){setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)}createHeader(t){const o=t.createEl("div",{cls:"editing-toolbar-header"});o.createEl("div",{cls:"editing-toolbar-title-container"}).createEl("h1",{text:"Obsidian Editing Toolbar: "+this.plugin.manifest.version,cls:"editing-toolbar-title"});const n=o.createEl("div",{cls:"editing-toolbar-info"});new e.Setting(n).setClass("editing-toolbar-fix-button").addButton(e=>{e.setIcon("wrench").setTooltip(v("Fix")).onClick(()=>{new Do(this.app,this.plugin).open()})})}getAppearanceBucket(e){const t=this.plugin.settings;t.appearanceByStyle&&"object"==typeof t.appearanceByStyle||(t.appearanceByStyle={});const o=t.appearanceByStyle;return o[e]&&"object"==typeof o[e]||(o[e]={}),o[e]}createColorSettings(t){const o=this.plugin.appearanceEditStyle||this.plugin.settings.positionStyle||"top",n=this.getAppearanceBucket(o),i=t.createDiv("custom-toolbar-container");i.style.padding="16px",i.style.borderRadius="8px",i.style.backgroundColor="var(--background-secondary)",new e.Setting(i).setName(v("Toolbar Theme")).setDesc(v("Select a preset toolbar theme, automatically setting the background color, icon color, and size for the selected style.")).addDropdown(e=>{const t={};G.forEach(e=>{t[e]="custom"===e?v("Custom Theme"):e}),e.addOptions(t),e.selectEl.options[3].disabled=!0,e.addOption("light","┌ Light"),e.addOption("dark","├ Dark"),e.addOption("vibrant","├ Vibrant"),e.addOption("minimal","├ Minimal"),e.addOption("elegant","└ Elegant"),e.setValue(n.aestheticStyle??this.plugin.settings.aestheticStyle),e.onChange(async e=>{const o=this.plugin.appearanceEditStyle||this.plugin.settings.positionStyle||"top",n=this.getAppearanceBucket(o);switch(e in t?(n.aestheticStyle=e,n.toolbarIconSize=18):n.aestheticStyle="custom",e){case"light":n.toolbarBackgroundColor="#F5F8FA",n.toolbarIconColor="#4A5568",n.toolbarIconSize=18;break;case"dark":n.toolbarBackgroundColor="#2D3033",n.toolbarIconColor="#E2E8F0",n.toolbarIconSize=18;break;case"vibrant":n.toolbarBackgroundColor="#7E57C2",n.toolbarIconColor="#FFFFFF",n.toolbarIconSize=20;break;case"minimal":n.toolbarBackgroundColor="#F8F9FA",n.toolbarIconColor="#6B7280",n.toolbarIconSize=16;break;case"elegant":n.toolbarBackgroundColor="#1A2F28",n.toolbarIconColor="#D4AF37",n.toolbarIconSize=19}const i=n.toolbarBackgroundColor??this.plugin.settings.toolbarBackgroundColor,a=n.toolbarIconColor??this.plugin.settings.toolbarIconColor,s=n.toolbarIconSize??18;document.documentElement.style.setProperty("--editing-toolbar-background-color",i),document.documentElement.style.setProperty("--editing-toolbar-icon-color",a),document.documentElement.style.setProperty("--toolbar-icon-size",`${s}px`),this.plugin.toolbarIconSize=s,this.destroyPickrs(),this.display(),await this.plugin.saveSettings(),this.triggerRefresh()})}),new e.Setting(i).setName(v("Toolbar Background Color")).setDesc(v("Set the background color of the toolbar.")).setClass("toolbar_background").then(e=>{const t=e.controlEl.createDiv({cls:"pickr-container"}),o=t.createDiv({cls:"picker"}),i=Oo.create(Zo({isView:!1,el:o,containerEl:t,swatches:["#F5F8FA","#F4F1E8","#2D3033","#1A2F28","#2A1D3B"],opacity:!0,defaultColor:n.toolbarBackgroundColor??this.plugin.settings.toolbarBackgroundColor}));this.setupPickrEvents(i,"toolbarBackgroundColor","background-color"),this.pickrs.push(i)}),new e.Setting(i).setName(v("Toolbar Icon Color")).setDesc(v("Set the color of the toolbar icon.")).setClass("toolbar_icon").then(e=>{const t=e.controlEl.createDiv({cls:"pickr-container"}),o=t.createDiv({cls:"picker"}),n=Oo.create(Zo({isView:!1,el:o,containerEl:t,swatches:["#4A5568","#D4AF37","#2D3033","#6D5846","#4C2A55"],opacity:!1,defaultColor:this.plugin.settings.toolbarIconColor}));this.pickrs.push(n),this.setupPickrEvents(n,"toolbarIconColor","icon-color")}),new e.Setting(i).setName(v("Toolbar Icon Size")).setDesc(v("Set the size of the toolbar icon (px); default: 18px")).addSlider(e=>{const t=n.toolbarIconSize??this.plugin.settings.toolbarIconSize;e.setValue(t).setLimits(12,32,1).setDynamicTooltip().onChange(async e=>{const t=this.plugin.positionStyle,o=this.plugin.appearanceEditStyle||this.plugin.settings.positionStyle||"top",n=this.getAppearanceBucket(o);n.toolbarIconSize=e,n.aestheticStyle="custom",t===o&&(this.plugin.toolbarIconSize=e,document.documentElement.style.setProperty("--toolbar-icon-size",`${e}px`)),await this.plugin.saveSettings(),this.display(),this.triggerRefresh()})});const a=i.createDiv("toolbar-preview-container");a.addClass("toolbar-preview-section"),a.style.marginTop="20px";a.createEl("h3",{text:v("Toolbar Preview (With a hypothetical command configuration.)")}).style.marginBottom="10px";const s=a.createDiv();s.classList.add("preview-toolbar-wrapper"),s.classList.add(`preview-${o}`);const r=s.createDiv();r.classList.add("editing-toolbar-preview"),r.classList.add(`preview-${o}`),r.setAttribute("id","editingToolbarModalBar");const l=n.aestheticStyle??this.plugin.settings.aestheticStyle??"default";if(this.applyAestheticStyle(r,l,o),"fixed"===o){const e=this.plugin.settings.toolbarIconSize||18,t=this.plugin.settings.cMenuNumRows||6;r.style.display="grid",r.style.gridTemplateColumns=`repeat(${t}, ${e+10}px)`,r.style.gap=`${Math.max((e-18)/4,2)}px`,r.style.margin="0 auto"}[{id:"bold",name:"Bold",icon:"bold"},{id:"italics",name:"Italics",icon:"italic"},{id:"trikethrough",name:"Strikethrough",icon:"strikethrough"},{id:"code",name:"Code",icon:"code"},{id:"blockquote",name:"Blockquote",icon:"quote-glyph"},{id:"insert-link",name:"Link",icon:"link"},{id:"left-sidebar",name:"Left sidebar",icon:"lucide-panel-left"},{id:"editor:insert-embed",name:"Add embed",icon:"note-glyph"},{id:"editor:insert-link",name:"Insert markdown link",icon:"link-glyph"},{id:"editor:insert-tag",name:"Add tag",icon:"price-tag-glyph"},{id:"editor:insert-wikilink",name:"Add internal link",icon:"bracket-glyph"},{id:"editor:toggle-code",name:"Code",icon:"code-glyph"},{id:"editor:toggle-blockquote",name:"Blockquote",icon:"lucide-text-quote"},{id:"editor:toggle-checklist-status",name:"Checklist status",icon:"checkbox-glyph"},{id:"editor:toggle-comments",name:"Comment",icon:"percent-sign-glyph"},{id:"editor:insert-callout",name:"Insert Callout",icon:"lucide-quote"},{id:"editor:insert-mathblock",name:"MathBlock",icon:"lucide-sigma-square"},{id:"editor:insert-table",name:"Insert Table",icon:"lucide-table"}].forEach(t=>{const o=new e.ButtonComponent(r);o.setClass("editingToolbarCommandItem"),o.buttonEl.classList.add("preview-button"),o.setTooltip(v(t.name)),t.icon&&e.setIcon(o.buttonEl,t.icon)});const c="custom"===l,d=n.toolbarBackgroundColor??this.plugin.settings.toolbarBackgroundColor,u=n.toolbarIconColor??this.plugin.settings.toolbarIconColor,m=n.toolbarIconSize??this.plugin.settings.toolbarIconSize??18;c&&d?r.style.backgroundColor=d:r.style.removeProperty("background-color");r.querySelectorAll("svg").forEach(e=>{c&&u?e.style.color=u:e.style.removeProperty("color"),e.style.width=`${m}px`,e.style.height=`${m}px`})}createCommandList(t){let o=[];if(this.plugin.settings.enableMultipleConfig)switch(this.currentEditingConfig){case"mobile":o=this.plugin.settings.mobileCommands;break;case"following":o=this.plugin.settings.followingCommands;break;case"top":o=this.plugin.settings.topCommands;break;case"fixed":o=this.plugin.settings.fixedCommands;break;default:o=this.plugin.settings.menuCommands}else o=this.plugin.settings.menuCommands;const n=t.createEl("div",{cls:"editingToolbarSettingsTabsContainer"});let i="";co.create(n,{group:"item",animation:500,draggable:".setting-item",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",dragoverBubble:!1,forceFallback:!0,fallbackOnBody:!0,swapThreshold:.7,fallbackClass:"sortable-fallback",easing:"cubic-bezier(1, 0, 0, 1)",delay:800,delayOnTouchOnly:!0,touchStartThreshold:5,filter:".setting-item-control button, .dropdown, .editingToolbarMenuTypeDropdown",preventOnFilter:!1,onChoose:function(e){e.item.classList.add("sortable-chosen-feedback")},onUnchoose:function(e){e.item.classList.remove("sortable-chosen-feedback")},onSort:e=>{if(e.from.className===e.to.className){const t=o,[n]=t.splice(e.oldIndex,1);if(t.splice(e.newIndex,0,n),this.plugin.settings.enableMultipleConfig)switch(this.currentEditingConfig){case"mobile":this.plugin.settings.mobileCommands=t;break;case"following":this.plugin.settings.followingCommands=t;break;case"top":this.plugin.settings.topCommands=t;break;case"fixed":this.plugin.settings.fixedCommands=t}else this.plugin.settings.menuCommands=t;this.plugin.saveSettings()}this.triggerRefresh()},onStart:function(e){i=e.item.className}});const s=o;s.forEach((t,r)=>{const l=new e.Setting(n);if("SubmenuCommands"in t){if(l.settingEl.setAttribute("data-id",t.id),l.setClass("editingToolbarCommandItem").setClass("editingToolbarCommandsubItem").setName(this.getLocalizedCommandName(t.name)).addButton(e=>{e.setClass("editingToolbarSettingsIcon").onClick(async()=>{new w(this.plugin,t,!1,null,this.currentEditingConfig).open()}),ue(t.icon)?e.buttonEl.innerHTML=t.icon:e.setIcon(t.icon)}).addButton(e=>{e.setIcon("pencil").setTooltip(v("Change Submenu Name")).setClass("editingToolbarSettingsButton").onClick(async()=>{new S(this.app,this.plugin,t,!1,this.currentEditingConfig).open()})}).addDropdown(o=>{o.addOption("submenu",v("Button Submenu")).addOption("dropdown",v("Dropdown Menu")).setValue(t.menuType||"submenu").onChange(async o=>{t.menuType=o,this.plugin.updateCurrentCommands(s,this.currentEditingConfig),await this.plugin.saveSettings(),this.triggerRefresh(),new e.Notice(v("Menu type changed to")+": "+v("dropdown"===o?"Dropdown Menu":"Button Submenu"))}),o.selectEl.addClass("editingToolbarMenuTypeDropdown")}).addButton(e=>this.createDeleteButton(e,async()=>{s.remove(t),this.plugin.updateCurrentCommands(s,this.currentEditingConfig),await this.plugin.saveSettings(),this.display(),this.triggerRefresh(),console.log(`%cCommand '${t.name}' was removed from editingToolbar`,"color: #989cab")})),"editingToolbar-plugin:change-font-color"==t.id)return;if("editingToolbar-plugin:change-background-color"==t.id)return;const n=l.settingEl.createEl("div",{cls:"editingToolbarSettingsTabsContainer_sub"});co.create(n,{group:{name:"item",pull:!0,put:function(){return!i.includes("editingToolbarCommandsubItem")}},draggable:".setting-item",animation:150,ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",dragoverBubble:!1,fallbackOnBody:!0,swapThreshold:.7,forceFallback:!0,delay:800,delayOnTouchOnly:!0,touchStartThreshold:5,fallbackClass:"sortable-fallback",easing:"cubic-bezier(1, 0, 0, 1)",onStart:function(){},onSort:e=>{if(e.from.className===e.to.className){const t=o,n=t[r]?.SubmenuCommands;if(n){const[o]=n.splice(e.oldIndex,1);n.splice(e.newIndex,0,o),this.plugin.updateCurrentCommands(t,this.currentEditingConfig),this.plugin.saveSettings()}}else if("editingToolbarSettingsTabsContainer"===e.to.className){const t=o,n=e.target.parentElement?.dataset?.id;if(!n)return void console.error("Cannot find parent dataset id");const i=Xo(n,t);if(-1===i||!t[i])return void console.error("Cannot find parent command:",n);const a=t[i].SubmenuCommands;if(!a||!Array.isArray(a)||e.oldIndex<0||e.oldIndex>=a.length)return void console.error("Invalid drag operation");const[s]=a.splice(e.oldIndex,1);t.splice(e.newIndex,0,s),this.plugin.updateCurrentCommands(t,this.currentEditingConfig),this.plugin.saveSettings()}else if("editingToolbarSettingsTabsContainer"===e.from.className){const t=o,n=e.target.parentElement?.dataset?.id;if(!n)return void console.error("Cannot find target dataset id");const i=Xo(n,t);if(-1===i||!t[i])return void console.error("Cannot find target command:",n);const a=t[i].SubmenuCommands;if(!a||!Array.isArray(a)||e.oldIndex<0||e.oldIndex>=t.length)return void console.error("Invalid drag operation");const[s]=t.splice(e.oldIndex,1);a.splice(e.newIndex,0,s),this.plugin.updateCurrentCommands(t,this.currentEditingConfig),this.plugin.saveSettings()}this.triggerRefresh()}}),t.SubmenuCommands.forEach(o=>{const i=new e.Setting(n);i.setClass("editingToolbarCommandItem").addButton(e=>{e.setClass("editingToolbarSettingsIcon").onClick(async()=>{new w(this.plugin,o,!0,null,this.currentEditingConfig).open()}),ue(o?.icon)?e.buttonEl.innerHTML=o.icon:e.setIcon(o.icon)}).setName(this.getLocalizedCommandName(o.name)).addButton(e=>{e.setIcon("pencil").setTooltip(v("Change Command Name")).setClass("editingToolbarSettingsButton").onClick(async()=>{new S(this.app,this.plugin,o,!0,this.currentEditingConfig).open()})}).addButton(e=>this.createDeleteButton(e,async()=>{t.SubmenuCommands.remove(o),await this.plugin.saveSettings(),this.display(),this.triggerRefresh(),console.log(`%cCommand '${t.name}' was removed from editingToolbar`,"color: #989cab")})),i.nameEl})}else l.addButton(e=>{e.setClass("editingToolbarSettingsIcon").onClick(async()=>{new w(this.plugin,t,!1,null,this.currentEditingConfig).open()}),ue(t.icon)?e.buttonEl.innerHTML=t.icon:e.setIcon(t.icon)}),"editingToolbar-Divider-Line"==t.id&&l.setClass("editingToolbar-Divider-Line"),l.setClass("editingToolbarCommandItem").setName(this.getLocalizedCommandName(t.name)).addButton(e=>{e.setIcon("pencil").setTooltip(v("Change Command Name")).setClass("editingToolbarSettingsButton").onClick(async()=>{new S(this.app,this.plugin,t,!1,this.currentEditingConfig).open()})}).addButton(e=>{e.setIcon("editingToolbarSub").setTooltip(v("Add Submenu")).setClass("editingToolbarSettingsButton").setClass("editingToolbarSettingsButtonaddsub").onClick(async()=>{const e={id:"SubmenuCommands-"+a(1),name:"submenu",icon:"remix-Filter3Line",SubmenuCommands:[]},t=o;t.splice(r+1,0,e),this.plugin.updateCurrentCommands(t,this.currentEditingConfig),await this.plugin.saveSettings(),this.display(),this.triggerRefresh(),console.log(`%cCommand '${e.id}' add `,"color: #989cab")})}).addButton(e=>{e.setIcon("vertical-split").setTooltip(v("Add Separator")).setClass("editingToolbarSettingsButton").setClass("editingToolbarSettingsButtonaddsub").onClick(async()=>{const e={id:"editingToolbar-Divider-Line",name:v("Vertical Split"),icon:"vertical-split"},t=o;t.splice(r+1,0,e),this.plugin.updateCurrentCommands(t,this.currentEditingConfig),await this.plugin.saveSettings(),this.display(),this.triggerRefresh()})}).addButton(e=>this.createDeleteButton(e,async()=>{s.remove(t),this.plugin.updateCurrentCommands(s,this.currentEditingConfig),await this.plugin.saveSettings(),this.display(),this.triggerRefresh(),console.log(`%cCommand '${t.name}' was removed from editingToolbar`,"color: #989cab")}))})}setupPickrEvents(e,t,o){e.on("save",e=>{const n=e.toHEXA().toString(),i=this.plugin.positionStyle,a=this.plugin.appearanceEditStyle||this.plugin.settings.positionStyle||i||"top";if("toolbarBackgroundColor"===t||"toolbarIconColor"===t){const e=this.getAppearanceBucket(a);e[t]=n,i===a&&document.documentElement.style.setProperty(`--editing-toolbar-${o}`,n),"custom"!==e.aestheticStyle&&(e.aestheticStyle="custom"),this.display(),this.triggerRefresh()}else this.plugin.settings[t]=n;this.plugin.saveSettings()})}destroyPickrs(){this.pickrs.forEach(e=>{e&&e.destroyAndRemove()}),this.pickrs=[]}hide(){this.destroyPickrs(),this.triggerRefresh()}removeCommandFromConfig(e,t){if(e)for(let o=e.length-1;o>=0;o--)e[o].id!==t?e[o].SubmenuCommands&&this.removeCommandFromConfig(e[o].SubmenuCommands,t):e.splice(o,1)}displayAISettings(t){const o=t.createDiv("editing-toolbar-ai-grid"),n=this.plugin.settings.ai.enabled,i=n&&this.plugin.settings.ai.enableInlineCompletion,a=i&&"auto"===this.plugin.settings.ai.completionTrigger,s=n&&this.plugin.settings.ai.enableCustomModel,r=this.plugin.settings.ai.pkmerModelRouting.mode,l=e=>{switch(e){case"04-fast":return v("Light model");case"03-agent":return v("Reasoning model");default:return e}},c=e=>(A.forEach(t=>{e.addOption(t.value,l(t.value))}),e),d=e=>{const t=e.collapsible?o.createEl("details",{cls:"editing-toolbar-ai-card editing-toolbar-ai-disclosure"}):o.createDiv("editing-toolbar-ai-card");e.collapsible&&e.open&&(t.open=!0);const n=(e.collapsible?t.createEl("summary",{cls:"editing-toolbar-ai-card-summary"}):t.createDiv("editing-toolbar-ai-card-summary editing-toolbar-ai-card-summary-static")).createDiv("editing-toolbar-ai-card-header"),i=n.createDiv("editing-toolbar-ai-card-copy");if(i.createDiv({cls:"editing-toolbar-ai-card-title",text:e.title}),i.createDiv({cls:"editing-toolbar-ai-card-desc",text:e.desc}),e.toggle){const{toggle:t}=e,o=n.createEl("div",{cls:"editing-toolbar-ai-card-toggle checkbox-container"});t.value&&o.addClass("is-enabled"),o.addEventListener("click",e=>{e.stopPropagation();const n=!o.hasClass("is-enabled");n?o.addClass("is-enabled"):o.removeClass("is-enabled"),t.onChange(n)})}else if(e.headerDropdown){const{headerDropdown:t}=e,o=n.createEl("select",{cls:"editing-toolbar-ai-card-header-select dropdown"});t.options.forEach(e=>{const n=o.createEl("option",{text:e.label});n.value=e.value,e.value===t.value&&(n.selected=!0)}),o.addEventListener("click",e=>e.stopPropagation()),o.addEventListener("change",e=>{t.onChange(e.target.value)})}else e.badge&&n.createDiv({cls:"editing-toolbar-ai-card-badge",text:e.badge});return t.createDiv("editing-toolbar-ai-card-body")};if(d({title:v("AI Editor"),desc:v("Enable AI editor features such as inline completion and selection rewrite."),toggle:{value:this.plugin.settings.ai.enabled,onChange:async e=>{e?await this.plugin.aiManager.requestEnableAIWithConsent("settings"):await this.plugin.aiManager.disableAI(),this.display()}}}),n){const t=d({title:v("PKMer AI"),desc:v("Log in to PKMer AI to get free AI features without manual model setup."),badge:this.plugin.settings.ai.pkmer.userInfo?v("Logged in"):v("Not logged in")}),o=document.createDocumentFragment();if(o.append(this.plugin.aiManager.getPKMerStatusText()),void 0!==this.plugin.settings.ai.pkmer.userInfo?.ai_quota?.quota){o.append(" ");const e=document.createElement("a");e.textContent=v("More Quota"),e.href=function(e=E()){return I(e)?Yo:Go}(),e.target="_blank",e.rel="noopener noreferrer",o.appendChild(e)}if(new e.Setting(t).setDesc(o).addButton(e=>{this.plugin.settings.ai.pkmer.userInfo?e.setButtonText(v("Logout")).onClick(async()=>{await this.plugin.aiManager.logoutFromPKMer(),this.display()}):e.setButtonText(v("Login")).setCta().onClick(async()=>{await this.plugin.aiManager.loginWithPKMer(),this.display()})}).addButton(e=>{this.plugin.settings.ai.pkmer.userInfo?e.setButtonText(v("Check Quota")).onClick(async()=>{await this.plugin.aiManager.refreshPKMerQuota(),this.display()}):e.buttonEl.style.display="none"}),!this.plugin.settings.ai.pkmer.userInfo){const e=t.createDiv({cls:"editing-toolbar-ai-note"});e.appendText(`${v("Need a PKMer AI account?")} `);const o=e.createEl("a",{text:v("Open PKMer AI")});o.href=function(e=E()){return I(e)?Ko:Go}(),o.target="_blank",o.rel="noopener noreferrer"}const n=t.createDiv({cls:"editing-toolbar-ai-note",text:v("Checking current AI route...")});this.plugin.aiManager.getProviderRouteStatusText().then(e=>{n.setText(e)});const u=d({title:v("Editor Features"),desc:v("Configure inline completion and rewrite after your AI provider is ready."),toggle:{value:this.plugin.settings.ai.enableInlineCompletion,onChange:async e=>{this.plugin.settings.ai.enableInlineCompletion=e,await this.plugin.saveSettings(),this.display()}}});i&&(new e.Setting(u).setName(v("Completion Mode")).setDesc(v("Choose whether completion is triggered manually or automatically after a short pause.")).addDropdown(e=>{e.addOption("manual",v("Manual")).addOption("auto",v("Auto")).setValue(this.plugin.settings.ai.completionTrigger).onChange(async e=>{this.plugin.settings.ai.completionTrigger=e,await this.plugin.saveSettings(),this.display()})}),"manual"===this.plugin.settings.ai.completionTrigger&&new e.Setting(u).setName(v("Manual Completion Shortcut")).setDesc(v("Default shortcut is Ctrl+J. You can customize it in Obsidian Hotkeys.")).addButton(e=>{e.setButtonText(v("Open Hotkey Settings")).onClick(()=>{this.app.setting.open(),this.app.setting.openTabById("hotkeys")})}),a&&new e.Setting(u).setName(v("Completion Delay (ms)")).setDesc(v("Delay before auto-triggering inline completion.")).addText(e=>{e.setValue(String(this.plugin.settings.ai.completionDelay)).onChange(async e=>{const t=Number.parseInt(e,10);!Number.isNaN(t)&&t>=0&&(this.plugin.settings.ai.completionDelay=t,await this.plugin.saveSettings())})}));const m=d({title:v("PKMer Model"),desc:v("Choose models by task."),headerDropdown:{options:[{value:"smart",label:v("Default")},{value:"manual",label:v("Manual")}],value:r,onChange:async e=>{this.plugin.settings.ai.pkmerModelRouting.mode=e,await this.plugin.saveSettings(),this.display()}},collapsible:!0,open:"manual"===r});if("smart"===r){const e=m.createDiv({cls:"editing-toolbar-ai-note"});e.createDiv({text:`${v("Completion")}: ${l(P(this.plugin.settings.ai,"completion"))}`}),e.createDiv({text:`${v("Rewrite")}: ${l(P(this.plugin.settings.ai,"rewrite"))}`}),e.createDiv({text:`${v("Reasoning")}: ${l(P(this.plugin.settings.ai,"reasoning"))}`}),e.createDiv({text:`${v("Structured")}: ${l(P(this.plugin.settings.ai,"artifact"))}`})}else new e.Setting(m).setName(v("Completion")).setDesc(v("Used for inline completion.")).addDropdown(e=>{c(e).setValue(this.plugin.settings.ai.pkmerModelRouting.completion).onChange(async e=>{this.plugin.settings.ai.pkmerModelRouting.completion=e,await this.plugin.saveSettings()})}),new e.Setting(m).setName(v("Rewrite")).setDesc(v("Used for normal rewrite.")).addDropdown(e=>{c(e).setValue(this.plugin.settings.ai.pkmerModelRouting.rewrite).onChange(async e=>{this.plugin.settings.ai.pkmerModelRouting.rewrite=e,await this.plugin.saveSettings()})}),new e.Setting(m).setName(v("Reasoning")).setDesc(v("Used for explain, summarize, and custom prompts.")).addDropdown(e=>{c(e).setValue(this.plugin.settings.ai.pkmerModelRouting.reasoning).onChange(async e=>{this.plugin.settings.ai.pkmerModelRouting.reasoning=e,await this.plugin.saveSettings()})}),new e.Setting(m).setName(v("Structured")).setDesc(v("Used for frontmatter and canvas.")).addDropdown(e=>{c(e).setValue(this.plugin.settings.ai.pkmerModelRouting.artifact).onChange(async e=>{this.plugin.settings.ai.pkmerModelRouting.artifact=e,await this.plugin.saveSettings()})});m.createDiv({cls:"editing-toolbar-ai-note",text:v("PKMer route only.")});const p=d({title:v("Custom Model (Optional)"),desc:v("Custom model is used automatically when PKMer AI is unavailable."),toggle:{value:this.plugin.settings.ai.enableCustomModel,onChange:async e=>{this.plugin.settings.ai.enableCustomModel=e,await this.plugin.saveSettings(),this.display()}}});if(s){const t=this.plugin.settings.ai.customModel.apiFormat??"openai-compatible",o="ollama"===t,n=this.plugin.settings.ai.customModel.baseUrl.trim(),i=o&&this.cachedCustomOllamaModelsBaseUrl===n?this.cachedCustomOllamaModels:[],a=o&&this.cachedCustomOllamaModelsBaseUrl===n?this.cachedCustomOllamaModelsError:"",s=this.plugin.aiManager.hasSecureStorage()?this.plugin.aiManager.hasCustomModelApiKey()?v("Stored securely in Obsidian secret storage."):v("Will be stored securely in Obsidian secret storage."):v("Current Obsidian version does not support secure secret storage."),r=o?`${v("Optional for Ollama. Leave empty unless your gateway requires authentication.")} ${s}`.trim():s;if(new e.Setting(p).setName(v("Custom API Format")).setDesc(v("Choose whether the custom model uses an OpenAI-compatible endpoint or the native Ollama API.")).addDropdown(e=>{e.addOption("openai-compatible",v("OpenAI-compatible")).addOption("ollama",v("Ollama")).setValue(t).onChange(async e=>{this.plugin.settings.ai.customModel.apiFormat=e,await this.plugin.saveSettings(),this.display()})}),new e.Setting(p).setName(v("Custom API Base URL")).setDesc(v(o?"Native Ollama endpoint. The root URL, /api, /api/chat, or /api/generate are all supported.":"OpenAI-compatible endpoint for your own provider.")).addText(e=>{e.setPlaceholder(o?"http://127.0.0.1:11434":"https://api.openai.com").setValue(this.plugin.settings.ai.customModel.baseUrl).onChange(async e=>{this.plugin.settings.ai.customModel.baseUrl=e.trim(),await this.plugin.saveSettings()})}),new e.Setting(p).setName(v("Custom Model Name")).setDesc(v("Model identifier used for inline completion and rewrite requests.")).addText(e=>{e.setPlaceholder(o?"qwen2.5:7b":"gpt-4o-mini").setValue(this.plugin.settings.ai.customModel.model).onChange(async e=>{this.plugin.settings.ai.customModel.model=e.trim(),await this.plugin.saveSettings()})}),o){const t=a?`${v("Choose a detected Ollama model to fill the model field.")} ${a}`.trim():v("Choose a detected Ollama model to fill the model field.");new e.Setting(p).setName(v("Detected Ollama Models")).setDesc(i.length>0?t:v("Fetch available models from your Ollama service.")).addDropdown(e=>{e.addOption("",v("Select a detected model")),i.forEach(t=>{e.addOption(t,t)});const t=this.plugin.settings.ai.customModel.model.trim();t&&!i.includes(t)&&e.addOption(t,t),e.setValue(i.includes(t)?t:""),e.onChange(async e=>{e&&(this.plugin.settings.ai.customModel.model=e,await this.plugin.saveSettings(),this.display())})}).addButton(e=>{e.setButtonText(v("Refresh")).onClick(async()=>{e.setDisabled(!0),e.setButtonText(v("Loading...")),await this.refreshCustomOllamaModels()})})}new e.Setting(p).setName(v("Custom API Key")).setDesc(r).addText(e=>{e.inputEl.type="password",e.setPlaceholder(this.plugin.aiManager.hasCustomModelApiKey()?v("Stored securely"):v(o?"Optional":"Enter API key")),e.setValue("").onChange(async e=>{e.trim()?this.plugin.aiManager.saveCustomModelApiKey(e):this.plugin.aiManager.clearCustomModelApiKey(),await this.plugin.saveSettings()})}).addButton(e=>{e.setButtonText(v("Clear")).onClick(async()=>{this.plugin.aiManager.clearCustomModelApiKey(),await this.plugin.saveSettings(),this.display()})}),new e.Setting(p).setName(v("Test Connection")).setDesc(v("Send a lightweight request to verify your custom model settings.")).addButton(e=>{e.setButtonText(v("Test Connection")).onClick(async()=>{e.setDisabled(!0),e.setButtonText(v("Testing..."));try{await this.plugin.aiManager.testCustomModelConnection()}finally{e.setDisabled(!1),e.setButtonText(v("Test Connection"))}})});const l=p.createEl("details",{cls:"editing-toolbar-ai-inline-disclosure"});l.createEl("summary",{cls:"editing-toolbar-ai-inline-summary",text:v("More Options")});const c=l.createDiv("editing-toolbar-ai-inline-body");new e.Setting(c).setName(v("Temperature")).setDesc(v("Lower values are more stable; higher values are more creative.")).addText(e=>{e.setValue(String(this.plugin.settings.ai.customModel.temperature)).onChange(async e=>{const t=Number.parseFloat(e);!Number.isNaN(t)&&t>=0&&(this.plugin.settings.ai.customModel.temperature=t,await this.plugin.saveSettings())})})}const h=d({title:v("Custom Prompt Templates"),desc:v("Manage quick-access templates for custom AI prompts"),collapsible:!0,open:!0}),g=h.createDiv({cls:"setting-item-description"});g.style.marginBottom="12px",g.style.padding="8px 12px",g.style.background="var(--background-secondary)",g.style.borderRadius="6px",g.style.fontSize="12px",g.innerHTML=`\n ${v("Available Variables")}:
\n {{selection}} - ${v("Selected text")} |\n {{file:path}} - ${v("Document path")} |\n {{file:content}} - ${v("Full document content")}
\n {{date}} - ${v("Date")} |\n {{time}} - ${v("Time")} |\n {{datetime}} - ${v("Date and time")} |\n {{vault:name}} - ${v("Vault name")}
\n ${v("Linked note references")}: ${v("Use [[note name]] to reference the content of other notes.")}\n `;(this.plugin.settings.ai.customPromptTemplates||[]).forEach((t,o)=>{new e.Setting(h).setName(t.name).setDesc(t.prompt.length>80?t.prompt.substring(0,80)+"...":t.prompt).addButton(e=>{e.setButtonText(v("Edit")).onClick(()=>{this.openTemplateEditor(t,o)})}).addButton(e=>{e.setButtonText(v("Delete")).setWarning().onClick(async()=>{this.plugin.settings.ai.customPromptTemplates.splice(o,1),await this.plugin.saveSettings(),this.display()})})}),new e.Setting(h).addButton(e=>{e.setButtonText(v("Add Template")).setCta().onClick(()=>{this.openTemplateEditor(null,-1)})})}}openTemplateEditor(t,o){const n=new e.Modal(this.app);n.titleEl.setText(v(t?"Edit Template":"Add Template"));const{contentEl:i}=n;let a=t?.name||"",s=t?.prompt||"";new e.Setting(i).setName(v("Template Name")).addText(e=>{e.setPlaceholder(v("Enter template name")).setValue(a).onChange(e=>{a=e}),e.inputEl.style.width="100%"}),new e.Setting(i).setName(v("Prompt Content")).addTextArea(e=>{e.setPlaceholder(v("Enter prompt content")).setValue(s).onChange(e=>{s=e}),e.inputEl.style.width="100%",e.inputEl.style.minHeight="120px"}),new e.Setting(i).addButton(e=>{e.setButtonText(v("Cancel")).onClick(()=>{n.close()})}).addButton(i=>{i.setButtonText(v("Save")).setCta().onClick(async()=>{if(!a.trim()||!s.trim())return void new e.Notice(v("Template name and content cannot be empty"));if(o<0&&this.plugin.settings.ai.customPromptTemplates.length>=9)return void new e.Notice(v("Maximum 10 templates allowed"));const i={id:t?.id||`template-${Date.now()}`,name:a.trim(),prompt:s.trim(),icon:t?.icon||"lucide-sparkles"};o>=0?this.plugin.settings.ai.customPromptTemplates[o]=i:this.plugin.settings.ai.customPromptTemplates.push(i),await this.plugin.saveSettings(),n.close(),this.display()})}),n.open()}displayImportExportSettings(t){const o=t.createDiv("import-export-container");o.style.padding="16px",o.style.borderRadius="8px",o.style.backgroundColor="var(--background-secondary)",o.style.marginBottom="20px",new e.Setting(o).setName(v("Export Configuration")).setDesc(v("Export your toolbar configuration to share with others.")).addButton(e=>e.setButtonText(v("Export")).setCta().onClick(()=>{new zo(this.app,this.plugin,"export").open()})),new e.Setting(o).setName(v("Import Configuration")).setDesc(v("Import toolbar configuration from JSON.")).addButton(e=>e.setButtonText(v("Import")).setCta().onClick(()=>{new zo(this.app,this.plugin,"import").open()}));const n=t.createDiv("import-export-info");n.style.marginTop="20px",n.style.padding="16px",n.style.borderRadius="8px",n.style.backgroundColor="var(--background-secondary)",n.createEl("h3",{text:v("Usage Instructions"),cls:"import-export-heading"}).style.marginTop="0";const i=n.createEl("ul");i.style.paddingLeft="20px",i.createEl("li",{text:v("Export: Generate a JSON configuration that you can save or share.")}),i.createEl("li",{text:v("Import: Paste a previously exported JSON configuration.")});const a=t.createDiv("community-share-container");a.style.marginTop="20px",a.style.padding="16px",a.style.borderRadius="8px",a.style.backgroundColor="rgba(var(--color-green-rgb), 0.1)",a.style.border="1px solid rgba(var(--color-green-rgb), 0.3)",a.createEl("h3",{text:v("Join the Community"),cls:"community-heading"}).style.marginTop="0";a.createEl("p").innerHTML=v("Share your toolbar settings and styles in our")+' Show and Tell ',a.createEl("p",{text:v("Get inspired by what others have created or showcase your own customizations.")});const s=t.createDiv("import-export-warning");s.style.marginTop="20px",s.style.padding="16px",s.style.borderRadius="8px",s.style.backgroundColor="rgba(var(--color-red-rgb), 0.1)",s.style.border="1px solid rgba(var(--color-red-rgb), 0.3)",s.createEl("p",{text:v("Warning: Importing configuration will overwrite your current settings. Consider exporting your current configuration first as a backup."),cls:"warning-text"}).style.margin="0"}applyAestheticStyle(e,t,o){Object.values(this.aestheticStyleMap).forEach(t=>{e.removeClass(t)});const n=this.aestheticStyleMap[t]||this.aestheticStyleMap.default;e.addClass(n);const i=this.aestheticStyleMap[o]||this.aestheticStyleMap.top;e.addClass(i)}getCommandsArrayByType(e){switch(e){case"following":return this.plugin.settings.followingCommands;case"top":return this.plugin.settings.topCommands;case"fixed":return this.plugin.settings.fixedCommands;case"mobile":return this.plugin.settings.mobileCommands;default:return this.plugin.settings.menuCommands}}}const en={editingToolbar:'',editingToolbarSub:'',editingToolbarAdd:'',editingToolbarDelete:'',editingToolbarReload:'',"codeblock-glyph":'',"underline-glyph":'',"superscript-glyph":'',"subscript-glyph":'',"bot-glyph":'',"header-1":'',"header-2":'',"header-3":'',"header-4":'',"header-5":'\n ',"header-6":'',"header-n":'',obsidian:'',"obsidian-new":''};class tn{constructor(e){this.plugin=e}init(){this.statusBarIcon=this.plugin.addStatusBarItem(),this.statusBarIcon.addClass("editingToolbar-statusbar-button"),e.setIcon(this.statusBarIcon,"editingToolbar"),this.registerClickEvent()}registerClickEvent(){this.plugin.registerDomEvent(this.statusBarIcon,"click",()=>{const e=this.statusBarIcon.parentElement.getBoundingClientRect(),t=this.statusBarIcon.getBoundingClientRect();this.showMenu(t,e)})}showMenu(t,o){const n=new e.Menu;n.addSections(["settings"]),this.addVisibilityToggle(n),this.addAestheticStyleToggle(n),n.addSections(["viewType"]),this.addViewTypeToggle(n),n.addSections(["controls"]),this.addToolbarControls(n);n.dom.addClass("editingToolbar-statusbar-menu"),n.showAtPosition({x:t.right+5,y:o.top-5})}addVisibilityToggle(t){t.addItem(t=>{t.setTitle(v("Hide & Show")),!e.requireApiVersion("0.15.0")||t.setSection("settings");const o=t.dom,n=new e.ToggleComponent(o).setValue(this.plugin.settings.cMenuVisibility).setDisabled(!0),i=async()=>{this.plugin.settings.cMenuVisibility=!this.plugin.settings.cMenuVisibility,n.setValue(this.plugin.settings.cMenuVisibility),1==this.plugin.settings.cMenuVisibility?setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100):p(this.plugin.settings.cMenuVisibility),se(this.plugin),await this.plugin.saveSettings()};t.onClick(e=>{e.preventDefault(),e.stopImmediatePropagation(),i()})}),t.addItem(t=>{t.setTitle(v("Toolbar Position")),!e.requireApiVersion("0.15.0")||t.setSection("settings"),t.setIcon("dock");const o=t.setSubmenu();o.addItem(t=>{t.setTitle(v("Top Toolbar"));const o=t.dom,n=new e.ToggleComponent(o).setValue(this.plugin.settings.enableTopToolbar||!1).setDisabled(!0);t.onClick(async e=>{e.preventDefault(),e.stopPropagation();const t=this.plugin.settings,o=this.plugin.positionStyle;t.enableTopToolbar=!t.enableTopToolbar,n.setValue(t.enableTopToolbar);let i=null;t.enableTopToolbar?i="top":"top"===o&&(i=t.enableFollowingToolbar?"following":t.enableFixedToolbar?"fixed":null),i&&i!==o&&this.plugin.onPositionStyleChange(i),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar()})}),o.addItem(t=>{t.setTitle(v("Following Toolbar"));const o=t.dom,n=new e.ToggleComponent(o).setValue(this.plugin.settings.enableFollowingToolbar||!1).setDisabled(!0);t.onClick(async e=>{e.preventDefault(),e.stopPropagation();const t=this.plugin.settings,o=this.plugin.positionStyle;t.enableFollowingToolbar=!t.enableFollowingToolbar,n.setValue(t.enableFollowingToolbar);let i=null;t.enableFollowingToolbar?i="following":"following"===o&&(i=t.enableTopToolbar?"top":t.enableFixedToolbar?"fixed":null),i&&i!==o&&this.plugin.onPositionStyleChange(i),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar()})}),o.addItem(t=>{t.setTitle(v("Fixed Toolbar"));const o=t.dom,n=new e.ToggleComponent(o).setValue(this.plugin.settings.enableFixedToolbar||!1).setDisabled(!0);t.onClick(async e=>{e.preventDefault(),e.stopPropagation();const t=this.plugin.settings,o=this.plugin.positionStyle;t.enableFixedToolbar=!t.enableFixedToolbar,n.setValue(t.enableFixedToolbar);let i=null;t.enableFixedToolbar?i="fixed":"fixed"===o&&(i=t.enableTopToolbar?"top":t.enableFollowingToolbar?"following":null),i&&i!==o&&this.plugin.onPositionStyleChange(i),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar()})})})}addViewTypeToggle(t){const o=this.plugin.app.workspace.getActiveViewOfType(e.ItemView);if(!o)return;const n=o.getViewType();t.addItem(t=>{t.setTitle(v("Current View: ")+n),!e.requireApiVersion("0.15.0")||t.setSection("settings"),t.setIcon("layout-template");const i=t.setSubmenu(),a=X.isAllowedViewType(o);i.addItem(e=>{e.setTitle(v(a?"Disable toolbar for this view":"Enable toolbar for this view")),e.setIcon(a?"eye-off":"eye"),e.onClick(async()=>{this.plugin.settings.viewTypeSettings||(this.plugin.settings.viewTypeSettings={}),this.plugin.settings.viewTypeSettings[n]=!a,await this.plugin.saveSettings(),se(this.plugin),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)})}),i.addItem(e=>{e.setTitle(v("Manage all view types")),e.setIcon("settings-2");const t=e.setSubmenu(),o=new Set(["markdown","canvas","thino_view","meld-encrypted-view",...Object.keys(this.plugin.settings.viewTypeSettings||{})]);Array.from(o).sort().forEach(e=>{const o=this.isViewTypeAllowed(e);t.addItem(t=>{t.setTitle(e),t.setIcon(o?"check":""),t.onClick(async()=>{this.plugin.settings.viewTypeSettings||(this.plugin.settings.viewTypeSettings={}),this.plugin.settings.viewTypeSettings[e]=!o,n===e&&(se(this.plugin),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)),await this.plugin.saveSettings()})})})})})}isViewTypeAllowed(e){if(!this.plugin.settings.viewTypeSettings||void 0===this.plugin.settings.viewTypeSettings[e]){return["markdown","canvas","thino_view","meld-encrypted-view"].includes(e)}return this.plugin.settings.viewTypeSettings[e]}addToolbarControls(t){const o=[{icon:"plus",title:v("Add Command"),click:()=>new k(this.plugin).open()}];"fixed"===this.plugin.positionStyle&&o.push({icon:"file-sliders",title:v("Position Settings"),click:()=>new T(this.plugin.app,this.plugin).open()}),o.forEach(o=>{t.addItem(t=>{t.setIcon(o.icon),t.setTitle(o.title),t.onClick(o.click),e.requireApiVersion("0.15.0")&&t.setSection("controls")})})}addAestheticStyleToggle(t){t.addItem(t=>{t.setTitle(v("Appearance Style")),!e.requireApiVersion("0.15.0")||t.setSection("settings"),t.setIcon("cherry");const o=t.setSubmenu();G.forEach(e=>{o.addItem(t=>{t.setTitle(e),t.setIcon(this.plugin.settings.aestheticStyle===e?"check":""),t.onClick(async()=>{this.plugin.settings.aestheticStyle=e,await this.plugin.saveSettings(),se(this.plugin),setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100)})})})})}}let on;class nn extends e.Modal{constructor(e){super(e.app),this.plugin=e,this.type="note",this.title="",this.content="",this.collapse="none",this.allCalloutOptions=[],this.builtInCalloutTypes=[{type:"note",aliases:[],icon:"lucide-pencil",label:"Note",color:"var(--callout-default)"},{type:"abstract",aliases:["summary","tldr"],icon:"lucide-clipboard-list",label:"Abstract",color:"var(--callout-summary)"},{type:"info",aliases:[],icon:"lucide-info",label:"Info",color:"var(--callout-info)"},{type:"todo",aliases:[],icon:"lucide-check-circle-2",label:"Todo",color:"var(--callout-todo)"},{type:"important",aliases:[],icon:"lucide-flame",label:"Important",color:"var(--callout-important)"},{type:"tip",aliases:["hint"],icon:"lucide-flame",label:"Tip",color:"var(--callout-tip)"},{type:"success",aliases:["check","done"],icon:"lucide-check",label:"Success",color:"var(--callout-success)"},{type:"question",aliases:["help","faq"],icon:"lucide-help-circle",label:"Question",color:"var(--callout-question)"},{type:"warning",aliases:["caution","attention"],icon:"lucide-alert-triangle",label:"Warning",color:"var(--callout-warning)"},{type:"failure",aliases:["fail","missing"],icon:"lucide-x",label:"Failure",color:"var(--callout-fail)"},{type:"danger",aliases:["error"],icon:"lucide-zap",label:"Danger",color:"var(--callout-error)"},{type:"bug",aliases:[],icon:"lucide-bug",label:"Bug",color:"var(--callout-bug)"},{type:"example",aliases:[],icon:"lucide-list",label:"Example",color:"var(--callout-example)"},{type:"quote",aliases:["cite"],icon:"lucide-quote",label:"Quote",color:"var(--callout-quote)"}],this.containerEl.addClass("insert-callout-modal"),this.prepareCalloutOptions();const t=this.plugin.commandsManager.getActiveEditor();if(t){const e=t.getSelection();e&&(this.content=e)}this.allCalloutOptions.find(e=>e.type===this.type)||(this.type=this.allCalloutOptions.length>0?this.allCalloutOptions[0].type:"note")}prepareCalloutOptions(){if(this.builtInCalloutTypes.forEach(e=>{this.allCalloutOptions.push({type:e.type,label:e.label,icon:e.icon,color:e.color,isAdmonition:!1}),e.aliases.forEach(t=>{this.allCalloutOptions.push({type:t,label:`${e.label} (${t})`,icon:e.icon,color:e.color,isAdmonition:!1})})}),this.plugin.admonitionDefinitions){const e=Object.values(this.plugin.admonitionDefinitions);e.length>0&&e.forEach(e=>{this.allCalloutOptions.some(t=>t.type===e.type)||this.allCalloutOptions.push({type:e.type,label:e.title||e.type.charAt(0).toUpperCase()+e.type.slice(1),icon:e.icon,color:`rgb(${e.color})`,isAdmonition:!0,sourcePlugin:"Admonition"})})}}onOpen(){this.display()}async display(){const{contentEl:t}=this;t.empty(),t.addEventListener("keydown",e=>{"Enter"===e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.insertButton&&this.insertButton.click())});const o=t.createDiv("callout-type-container");this.iconContainerEl=o.createDiv("callout-icon-container"),new e.Setting(o).setName(v("Callout Type")).addDropdown(e=>{const t=this.allCalloutOptions.filter(e=>!e.isAdmonition),o=this.allCalloutOptions.filter(e=>e.isAdmonition);if(t.length>0&&o.length>0){e.addOption("---separator---","---- Admonitions ----");const t=e.selectEl.options[e.selectEl.options.length-1];t&&(t.disabled=!0)}o.forEach(t=>{e.addOption(t.type,`${t.label} (Admonition)`)}),e.addOption("---separator---","---- Default ----"),t.forEach(t=>{e.addOption(t.type,t.label)}),this.allCalloutOptions.some(e=>e.type===this.type)||(this.type=this.allCalloutOptions.length>0?this.allCalloutOptions[0].type:"note"),e.setValue(this.type),e.onChange(t=>{"---separator---"!==t?(this.type=t,this.updateIconAndColor(this.iconContainerEl,t)):e.setValue(this.type)})}),this.updateIconAndColor(this.iconContainerEl,this.type),new e.Setting(t).setName(v("Title")).setDesc(v("Optional, leave blank for default title")).addText(e=>{e.setPlaceholder(v("Input title")).setValue(this.title).onChange(e=>{this.title=e})}),new e.Setting(t).setName(v("Collapse State")).addDropdown(e=>{e.addOption("none",v("Default")).addOption("open",v("Open")).addOption("closed",v("Closed")).setValue(this.collapse).onChange(e=>{this.collapse=e})}),new e.Setting(t).setName(v("Content")).addTextArea(e=>{e.setPlaceholder(v("Input content")).setValue(this.content).onChange(e=>{this.content=e}),e.inputEl.rows=5,e.inputEl.cols=40,this.contentTextArea=e.inputEl});const n=t.createDiv("shortcut-hint");n.setText(`${e.Platform.isMacOS?"⌘":"Ctrl"} + Enter ${v("to insert")}`),n.style.textAlign="right",n.style.fontSize="0.8em",n.style.opacity="0.7",n.style.marginTop="5px",new e.Setting(t).addButton(e=>(e.setButtonText(v("Insert")).setCta().onClick(()=>{this.insertCallout(),this.close()}),this.insertButton=e.buttonEl,e)).addButton(e=>(e.setButtonText(v("Cancel")).setTooltip(v("Cancel")).onClick(()=>this.close()),e)),setTimeout(()=>{this.contentTextArea&&this.contentTextArea.focus()},10)}updateIconAndColor(t,o){if(!t)return;const n=this.allCalloutOptions.find(e=>e.type===o);if(t.empty(),n)if(n.isAdmonition){const o=n.icon;if("custom"===o.type&&o.svg){t.innerHTML=o.svg;const e=t.querySelector("svg");e&&(e.style.fill=n.color,e.style.width="var(--icon-size)",e.style.height="var(--icon-size)")}else o.name.startsWith("lucide-")||"default"===o.type?(e.setIcon(t,o.name),t.style.setProperty("--callout-color",n.color)):(e.setIcon(t,"lucide-box"),t.style.setProperty("--callout-color",n.color))}else e.setIcon(t,n.icon),t.style.setProperty("--callout-color",n.color);else e.setIcon(t,"lucide-alert-circle"),t.style.removeProperty("--callout-color")}insertCallout(){const e=this.plugin.commandsManager.getActiveEditor();if(!e)return;let t=`> [!${this.type}]`;"none"!==this.collapse&&(t+=""+("open"===this.collapse?"+":"-")),this.title&&(t+=` ${this.title}`),t+=`\n> ${this.content.replace(/\n/g,"\n> ")}`;const o=e.getCursor(),n=e.getLine(o.line),i=0===o.ch;let a;if(e.getSelection()){!i&&n.trim().length>0&&(t="\n"+t);const o=e.getCursor("from");e.replaceSelection(t);const s=t.split("\n").length;a={line:o.line+s,ch:0}}else{!i&&n.trim().length>0&&(t="\n"+t),e.replaceRange(t,o);const s=t.split("\n").length;a={line:o.line+s,ch:0}}setTimeout(()=>{e.replaceRange("\n",a),e.setCursor({line:a.line+1,ch:0}),e.focus()},0)}}class an{static isValidUrl(e){try{return new URL(e),!0}catch(e){return!1}}static parseTitle(e,t){const o=[e.includes("mp.weixin.qq.com")?this.wxTitlePattern:null,this.htmlTitlePattern,/]*>(.*?)<\/title>/i,//im].filter(Boolean);for(const e of o){const o=t.match(e);if(o&&"string"==typeof o[1])return o[1].trim()}throw new Error("Unable to parse the title tag")}static getFallbackTitle(e){const t=e.match(/[^/\\]+$/);return t?t[0].replace(/\.[^/.]+$/,"").replace(/[-_]/g," ").trim():e}static async fetchRemoteTitle(t){if(!this.isValidUrl(t)||!t.match(/^https?:\/\//))return this.getFallbackTitle(t);try{const o=await e.requestUrl({url:t,method:"GET",headers:{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"},throw:!0});if(200!==o.status)throw new Error(`Status code ${o.status}`);const n=o.text,i=this.parseTitle(t,n);return!i||i.length>100?this.getFallbackTitle(t):i}catch(e){return console.error(`Failed to fetch title for ${t}:`,e),this.getFallbackTitle(t)}}}an.htmlTitlePattern=/([^<]*)<\/title>/im,an.wxTitlePattern=//im;class sn extends e.Modal{constructor(e){super(e.app),this.plugin=e,this.linkText="",this.linkUrl="",this.linkAlias="",this.isEmbed=!1,this.insertNewLine=!1,this.imageWidth="",this.imageHeight="",this.prefixText="",this.suffixText="",this.selectedText="";const t=this.plugin.commandsManager.getActiveEditor();if(t){const e=t.getSelection()||"";e?this.handleSelectedText(t,e):this.handleCursorPosition(t)}else this.parseClipboard();this.updateHeader()}handleSelectedText(e,t){const o=this.tryExpandSelection(e,t);if(o){const t=this.formatTargetText(o);e.setSelection(o.from,o.to),this.selectedText=t,this.parseSelectedText(t)}else this.selectedText=t,this.parseSelectedText(t)}handleCursorPosition(e){const t=e.getCursor(),o=this.findLinkAtCursor(e,t);if(o){const t=this.formatTargetText(o);e.setSelection(o.from,o.to),this.selectedText=t,this.parseSelectedText(t)}else this.parseClipboard()}tryExpandSelection(e,t){const o=e.getCursor("from"),n=e.getLine(o.line),i=o.ch,a=e.getCursor("to").ch;return this.matchLinkInLine(n,i,a,o.line)}findLinkAtCursor(e,t){const o=e.getLine(t.line),n=t.ch;return this.matchLinkInLine(o,n,n,t.line)}matchLinkInLine(e,t,o,n){const i=/(!)?\[([^\]]+)\]\(([^\s)]+)(?:\s+["']([^"']*)["'])?\)/g;let a;for(;null!==(a=i.exec(e));){const e=!!a[1],i=a.index,s=a.index+a[0].length,r=a[2],l=a[3],c=a[4]||"";if(t<=s&&o>=i)return{isImage:e,text:r,url:l,title:c,from:{line:n,ch:i},to:{line:n,ch:s}}}const s=/(?:^|\s)([a-zA-Z][a-zA-Z\d+\-.]*:\/\/\S+|\S+\.[a-zA-Z]{2,}(?:\/\S*)?)/g;for(;null!==(a=s.exec(e));){const e=a[1],i=a.index+(a[0].startsWith(" ")?1:0),s=i+e.length;if(t<=s&&o>=i)return{isImage:/\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(e),text:e,url:e,title:"",from:{line:n,ch:i},to:{line:n,ch:s}}}return null}formatTargetText(e){return e.isImage?``:e.title?`[${e.text}](${e.url} "${e.title}")`:`[${e.text}](${e.url})`}parseSelectedText(e){const t=e.match(/!\[.*?\]\(.*?\)/);if(t){const o=e.substring(0,t.index),n=e.substring(t.index+t[0].length),i=this.parseMarkdownImageLink(e);if(i)return this.linkText=i.title,this.linkUrl=i.url,this.linkAlias=i.quotedTitle||"",this.imageWidth=i.width||"",this.imageHeight=i.height||"",this.isEmbed=!0,this.prefixText=o,void(this.suffixText=n)}const o=e.match(/\[([^\]]+)\]\(([a-zA-Z]+:\/\/[^\s)]+)(?:\s+["']([^"']*)["'])?\)/);if(o){const t=o[0],n=e.substring(0,o.index),i=e.substring(o.index+t.length),a=this.parseMarkdownLink(t);a&&(this.linkText=a.text,this.linkUrl=a.url,this.linkAlias=a.title||"",this.isEmbed=!1),this.prefixText=n,this.suffixText=i}else{const t=this.parseMixedContent(e);t&&(this.linkText=t.title,this.linkUrl=t.url)}}parseMixedContent(e){let t;return(t=e.match(/^\[(.*?)\]\((.*?)\)$/))?{title:t[1].trim(),url:t[2].trim()}:(t=e.match(/]+href=["']([^"']+)["'][^>]*>([^<]+)<\/a>/i))?{title:t[2].trim(),url:t[1].trim()}:(t=e.match(/^(.*?)\s*((?:https?:\/\/|www\.)\S+)$/i))&&t[1].trim()?{title:t[1].trim(),url:t[2].trim()}:this.isValidUrl(e.trim())?{title:this.extractTitleFromUrl(e.trim()),url:e.trim()}:{title:e.trim(),url:""}}extractTitleFromUrl(e){const t=e.match(/^([a-zA-Z]+):\/\/(.+)$/);if(t){const[,e,o]=t,n=o.split(/[/\\]/),i=n[n.length-1];return i?decodeURIComponent(i).trim():e.toUpperCase()}const o=e.match(/^\[\[(.*?)\]\]$/);if(o)return o[1];const n=e.match(/[^/\\]+$/);return n?n[0].replace(/\.[^/.]+$/,"").replace(/[-_]/g," ").trim():e}isValidUrl(e){if(!e||e.includes("\n")||/\s/.test(e))return!1;if(["obsidian://","zotero://","evernote://","notion://","bear://","things://","drafts://","x-devonthink-item://","file://","ftp://","ftps://","http://","https://","tel:","mailto:"].some(t=>e.startsWith(t)))return!0;if(e.match(/^\[\[.*?\]\]$/))return!0;if(e.match(/^[./\\]/)||e.match(/^[a-zA-Z]:\\/)||e.match(/^\/[^/]/)||e.match(/^[a-zA-Z]+:\/\//))return!0;try{return new URL(e),!0}catch(e){return!1}}parseMarkdownImageLink(e){const t=e.match(/!\[(.*?)(?:\|(\d+)(?:x(\d+))?)?\]\(\s*([^\s)]+)(?:\s+["']([^"']*)["'])?\s*\)/);if(t){const[,e,o,n,i,a]=t;if(this.isEmbed=!0,this.embedToggle){this.embedToggle.setValue(!0);const e=this.contentEl.querySelector(".image-size-setting");e&&(e.style.display="block")}return{title:e.trim(),url:i.trim(),width:o,height:n,quotedTitle:a?.trim()}}return null}parseMarkdownLink(e){const t=e.match(/\[([^\]]+)\]\(([a-zA-Z]+:\/\/[^\s)]+)(?:\s+["']([^"']*)["'])?\)/);if(t){const[,e,o,n]=t;return{text:e.trim(),url:o.trim(),title:n?.trim()}}return null}async parseClipboard(){try{const e=await this.readClipboard(),t=e["text/plain"];if(t){const e=this.parseMarkdownImageLink(t);if(e)return this.linkText=e.title,this.linkUrl=e.url,this.linkAlias=e.quotedTitle||"",(e.width||e.height)&&(this.isEmbed=!0,this.imageWidth=e.width||"",this.imageHeight=e.height||""),void this.updateUI();const o=this.parseMarkdownLink(t);if(o)return this.linkText=o.text,this.linkUrl=o.url,this.linkAlias=o.title||"",this.isEmbed=!1,void this.updateUI()}if(e["text/html"]){const t=this.parseHtmlContent(e["text/html"]);t&&(this.linkText=this.linkText||t.title,this.linkUrl=t.url)}else if(e["text/markdown"]){const t=this.parseMarkdownContent(e["text/markdown"]);t&&(this.linkText=this.linkText||t.title,this.linkUrl=t.url)}else if(e["text/plain"]){const t=this.parseMixedContent(e["text/plain"]);t&&(this.linkText=this.linkText||t.title,this.linkUrl=t.url)}this.updateUI()}catch(e){console.error("Failed to read clipboard:",e)}}async readClipboard(){const e={};try{const t=await navigator.clipboard.read();for(const o of t){const t=o.types;for(const n of t)if("text/html"===n||"text/plain"===n||"text/markdown"===n){const t=await o.getType(n);e[n]=await t.text()}}}catch(t){try{const t=await navigator.clipboard.readText();e["text/plain"]=t}catch(e){console.error("Failed to read clipboard:",e)}}return e}parseHtmlContent(e){const t=(new DOMParser).parseFromString(e,"text/html"),o=t.querySelector("a");if(o)return{title:o.textContent?.trim()||"",url:o.href};const n=t.body.textContent||"";return this.parseMixedContent(n)}parseMarkdownContent(e){const t=e.match(/\[([^\]]+)\]\(([^)]+)\)/);return t?{title:t[1].trim(),url:t[2].trim()}:this.parseMixedContent(e)}onOpen(){this.display()}updateHeader(){const e=this.getPreviewText();this.previewSetting&&(this.previewSetting.controlEl.querySelector("input").value=e)}getPreviewText(){const e=this.linkText||"",t=this.linkUrl;let o=this.isEmbed?"!":"";return o+=`[${e}`,this.isEmbed&&(this.imageWidth||this.imageHeight)&&(o+="|",this.imageWidth&&this.imageHeight?o+=`${this.imageWidth}x${this.imageHeight}`:this.imageWidth?o+=this.imageWidth:this.imageHeight&&(o+=`x${this.imageHeight}`)),o+=`](${t}`,this.linkAlias&&(o+=` "${this.linkAlias}"`),o+=")",o}async display(){const{contentEl:t}=this;t.empty(),t.addClass("insert-link-modal"),this.titleEl.textContent="",this.titleEl.addClass("insert-link-modal-title"),t.addEventListener("keydown",e=>{"Enter"===e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.insertButton&&this.insertButton.click())});new e.Setting(t).addButton(t=>{t.setIcon("lucide-globe").setTooltip(v("Fetch Remote Title")).onClick(async()=>{if(this.linkUrl){t.setDisabled(!0),t.setIcon("lucide-loader");const e=await this.fetchRemoteTitle(this.linkUrl);t.setIcon("lucide-globe"),t.setDisabled(!1),this.linkText=e,this.linkTextInput.setValue(e),this.updateHeader()}else new e.Notice(v("Please enter a URL first"))})}).setName(v("Link Text")).addText(e=>{this.linkTextInput=e,e.setPlaceholder(v("Link Text")).setValue(this.linkText).onChange(e=>{this.linkText=e,this.updateHeader()})});const o=new e.Setting(t).setName(v("Title")).addText(e=>{this.linkAliasInput=e,e.setPlaceholder(v("Link Title (optional)")).setValue(this.linkAlias).onChange(e=>{this.linkAlias=e,this.updateHeader()})}),n=new e.Setting(t).setName(v("Link URL")).setClass("link-url-setting").addText(e=>{this.linkUrlInput=e,e.setPlaceholder(v("Link URL")).setValue(this.linkUrl).onChange(e=>{this.linkUrl=e.trim(),this.validateUrl(this.linkUrl),this.updateHeader()})}).addButton(e=>{e.setIcon("lucide-clipboard").setTooltip(v("Paste and Parse")).onClick(async()=>{await this.parseClipboard(),this.updateHeader()})});this.urlErrorMsg=n.descEl.createDiv("url-error"),this.urlErrorMsg.style.color="var(--text-error)",this.urlErrorMsg.style.display="none";const i=new e.Setting(t).setName(v("Embed Content")).setDesc(v("If it is an image, turn on"));this.embedToggle=new e.ToggleComponent(i.controlEl),this.embedToggle.setValue(this.isEmbed).onChange(e=>{this.isEmbed=e;const n=t.querySelector(".image-size-setting");o.settingEl,n&&(n.style.display=e?"flex":"none"),this.updateHeader()});const a=new e.Setting(t).addButton(e=>{e.setIcon("lucide-maximize").setTooltip(v("Fit Editor Width")).onClick(()=>{const e=this.getImageDimensions();e&&(this.imageWidth=e.width.toString(),this.imageHeight=e.height?.toString(),a.components[1].setValue(this.imageWidth),this.imageHeight&&a.components[2].setValue(this.imageHeight),this.updateHeader())})});a.setClass("image-size-setting").setName(v("Image Size")).addText(e=>{e.inputEl.addClass("image-width-input"),e.setPlaceholder(v("Image Width")).setValue(this.imageWidth).onChange(t=>{this.imageWidth=t.replace(/[^\d]/g,""),e.setValue(this.imageWidth),this.updateHeader()})});const s=a.controlEl.createDiv("image-size-icon");e.setIcon(s,"lucide-x"),a.addText(e=>{e.inputEl.addClass("image-height-input"),e.setPlaceholder(v("Image Height")).setValue(this.imageHeight).onChange(t=>{this.imageHeight=t.replace(/[^\d]/g,""),e.setValue(this.imageHeight),this.updateHeader()})}),a.settingEl.style.display=this.isEmbed?"block":"none",new e.Setting(t).setName(v("Insert New Line")).setDesc(v("Insert a link on the next line")).addToggle(e=>{e.setValue(this.insertNewLine).onChange(e=>{this.insertNewLine=e,this.updateHeader()})}),this.previewSetting=new e.Setting(t).setClass("preview-setting").setTooltip(this.getPreviewText()).addText(e=>{e.setValue(this.getPreviewText()).inputEl.setAttribute("readonly","true")});const r=t.createDiv("shortcut-hint");r.setText(`${e.Platform.isMacOS?"⌘":"Ctrl"} + Enter ${v("to insert")}`),r.style.textAlign="right",r.style.fontSize="0.8em",r.style.opacity="0.7",r.style.marginTop="5px",new e.Setting(t).addButton(e=>{e.setButtonText(v("Insert")).setCta().onClick(()=>{this.insertLink(),this.close()}),this.insertButton=e.buttonEl}).addButton(e=>e.setButtonText(v("Cancel")).onClick(()=>{this.close()})),setTimeout(()=>{this.linkText||this.linkUrl?!this.linkText&&this.linkUrl?this.linkTextInput.inputEl.focus():this.linkText&&!this.linkUrl?this.linkUrlInput.inputEl.focus():this.linkAliasInput.inputEl.focus():this.linkTextInput.inputEl.focus()},10)}async fetchRemoteTitle(e){return an.fetchRemoteTitle(e)}getImageDimensions(){const t=this.app.workspace.getActiveViewOfType(e.MarkdownView);if(!t)return null;const o=t.contentEl.querySelector(".markdown-source-view .cm-content"),n=t.contentEl;if(!o||!n)return null;const i=o.offsetWidth,a=n.offsetHeight,s=Math.floor(a/2);if("preview"===t.getMode()||"source"===t.getMode()){const e=o.querySelectorAll("img");if(e.length>0){let t=null;if(this.linkUrl&&e.forEach(e=>{e.src===this.linkUrl&&e.complete&&e.naturalWidth>0&&(t=e)}),t){const e=t.naturalWidth,o=e/t.naturalHeight;let n=Math.min(e,Math.floor(.65*i)),a=Math.floor(n/o);return a>s&&(a=s,n=Math.floor(a*o)),{width:n,height:a}}}}const r=Math.floor(s*(4/3));return{width:Math.min(Math.floor(.65*i),r),height:null}}validateUrl(e){return e?this.isValidUrl(e)?(this.urlErrorMsg.style.display="none",!0):(this.urlErrorMsg.textContent=v("URL Format Error"),this.urlErrorMsg.style.display="block",!1):(this.urlErrorMsg.style.display="none",!0)}insertLink(){if(!this.validateUrl(this.linkUrl))return;const e=this.plugin.commandsManager.getActiveEditor();if(!e)return;let t=this.linkText||this.linkUrl;const o=this.linkUrl;let n,i=this.isEmbed?"!":"";i+=`[${t}`,this.isEmbed&&(this.imageWidth||this.imageHeight)&&(i+="|",this.imageWidth&&this.imageHeight?i+=`${this.imageWidth}x${this.imageHeight}`:this.imageWidth?i+=this.imageWidth:this.imageHeight&&(i+=`x${this.imageHeight}`)),i+=`](${o}`,this.linkAlias&&(i+=` "${this.linkAlias}"`),i+=")";if(e.somethingSelected()){const t=e.getCursor("from"),o=e.getCursor("to");if(this.insertNewLine)e.replaceRange("\n"+i,{line:o.line,ch:e.getLine(o.line).length}),n={line:o.line+1,ch:i.length};else{const a=this.prefixText+i+this.suffixText;e.replaceRange(a,{line:t.line,ch:t.ch},o),n={line:t.line,ch:this.prefixText.length+i.length}}}else{const t=e.getCursor(),o=e.getLine(t.line);if(this.insertNewLine){const a=t.line+1;e.replaceRange("\n",{line:t.line,ch:o.length}),e.setCursor({line:a,ch:0}),e.replaceRange(i,{line:a,ch:0}),n={line:a,ch:i.length}}else e.replaceRange(i,t),n={line:t.line,ch:t.ch+i.length}}setTimeout(()=>{n&&e.setCursor(n),e.focus()},0)}updateUI(){this.linkTextInput&&this.linkTextInput.setValue(this.linkText),this.linkUrlInput&&(this.linkUrlInput.setValue(this.linkUrl),this.validateUrl(this.linkUrl));const e=this.contentEl.querySelector(".image-width-input"),t=this.contentEl.querySelector(".image-height-input");e&&(e.value=this.imageWidth),t&&(t.value=this.imageHeight),this.linkAliasInput&&this.linkAliasInput.setValue(this.linkAlias);const o=this.contentEl.querySelector(".setting-item:nth-child(2)");o&&(o.style.display="flex"),this.updateHeader()}}class rn{static getPlainText(t){const o=t.getSelection();if(!o)return void new e.Notice(v("Please select text first"));let n=o.replace(/\[([^\[\]]*)\]\([^\(\)]+\)/gim,"$1").replace(/(^#+\s|(?<=^|\s*)#|^>|^\- \[( |x)\]|^\+ |<[^<>]+>|^1\. |^\-+$|^\*+$|==|\*+|~~|```|!*\[\[|\]\])/gm,"").replace(/^[ ]+|[ ]+$/gm,"").replace(/(\r\n|\n)+/gm,"\n");navigator.clipboard.writeText(n),new e.Notice(v("Plain text copied to clipboard"))}static insertBlankLines(e){const t=e.getValue();if(!t)return;const o=t.replace(/([^\n])\n([^\n])/g,"$1\n\n$2");e.setValue(o)}static processWhitespace(t,o={}){const n=t.getSelection();if(!n)return void new e.Notice(v("Please select text first"));let i=n;o.all?i=i.replace(/[ \u3000\t]+/g,""):(o.tabs&&(i=i.replace(/\t/g,"")),o.compress&&(i=i.replace(/[ \u3000]+/g," ")));let a=i.split(/\r?\n/);if(o.trim&&(a=a.map(e=>e.trim())),o.removeEmptyLines)a=a.filter(e=>e.length>0);else if(o.compactEmptyLines){const e=[];for(let t=0;t0&&0===a[t-1].length||e.push(a[t]);a=e}i=a.join("\n"),o.removeEmptyLines&&(i=i.trim()),t.replaceSelection(i),new e.Notice(v("Whitespace cleaning completed"))}static splitLines(t){const o=t.getSelection();if(!o)return void new e.Notice(v("Please select text first"));const n=this.detectPattern(o);let i;if(n)i=o.split(n).map(e=>e.trim()).filter(e=>e.length>0),new e.Notice(v("List pattern detected, auto-split"));else{const t=this.detectSeparator(o);if(!t)return void new e.Notice(v("No obvious separator or list pattern detected"));i=this.smartSplit(o,t),new e.Notice(`${v("Merged with")} '${t}' ${v("Merge completed")}`)}t.replaceSelection(i.join("\n"))}static detectPattern(e){const t=/\s?\d+[\.、]\s?/g,o=/\s?[→=>]\s?/g;return(e.match(t)||[]).length>1?t:(e.match(o)||[]).length>1?o:null}static smartSplit(e,t){const o=[];let n="",i=null;const a={'"':'"',"'":"'","“":"”","‘":"’","(":")","(":")","《":"》","[":"]","[[":"]]"};for(let s=0;se.length>0)}static detectSeparator(e){let t=0,o=null;return["、",",",",",";",";","|","·"].forEach(n=>{const i=e.split(n).length-1;i>t&&(t=i,o=n)}),o}static async smartPaste(t){try{const e=await navigator.clipboard.readText();if(!e)return;let o=e.replace(/(\r\n|\n){3,}/g,"\n\n");o=o.replace(/[ \t]+$/gm,""),t.replaceSelection(o)}catch(t){new e.Notice(v("Paste failed"))}}static smartTypography(t){const o=t.getSelection();if(!o||0===o.trim().length)return void new e.Notice(v("Please select text first"));const n=(o.match(/[\u4e00-\u9fa5]/g)||[]).length/o.length>.1;let i=o;const a=[];[/`[^`]+`/g,/\$[^$]+\$/g,/https?:\/\/[^\s)]+/g,/!\[.*?\]\(.*?\)/g,/\[.*?\]\(.*?\)/g].forEach(e=>{i=i.replace(e,e=>(a.push(e),`__PROTECTED_${a.length-1}__`))}),n?(i=i.replace(/,/g,",").replace(/\.(?!\d)/g,"。").replace(/;/g,";").replace(/:/g,":").replace(/\?/g,"?").replace(/!/g,"!").replace(/\(/g,"(").replace(/\)/g,")").replace(/"([^"]*)"/g,"“$1”").replace(/([\u4e00-\u9fa5])([a-zA-Z0-9])/g,"$1 $2").replace(/([a-zA-Z0-9])([\u4e00-\u9fa5])/g,"$1 $2"),new e.Notice(v("Detected Chinese context: converted to full-width symbols"))):(i=i.replace(/,/g,", ").replace(/。/g,". ").replace(/;/g,"; ").replace(/:/g,": ").replace(/?/g,"? ").replace(/!/g,"! ").replace(/(/g,"(").replace(/)/g,")").replace(/[""]/g,'"').replace(/ {2,}/g," "),new e.Notice(v("Detected code/English context: converted to half-width symbols"))),a.forEach((e,t)=>{i=i.replace(`__PROTECTED_${t}__`,e)}),t.replaceSelection(i)}static dedupe(t,o={}){const n=t.getSelection();if(!n)return void new e.Notice(v("Please select text to dedupe first"));const i=n.split(/\r?\n/),a=new Set,s=[];for(const e of i){const t=o.trimBeforeCompare?e.trim():e;""!==t?a.has(t)||(a.add(t),s.push(e)):o.includeEmpty?a.has("")||(a.add(""),s.push(e)):s.push(e)}o.sort&&s.sort((e,t)=>e.localeCompare(t,"zh-CN",{numeric:!0})),t.replaceSelection(s.join("\n")),new e.Notice(`${v("Deduplication completed, remaining")} ${s.length} ${v("lines")}`)}static addWrap(t,o="",n="",i=!0){const a=t.getSelection(),s=a&&""!==a.trim(),r=s?a:t.getValue();if(!r)return;const l=r.split("\n").map(e=>i&&0===e.trim().length?e:`${o}${e}${n}`).join("\n");s?t.replaceSelection(l):t.setValue(l),new e.Notice(v("Prefix/suffix added"))}static numberList(t,o=1,n=1,i=". ",a=""){const s=t.getSelection();if(!s)return void new e.Notice(v("Please select text to number first"));const r=s.split("\n");let l=o;const c=r.map(e=>{if(""===e.trim())return e;const t=e.replace(/^\s*\d+[\.\))、]?\s*/,""),o=`${a}${l}${i}${t}`;return l+=n,o}).join("\n");t.replaceSelection(c),new e.Notice(`${v("Numbering completed: starting from")} ${o}`)}static extractBetween(t,o,n){if(!o&&!n)return void new e.Notice(v("Please specify start or end string"));const i=t.getValue();if(i)try{const a=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),s=a(o),r=a(n),l=new RegExp(`${s}(.*?)${r}`,"g"),c=[];let d;for(;null!==(d=l.exec(i));)void 0!==d[1]&&c.push(d[1]);c.length>0?(t.setValue(c.join("\n")),new e.Notice(`${v("Extracted")} ${c.length} ${v("matches")}`)):new e.Notice(v("No matches found"))}catch(t){new e.Notice(v("Extraction failed"))}}static mergeLines(t,o){const n=t.getSelection();if(!n||""===n.trim())return void new e.Notice(v("Please select lines to merge first"));const i=n.split(/\r?\n/),a=""!==o.separator;let s="";for(let e=0;ee.match(n)).filter(e=>e&&e[2].length>0).map(e=>e[2].replace(/\t/g," ").length),s=a.length>0?Math.min(...a):4;o.forEach(e=>{const t=e.match(n);if(t){const e=Math.round(t[2].replace(/\t/g," ".repeat(s)).length/s);e>i&&(i=e)}});let r=[],l=[],c=[],d=!1;for(let e=0;e0&&l.push([...c]),c=[e,""]):c[1]=c[1]?c[1]+"
"+e:e:(void 0!==c[t]&&(l.push([...c]),c=c.slice(0,t)),c[t]=e)}else if(d){if(""!==t.trim()){const e=c.length-1;e>=0&&(c[e]+="
"+t.trim())}}else r.push(t)}c.length>0&&l.push(c);const u=1===i?l:this.applyVisualMerge(l);this.renderFinalResult(e,r,u,[],i+1)}static applyVisualMerge(e){let t=[];return e.map((e,o)=>{if(0===o)return t=[...e],e;const n=e.map((o,n)=>e.slice(0,n).every((e,o)=>e===t[o]||""===e)&&o===t[n]?"":o);return t=[...e],n})}static renderFinalResult(t,o,n,i,a){let s="| "+Array.from({length:a},(e,t)=>0===t?v("Item"):`${v("Content")} ${t}`).join(" | ")+" |\n"+("| "+Array.from({length:a},()=>"---").join(" | ")+" |\n")+n.map(e=>`| ${Array.from({length:a},(t,o)=>(e[o]||"").replace(/\|/g,"\\|")).join(" | ")} |`).join("\n"),r="";o.length>0&&(r+=o.join("\n").trimEnd()+"\n\n"),r+=s,i.length>0&&(r+="\n\n"+i.join("\n").trimStart());const l=t.getCursor("from");if(l.line>0&&0===o.length){""!==t.getLine(l.line-1).trim()&&(r="\n"+r)}t.replaceSelection(r),new e.Notice(v("Super conversion completed: context preserved and layout optimized"))}static convertTableToList(t){let o=t.getSelection();if(!o||!o.includes("|")){const e=t.getCursor("from"),n=t.lineCount();let i=e.line;for(;i>0;){if(!t.getLine(i-1).includes("|"))break;i--}let a=e.line;for(;a0&&(o=e.join("\n"),t.setSelection({line:i,ch:0},{line:a,ch:t.getLine(a).length}))}}if(!o||!o.includes("|"))return void new e.Notice(v("Please select a valid Markdown table"));const n=o.split(/\r?\n/),i=[];for(const e of n){if(e.match(/^\s*\|?[\s\-:|]+\|?\s*$/)||""===e.trim())continue;e.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(e=>e.trim()).forEach((e,t)=>{if(""!==e&&e!==v("Item")&&!e.startsWith(v("Content"))){const o=" ".repeat(t);i.push(`${o}- ${e}`)}})}t.replaceSelection(i.join("\n")),new e.Notice(v("Table converted to multi-level list"))}}const ln={previewCharsPerSection:2e3,maxSections:50,verbatimThreshold:5e4};function cn(e,t={}){const o={...ln,...t};return e.length<=o.verbatimThreshold?e:function(e,t,o){const n=e.split(/(?=^#{1,6}\s+)/m).filter(e=>e.trim());if(n.length<=1)return dn(e,4*t);const i=n.slice(0,o),a=n.length>o,s=i.map(e=>{const o=e.trim().split("\n"),n=o[0],i=o.slice(1).join("\n").trim();return i.length<=t?e.trim():`${n}\n${dn(i,t)}`}).join("\n\n");if(a)return`${s}\n\n[... ${n.length-o} more sections omitted ...]`;return s}(e,o.previewCharsPerSection,o.maxSections)}function dn(e,t){if(e.length<=t)return e;const o=e.slice(0,t),n=/[.!?。!?]\s+/g;let i,a=-1;for(;null!==(i=n.exec(o));)i.index>.5*t&&(a=i.index+1);if(a>0)return o.slice(0,a)+" ...";const s=o.lastIndexOf("\n\n");if(s>.5*t)return o.slice(0,s)+"\n\n...";const r=o.lastIndexOf(" ");return r>.8*t?o.slice(0,r)+" ...":o+"..."}class un extends e.Modal{constructor(e,t,o,n,i={}){super(e),this.result={},this.inputElements=new Map,this.linkedNoteFieldStates=new Map,this.linkedContextByField=new Map,this.linkedContextSyncTokens=new Map,this.linkedContextWrapEl=null,this.linkedContextItemsEl=null,this.title=t,this.fields=o,this.onSubmit=n,this.options=i,o.forEach(e=>{this.result[e.key]=e.defaultValue||""})}onOpen(){const{contentEl:t}=this;if(t.empty(),this.inputElements.clear(),this.linkedNoteFieldStates.clear(),this.linkedContextByField.clear(),this.linkedContextSyncTokens.clear(),this.linkedContextWrapEl=null,this.linkedContextItemsEl=null,this.options.modalClassName&&this.modalEl.addClass(this.options.modalClassName),t.createEl("h2",{text:this.title}),this.fields.forEach(o=>{if(o.hideLabel)return void this.renderFullWidthField(t,o);const n=new e.Setting(t).setName(o.label);o.multiline?n.addTextArea(e=>{e.setPlaceholder(o.placeholder||"").setValue(o.defaultValue||"").onChange(e=>{this.result[o.key]=e}),this.inputElements.set(o.key,e.inputEl),e.inputEl.rows=5,e.inputEl.addClass("editing-toolbar-textarea-input"),this.attachLinkedNoteSupport(o,e.inputEl,e.inputEl.parentElement instanceof HTMLElement?e.inputEl.parentElement:n.controlEl),o===this.fields[0]&&setTimeout(()=>e.inputEl.focus(),10),e.inputEl.addEventListener("keydown",e=>{this.handleLinkedNoteKeydown(o.key,e)||"Enter"===e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.submit())})}):n.addText(e=>{e.setPlaceholder(o.placeholder||"").setValue(o.defaultValue||"").onChange(e=>{this.result[o.key]=e}),this.inputElements.set(o.key,e.inputEl),this.attachLinkedNoteSupport(o,e.inputEl,e.inputEl.parentElement instanceof HTMLElement?e.inputEl.parentElement:n.controlEl),o===this.fields[0]&&setTimeout(()=>e.inputEl.focus(),10),e.inputEl.addEventListener("keydown",e=>{this.handleLinkedNoteKeydown(o.key,e)||"Enter"===e.key&&(e.preventDefault(),this.submit())})})}),this.options.contextItems&&this.options.contextItems.length>0){const e=t.createDiv({cls:"editing-toolbar-text-input-context"});this.options.contextLabel&&e.createDiv({cls:"editing-toolbar-text-input-context-label",text:this.options.contextLabel}),this.options.contextItems.forEach(t=>{const o=e.createDiv({cls:"editing-toolbar-text-input-context-item"});o.createSpan({cls:"editing-toolbar-text-input-context-item-label",text:t.label});const n=o.createSpan({cls:"editing-toolbar-text-input-context-item-preview",text:t.preview});t.title&&(n.title=t.title)})}if(this.hasLinkedNoteSupport()&&(this.linkedContextWrapEl=t.createDiv({cls:"editing-toolbar-text-input-context"}),this.linkedContextWrapEl.style.display="none",this.linkedContextWrapEl.createDiv({cls:"editing-toolbar-text-input-context-label",text:this.options.linkedNotes?.contextLabel||v("Referenced notes")}),this.linkedContextItemsEl=this.linkedContextWrapEl.createDiv(),this.syncLinkedNoteContextsForEnabledFields()),this.options.suggestions&&this.options.suggestions.length>0){const e=t.createDiv({cls:"editing-toolbar-text-input-suggestions"});e.createDiv({cls:"editing-toolbar-text-input-suggestions-label",text:v("Suggestions")});const o=e.createDiv({cls:"editing-toolbar-text-input-suggestions-chips"});this.options.suggestions.forEach(e=>{const t=o.createEl("button",{cls:"mod-muted editing-toolbar-text-input-suggestion-chip",text:e.label});t.type="button",t.addEventListener("click",()=>{this.applySuggestion(e)})})}const o=[this.options.linkedNotes?.enabled?this.options.linkedNotes.hint||v("Type [[]] to reference document content."):"",this.options.footerHint||""].filter(e=>e.trim().length>0);if(o.length>0){t.createDiv({cls:"editing-toolbar-text-input-footer-hint"}).textContent=o.join(" ")}new e.Setting(t).addButton(e=>e.setButtonText(v("Confirm")).setCta().onClick(()=>{this.submit()})).addButton(e=>e.setButtonText(v("Cancel")).onClick(()=>{this.close()}))}renderFullWidthField(e,t){const o=e.createDiv({cls:"editing-toolbar-text-input-field-full"});if(t.multiline){const e=o.createEl("textarea",{cls:"editing-toolbar-textarea-input"});return e.placeholder=t.placeholder||"",e.value=t.defaultValue||"",e.rows=5,t.label&&e.setAttribute("aria-label",t.label),this.inputElements.set(t.key,e),this.attachLinkedNoteSupport(t,e,o),t===this.fields[0]&&setTimeout(()=>e.focus(),10),e.addEventListener("input",()=>{this.result[t.key]=e.value}),void e.addEventListener("keydown",e=>{this.handleLinkedNoteKeydown(t.key,e)||"Enter"===e.key&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.submit())})}const n=o.createEl("input");n.type="text",n.placeholder=t.placeholder||"",n.value=t.defaultValue||"",t.label&&n.setAttribute("aria-label",t.label),this.inputElements.set(t.key,n),this.attachLinkedNoteSupport(t,n,o),t===this.fields[0]&&setTimeout(()=>n.focus(),10),n.addEventListener("input",()=>{this.result[t.key]=n.value}),n.addEventListener("keydown",e=>{this.handleLinkedNoteKeydown(t.key,e)||"Enter"===e.key&&(e.preventDefault(),this.submit())})}async submit(){await this.syncLinkedNoteContextsForEnabledFields();const e=this.buildSubmitMeta();await this.onSubmit(this.result,e),this.close()}applySuggestion(e){const t=e.fieldKey||this.fields[0]?.key;if(!t)return;const o=this.inputElements.get(t);if(!o)return;const n=this.result[t]||"",i=!1===e.replace&&n.trim().length>0?`${n.trim()}\n${e.value}`:e.value;if(this.result[t]=i,o.value=i,o.focus(),"number"==typeof o.selectionStart){const e=i.length;o.setSelectionRange(e,e)}this.isLinkedNoteField(t)&&this.syncLinkedNoteContextForField(t,i)}onClose(){const{contentEl:e}=this;e.empty(),this.inputElements.clear(),this.linkedNoteFieldStates.clear(),this.linkedContextByField.clear(),this.linkedContextSyncTokens.clear(),this.linkedContextWrapEl=null,this.linkedContextItemsEl=null,this.options.modalClassName&&this.modalEl.removeClass(this.options.modalClassName)}hasLinkedNoteSupport(){return!0===this.options.linkedNotes?.enabled}isLinkedNoteField(e){if(!this.hasLinkedNoteSupport())return!1;const t=this.options.linkedNotes?.fieldKeys;return t&&0!==t.length?t.includes(e):e===this.fields[0]?.key}attachLinkedNoteSupport(e,t,o){if(!this.isLinkedNoteField(e.key)||!o)return;o.addClass("editing-toolbar-text-input-linked-note-host");const n=o.createDiv({cls:"editing-toolbar-text-input-mention-dropdown"});n.style.display="none",this.linkedNoteFieldStates.set(e.key,{fieldKey:e.key,inputEl:t,hostEl:o,dropdownEl:n,linkStartPos:-1,selectedSuggestionIndex:0,suggestionFiles:[]}),t.addEventListener("input",()=>{const o="number"==typeof t.selectionStart?t.selectionStart:t.value.length,n=this.normalizeLinkTriggerInput(t.value,o);n.value===t.value&&n.cursorPos===o||(t.value=n.value,t.setSelectionRange(n.cursorPos,n.cursorPos),this.result[e.key]=n.value),this.updateLinkedNoteSuggestions(e.key),this.syncLinkedNoteContextForField(e.key,t.value)}),t.value.trim().length>0&&this.syncLinkedNoteContextForField(e.key,t.value)}handleLinkedNoteKeydown(e,t){const o=this.linkedNoteFieldStates.get(e);if(!o)return!1;if(!("none"!==o.dropdownEl.style.display&&o.suggestionFiles.length>0))return"Escape"===t.key&&(o.dropdownEl.style.display="none",o.linkStartPos=-1,o.suggestionFiles=[]),!1;if("ArrowDown"===t.key)return t.preventDefault(),o.selectedSuggestionIndex=(o.selectedSuggestionIndex+1)%o.suggestionFiles.length,this.renderLinkedNoteDropdown(o),!0;if("ArrowUp"===t.key)return t.preventDefault(),o.selectedSuggestionIndex=(o.selectedSuggestionIndex-1+o.suggestionFiles.length)%o.suggestionFiles.length,this.renderLinkedNoteDropdown(o),!0;if("Tab"===t.key||"Enter"===t.key&&!t.shiftKey&&!t.ctrlKey&&!t.metaKey){t.preventDefault();const e=o.suggestionFiles[o.selectedSuggestionIndex];return e&&this.selectLinkedNoteSuggestion(o,e),!0}return"Escape"===t.key&&(t.preventDefault(),o.dropdownEl.style.display="none",o.linkStartPos=-1,o.suggestionFiles=[],!0)}updateLinkedNoteSuggestions(e){const t=this.linkedNoteFieldStates.get(e);if(!t)return;const o="number"==typeof t.inputEl.selectionStart?t.inputEl.selectionStart:t.inputEl.value.length,n=t.inputEl.value.substring(0,o),i=n.match(/\[\[([^\]]*?)$/);if(!i)return t.dropdownEl.style.display="none",t.linkStartPos=-1,void(t.suggestionFiles=[]);t.linkStartPos=n.lastIndexOf("[[");const a=i[1].toLowerCase(),s=this.app.workspace.getActiveFile(),r=this.app.vault.getMarkdownFiles().filter(e=>e.basename.toLowerCase().includes(a)||e.path.toLowerCase().includes(a)).sort((e,t)=>s&&e.path===s.path?-1:s&&t.path===s.path?1:e.path.localeCompare(t.path)).slice(0,10);t.suggestionFiles=r,t.selectedSuggestionIndex=0,0!==r.length?this.renderLinkedNoteDropdown(t):t.dropdownEl.style.display="none"}renderLinkedNoteDropdown(e){e.dropdownEl.empty(),e.suggestionFiles.forEach((t,o)=>{const n=e.dropdownEl.createDiv({cls:"editing-toolbar-text-input-mention-item"});n.toggleClass("selected",o===e.selectedSuggestionIndex),n.createSpan({cls:"editing-toolbar-text-input-mention-icon",text:"📄"}),n.createSpan({cls:"editing-toolbar-text-input-mention-name",text:t.basename}),n.createSpan({cls:"editing-toolbar-text-input-mention-path",text:t.path}),n.addEventListener("mousedown",o=>{o.preventDefault(),this.selectLinkedNoteSuggestion(e,t)})}),e.dropdownEl.style.display="block";const t=e.dropdownEl.querySelectorAll(".editing-toolbar-text-input-mention-item")[e.selectedSuggestionIndex];t?.scrollIntoView({block:"nearest"})}async selectLinkedNoteSuggestion(e,t){const o="number"==typeof e.inputEl.selectionStart?e.inputEl.selectionStart:e.inputEl.value.length,n=this.app.workspace.getActiveFile()?.path??"",i=this.app.metadataCache.fileToLinktext(t,n,!0),a=e.inputEl.value.substring(0,e.linkStartPos),s=`${a}[[${i}]] ${e.inputEl.value.substring(o)}`;e.inputEl.value=s,this.result[e.fieldKey]=s,e.dropdownEl.style.display="none",e.suggestionFiles=[],e.linkStartPos=-1,e.inputEl.focus();const r=a.length+i.length+5;e.inputEl.setSelectionRange(r,r),await this.syncLinkedNoteContextForField(e.fieldKey,s)}normalizeLinkTriggerInput(e,t){let o=e,n=t;if(o.includes("【【")){const e=o.substring(0,n),t=o.substring(n),i=e.replace(/【【/g,"[[");o=i+t.replace(/【【/g,"[["),n-=e.length-i.length}const i=o.substring(0,n).match(/(?:\[|【){2,}$/);if(i&&i[0].includes("【")){const e=n-i[0].length;o=`${o.substring(0,e)}[[${o.substring(n)}`,n=e+2}return{value:o,cursorPos:n}}async syncLinkedNoteContextsForEnabledFields(){const e=[];this.fields.forEach(t=>{if(!this.isLinkedNoteField(t.key))return;const o=this.inputElements.get(t.key),n=o?.value??this.result[t.key]??"";e.push(this.syncLinkedNoteContextForField(t.key,n))}),await Promise.all(e)}async syncLinkedNoteContextForField(e,t){const o=(this.linkedContextSyncTokens.get(e)??0)+1;this.linkedContextSyncTokens.set(e,o);const n=await this.resolveLinkedContextItems(e,t);this.linkedContextSyncTokens.get(e)===o&&(this.linkedContextByField.set(e,n),this.renderLinkedContextItems())}async resolveLinkedContextItems(t,o){const n=this.app.workspace.getActiveFile()?.path??"",i=Array.from(o.matchAll(/\[\[([^\]]+)\]\]/g)),a=[],s=new Set;for(const o of i){const i=o[1]?.trim();if(!i)continue;const[r,l]=i.split("|"),c=e.parseLinktext((r||"").trim());if(!c.path)continue;const d=this.app.metadataCache.getFirstLinkpathDest(c.path,n);if(d&&!s.has(d.path)){s.add(d.path);try{const e=cn(await this.app.vault.cachedRead(d)),o=e.replace(/\s+/g," ").trim(),n=o.length>80?`${o.slice(0,80)}...`:o,s=l?.trim()||d.basename,r=e.length>140?`${e.slice(0,140)}...`:e;a.push({fieldKey:t,reference:`[[${i}]]`,label:`📄 ${s}`,preview:`${n} (${e.length.toLocaleString()} chars)`,title:r||d.path,content:e,filePath:d.path})}catch(e){console.error(`[TextInputModal] Failed to read linked file: ${i}`,e)}}}return a}renderLinkedContextItems(){if(!this.linkedContextWrapEl||!this.linkedContextItemsEl)return;this.linkedContextItemsEl.empty();const e=Array.from(this.linkedContextByField.values()).flat();0!==e.length?(this.linkedContextWrapEl.style.display="block",e.forEach(e=>{const t=this.linkedContextItemsEl?.createDiv({cls:"editing-toolbar-text-input-context-item"});t?.createSpan({cls:"editing-toolbar-text-input-context-item-label",text:e.label});const o=t?.createSpan({cls:"editing-toolbar-text-input-context-item-preview",text:e.preview});o&&e.title&&(o.title=e.title)})):this.linkedContextWrapEl.style.display="none"}buildSubmitMeta(){const e={},t={},o=[];return this.fields.forEach(n=>{const i=this.result[n.key]??"";e[n.key]=this.isLinkedNoteField(n.key)?this.stripWikiLinkSyntax(i):i;const a=[...this.linkedContextByField.get(n.key)??[]];t[n.key]=a,o.push(...a)}),{cleanedResult:e,linkedContextItems:o,linkedContextByField:t}}stripWikiLinkSyntax(e){return e.replace(/\[\[([^\]]+)\]\]/g,(e,t)=>{const[o,n]=String(t).split("|");return(n||o||"").trim()}).trim()}}class mn{formatAICommandName(...e){return[v("AI"),...e.map(e=>v(e))].join(" / ")}constructor(e){this.aiRewriteCommandIcons={improve:"lucide-wand-2","fix-grammar":"lucide-spell-check-2","make-shorter":"lucide-minimize-2","make-longer":"lucide-maximize-2",simplify:"lucide-align-left",professional:"lucide-briefcase",casual:"lucide-message-circle-more","translate-en":"lucide-languages","translate-zh":"lucide-languages","translate-ja":"lucide-languages","translate-de":"lucide-languages","translate-fr":"lucide-languages","translate-es":"lucide-languages",explain:"lucide-info",summarize:"lucide-file-text",continue:"lucide-pencil-line",custom:"lucide-message-square"},this.executeCommandWithoutBlur=async(e,t)=>{e&&(await t(),e.focus())},this.executeHistoryAction=e=>{if(this.executeCanvasHistoryAction(e))return;const t=this.getActiveEditor();if(t)return void this.executeCommandWithoutBlur(t,()=>"undo"===e?t?.undo():t?.redo());const o="undo"===e?["canvas:undo","editor:undo"]:["canvas:redo","editor:redo"];for(const e of o)try{if(this.plugin.app.commands.executeCommandById(e))return}catch{continue}},this._commandsMap={hrline:{char:5,line:1,prefix:"\n---",suffix:"\n",islinehead:!0},justify:{char:0,line:0,prefix:'',suffix:"
",islinehead:!1},left:{char:0,line:0,prefix:'',suffix:"
",islinehead:!1},right:{char:0,line:0,prefix:'',suffix:"
",islinehead:!1},center:{char:0,line:0,prefix:"",suffix:" ",islinehead:!1},underline:{char:0,line:0,prefix:"",suffix:"",islinehead:!1},superscript:{char:0,line:0,prefix:"",suffix:"",islinehead:!1},subscript:{char:0,line:0,prefix:"",suffix:"",islinehead:!1},codeblock:{char:4,line:0,prefix:"\n```\n",suffix:"\n```\n",islinehead:!1}},this.modCommands=[{id:"editor:insert-embed",name:"Insert Embed",icon:"note-glyph"},{id:"editor:insert-link",name:"Insert Link",icon:"link-glyph"},{id:"editor:insert-tag",name:"Insert Tag",icon:"price-tag-glyph"},{id:"editor:insert-wikilink",name:"Insert Internal link",icon:"bracket-glyph"},{id:"editor:toggle-code",name:"Insert Code",icon:"code-glyph"},{id:"editor:toggle-blockquote",name:"Insert Blockquote",icon:"lucide-text-quote"},{id:"editor:toggle-checklist-status",name:"Cycle List and Checklist",icon:"checkbox-glyph"},{id:"editor:toggle-comments",name:"Insert Comment",icon:"percent-sign-glyph"},{id:"editor:insert-callout",name:"Insert Callout",icon:"lucide-quote"},{id:"editor:insert-mathblock",name:"Insert MathBlock",icon:"lucide-sigma-square"},{id:"editor:insert-table",name:"Insert Table",icon:"lucide-table"},{id:"editor:swap-line-up",name:"Swap Line Up",icon:"lucide-corner-right-up"},{id:"editor:swap-line-down",name:"Swap Line Down",icon:"lucide-corner-right-down"},{id:"editor:attach-file",name:"Attach File",icon:"lucide-paperclip"},{id:"editor:clear-formatting",name:"Clear Formatting",icon:"lucide-eraser"}],this.applyCommand=(e,t)=>{const o=t.getSelection(),n=t.getCursor("from"),i=t.getCursor("to");let a=e.prefix;e.islinehead&&n.ch>0&&(a="\n"+a);const s=e.suffix,r={line:n.line-e.line,ch:n.ch-a.length};if(t.getRange(r,n)==a){const l={line:n.line+e.line,ch:i.ch+s.length};if(t.getRange(i,l)==s){t.replaceRange(o,r,l),t.setCursor(n.line-e.line,n.ch);const i={line:n.line,ch:n.ch-a.length},s={line:n.line,ch:i.ch+o.length};return void t.setSelection(i,s)}}if(t.replaceSelection(`${a}${o}${s}`),e.char>0)t.setCursor(n.line+e.line,n.ch+e.char+o.length);else{const e=n,i={line:e.line,ch:e.ch+a.length},s={line:e.line,ch:i.ch+o.length};t.setSelection(i,s)}},this.plugin=e}executeCanvasHistoryAction(e){const t=this.getActiveCanvasView();if("canvas"!==t?.getViewType?.())return!1;try{t.canvas?.wrapperEl?.focus?.({preventScroll:!0})}catch{}const o=[{owner:t.canvas,method:e,label:`canvas.${e}()`},{owner:t.canvas?.history,method:"undo"===e?"back":"forward",label:`canvas.history.${"undo"===e?"back":"forward"}()`}];for(const e of o){const t=e.owner?.[e.method];if("function"==typeof t)return t.call(e.owner),!0}return!1}getActiveCanvasView(){const e=this.plugin.app.workspace.activeLeaf?.view;if("canvas"===e?.getViewType?.())return e;const t=this.plugin.app.workspace.getLeavesOfType?.("canvas")??[];for(const e of t){const t=e?.view;if("canvas"===t?.getViewType?.())return t}return null}async applyRegexCommand(t,o){try{let n=t.getSelection(),i=t.getCursor("from"),a=t.getCursor("to");if(!n)if(this.plugin.settings.useCurrentLineForRegex){const o=i.line,s=t.getLine(o);if(!s||""===s.trim())return void new e.Notice(v("Current line is empty, please select text or move to a non-empty line"));n=s,i={line:o,ch:0},a={line:o,ch:s.length},t.setSelection(i,a)}else try{const o=await this.readClipboard();if(n=o["text/html"]?e.htmlToMarkdown(o["text/html"]):o["text/markdown"]||o["text/plain"],!n)return void new e.Notice(v("Please select text or copy text to clipboard first"));t.replaceRange(n,i,i);const a=t.offsetToPos(t.posToOffset(i)+n.length);t.setSelection(i,a)}catch(t){return console.error("读取剪贴板失败:",t),void new e.Notice(v("Please select text first"))}if(o.useCondition&&o.conditionPattern){if(!new RegExp(o.conditionPattern).test(n))return void new e.Notice(v("The selected text does not meet the condition requirements"))}let s="";!1!==o.regexGlobal&&(s+="g"),o.regexCaseInsensitive&&(s+="i"),o.regexMultiline&&(s+="m");const r=new RegExp(o.regexPattern,s),l=t.getCursor("from"),c=t.getCursor("to");t.transaction({changes:[{from:l,to:c,text:n.replace(r,o.regexReplacement)}]});const d=t.getSelection(),u=t.offsetToPos(t.posToOffset(l)),m=t.offsetToPos(t.posToOffset(l)+d.length);t.setSelection(u,m)}catch(t){console.error("正则表达式命令执行错误:",t),new e.Notice(v("Regex command execution error: ")+t.message)}}async readClipboard(){const e={};try{const t=await navigator.clipboard.read();for(const o of t){const t=o.types;for(const n of t)if("text/html"===n||"text/plain"===n||"text/markdown"===n){const t=await o.getType(n);e[n]=await t.text()}}}catch(t){try{const t=await navigator.clipboard.readText();e["text/plain"]=t}catch(e){console.error("读取剪贴板失败:",e)}}return e}getActiveEditor(){const e=this.plugin.app.workspace?.activeEditor;if(e&&e.editor)return e.editor;try{const e=this.plugin.app.workspace.activeLeaf?.view?.editor;if(e)return e}catch{}return null}registerCommands(){this.plugin.addCommand({id:"renumber-ordered-list",name:"Renumber Ordered List",editorCallback:e=>{e&&this.executeCommandWithoutBlur(e,()=>function(e){if(!e.getSelection()){const t=e.getCursor(),o=e.getLine(t.line);if(/^\s*\d+\.\s/.test(o)){const o=e.getLine(t.line).match(/^\s*/)?.[0].length||0,n=t.line-1,i=n>=0?e.getLine(n).trim():"";if(n<0||!/^\s*\d+\.\s/.test(i)||(i.match(/^\s*/)?.[0].length||0)0;){const t=e.getLine(o-1);if(!/^\s*\d+\.\s/.test(t.trim()))break;o--}for(;n{this.plugin.settings.cMenuVisibility=!this.plugin.settings.cMenuVisibility,this.plugin.settings.cMenuVisibility?setTimeout(()=>{dispatchEvent(new Event("editingToolbar-NewCommand"))},100):p(this.plugin.settings.cMenuVisibility),se(this.plugin),await this.plugin.saveSettings()}}),this.plugin.addCommand({id:"toggle-top-toolbar",name:"Toggle Top Toolbar",callback:async()=>{const e=this.plugin.settings,t=this.plugin.positionStyle;e.enableTopToolbar=!e.enableTopToolbar;let o=null;e.enableTopToolbar?o="top":"top"===t&&(o=e.enableFollowingToolbar?"following":e.enableFixedToolbar?"fixed":null),o&&o!==t&&this.plugin.onPositionStyleChange(o),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar()}}),this.plugin.addCommand({id:"toggle-following-toolbar",name:"Toggle Following Toolbar",callback:async()=>{const e=this.plugin.settings,t=this.plugin.positionStyle;e.enableFollowingToolbar=!e.enableFollowingToolbar;let o=null;e.enableFollowingToolbar?o="following":"following"===t&&(o=e.enableTopToolbar?"top":e.enableFixedToolbar?"fixed":null),o&&o!==t&&this.plugin.onPositionStyleChange(o),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar()}}),this.plugin.addCommand({id:"toggle-fixed-toolbar",name:"Toggle Fixed Toolbar",callback:async()=>{const e=this.plugin.settings,t=this.plugin.positionStyle;e.enableFixedToolbar=!e.enableFixedToolbar;let o=null;e.enableFixedToolbar?o="fixed":"fixed"===t&&(o=e.enableTopToolbar?"top":e.enableFollowingToolbar?"following":null),o&&o!==t&&this.plugin.onPositionStyleChange(o),await this.plugin.saveSettings(),this.plugin.handleeditingToolbar()}}),this.plugin.addCommand({id:"get-plain-text",name:"Get Plain Text",editorCallback:e=>{rn.getPlainText(e)}}),this.plugin.addCommand({id:"insert-blank-lines",name:"Insert Blank Lines",editorCallback:e=>{rn.insertBlankLines(e)}}),this.plugin.addCommand({id:"remove-blank-lines",name:"Remove Blank Lines",editorCallback:e=>rn.processWhitespace(e,{removeEmptyLines:!0})}),this.plugin.addCommand({id:"ai-login-pkmer",name:this.formatAICommandName("Account","Login to PKMer AI"),icon:"lucide-log-in",callback:async()=>{await this.plugin.aiManager.loginWithPKMer()}}),this.plugin.addCommand({id:"ai-logout-pkmer",name:this.formatAICommandName("Account","Logout from PKMer"),icon:"lucide-log-out",callback:async()=>{await this.plugin.aiManager.logoutFromPKMer()}}),this.plugin.addCommand({id:"ai-inline-completion",name:this.formatAICommandName("Complete","Quick Trigger"),icon:"lucide-sparkles",hotkeys:[{modifiers:["Mod"],key:"j"}],editorCallback:e=>{this.plugin.aiManager.triggerInlineCompletion(e)}});const t=(e,t,o,n)=>{this.plugin.addCommand({id:e,name:this.formatAICommandName("Rewrite",t),icon:n,editorCallback:e=>{this.plugin.aiManager.startRewrite(e,o)}})};t("ai-rewrite-improve","Improve Selection","improve","lucide-wand-2"),t("ai-rewrite-continue","Continue writing","continue","lucide-pencil-line"),O.forEach(e=>{const o=`ai-rewrite-${e.instruction}`;"improve"!==e.instruction&&"continue"!==e.instruction&&t(o,e.label,e.instruction,this.aiRewriteCommandIcons[e.instruction])}),this.plugin.addCommand({id:"ai-rewrite-custom",name:this.formatAICommandName("Rewrite","Custom Rewrite"),icon:"lucide-message-square",editorCallback:e=>{this.plugin.aiManager.openCustomRewrite(e)}}),this.plugin.addCommand({id:"ai-canvas-expand",name:this.formatAICommandName("Canvas","Expand Current Node"),icon:"lucide-waypoints",callback:()=>{this.plugin.aiManager.openCanvasNodeExpansionModal()}}),this.plugin.addCommand({id:"ai-canvas-global-prompt",name:this.formatAICommandName("Canvas","Global Prompt"),icon:"lucide-sparkles",callback:()=>{this.plugin.aiManager.openCanvasGlobalPromptModal()}}),ee.forEach(e=>{this.plugin.addCommand({id:`ai-toolbox-${e.id}`,name:this.formatAICommandName("Toolbox",e.label),icon:e.icon,editorCallback:t=>{this.plugin.aiManager.runToolboxAction(t,e.id)}})}),this.plugin.addCommand({id:"split-lines",name:"Split Lines",editorCallback:e=>{rn.splitLines(e)}}),this.plugin.addCommand({id:"smart-symbols",name:"Full Half Converter",editorCallback:e=>{rn.smartTypography(e)}}),this.plugin.addCommand({id:"dedupe-lines",name:"Dedupe Lines",editorCallback:e=>rn.dedupe(e,{trimBeforeCompare:!0})}),this.plugin.addCommand({id:"add-wrap",name:"Add Prefix/Suffix",editorCallback:e=>{new un(this.plugin.app,v("Add Prefix/Suffix"),[{key:"prefix",label:v("Prefix"),placeholder:v("Enter prefix"),defaultValue:""},{key:"suffix",label:v("Suffix"),placeholder:v("Enter suffix"),defaultValue:""}],t=>{const o=t;rn.addWrap(e,o.prefix,o.suffix,!0)}).open()}}),this.plugin.addCommand({id:"number-lines",name:"Number Lines (Custom)",editorCallback:e=>{new un(this.plugin.app,v("Number Lines Configuration"),[{key:"start",label:v("Start Number"),placeholder:"1",defaultValue:"1"},{key:"step",label:v("Step"),placeholder:"1",defaultValue:"1"},{key:"sep",label:v("Separator"),placeholder:". ",defaultValue:". "}],t=>{const o=parseInt(t.start)||1,n=parseInt(t.step)||1,i=t.sep||". ";rn.numberList(e,o,n,i,"")}).open()}}),this.plugin.addCommand({id:"remove-whitespace-trim",name:"Trim Line Ends",editorCallback:e=>{rn.processWhitespace(e,{trim:!0})}}),this.plugin.addCommand({id:"remove-whitespace-compress",name:"Shrink Extra Spaces",editorCallback:e=>{rn.processWhitespace(e,{compress:!0})}}),this.plugin.addCommand({id:"remove-whitespace-all",name:"Remove All Whitespace",editorCallback:e=>{rn.processWhitespace(e,{all:!0})}}),this.plugin.addCommand({id:"list-to-table",name:v("List to Table"),editorCallback:e=>{rn.convertListToTableMultiDim(e)}}),this.plugin.addCommand({id:"table-to-list",name:v("Table to List"),editorCallback:e=>rn.convertTableToList(e)}),this.plugin.addCommand({id:"extract-between",name:"Extract Between Strings",editorCallback:e=>{new un(this.plugin.app,v("Extract Between Strings"),[{key:"start",label:v("Start String"),placeholder:v("Enter start string"),defaultValue:"["},{key:"end",label:v("End String"),placeholder:v("Enter end string"),defaultValue:"]"}],t=>{const o=t;rn.extractBetween(e,o.start,o.end)}).open()}}),this.plugin.addCommand({id:"merge-lines",name:v("Merge Lines"),editorCallback:e=>{new un(this.plugin.app,v("Merge Lines Settings"),[{key:"sep",label:v("Separator (leave empty for smart spacing)"),placeholder:v("e.g., comma, pipe, arrow"),defaultValue:""}],t=>{rn.mergeLines(e,{separator:t.sep,preserveParagraphs:""===t.sep,trimLines:!0})}).open()}}),this.plugin.addCommand({id:"format-eraser",name:"Format Eraser",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>Te(this.plugin,e))},icon:"eraser"}),this.plugin.addCommand({id:"change-font-color",name:"Change Font Color",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>l(this.plugin.settings.cMenuFontColor??"#2DC26B",e))},icon:''}),this.plugin.addCommand({id:"change-background-color",name:"Change Background Color",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>c(this.plugin.settings.cMenuBackgroundColor??"#FA541C",e))},icon:''}),this.plugin.addCommand({id:"indent-list",name:"Indent List",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.indentList())},icon:"indent-glyph"}),this.plugin.addCommand({id:"undent-list",name:"Unindent List",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.unindentList())},icon:"unindent-glyph"}),this.plugin.addCommand({id:"toggle-numbered-list",name:"Numbered List",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleNumberList())},icon:"number-list-glyph"}),this.plugin.addCommand({id:"toggle-bullet-list",name:"Unordered List",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleBulletList())},icon:"bullet-list-glyph"}),this.plugin.addCommand({id:"toggle-highlight",name:"Highlight",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleMarkdownFormatting("highlight"))},icon:"highlight-glyph"}),this.plugin.addCommand({id:"toggle-bold",name:"Toggle Bold",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>{e.toggleMarkdownFormatting("bold")})},icon:"bold-glyph"}),this.plugin.addCommand({id:"toggle-italics",name:"Toggle Italics",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleMarkdownFormatting("italic"))},icon:"italic-glyph"}),this.plugin.addCommand({id:"toggle-strikethrough",name:"Toggle Strikethrough",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleMarkdownFormatting("strikethrough"))},icon:"strikethrough-glyph"}),this.plugin.addCommand({id:"toggle-inline-math",name:"Toggle Inline Math",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleMarkdownFormatting("math"))},icon:"lucide-sigma"}),this.plugin.addCommand({id:"editor:cycle-list-checklist",name:"Cycle List and Checklist",icon:"lucide-check-square",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,()=>e?.toggleCheckList(!0))}}),this.plugin.addCommand({id:"editor-undo",name:"Undo Edit",callback:()=>{this.executeHistoryAction("undo")},icon:"undo-glyph"}),this.plugin.addCommand({id:"editor-redo",name:"Redo Edit",callback:()=>{this.executeHistoryAction("redo")},icon:"redo-glyph"}),this.plugin.addCommand({id:"editor-copy",name:"Copy",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,async()=>{try{await window.navigator.clipboard.writeText(e.getSelection()),this.plugin.app.commands.executeCommandById("editor:focus")}catch(e){console.error("Copy failed:",e)}})},icon:"lucide-copy"}),this.plugin.addCommand({id:"editor-paste",name:"Paste",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,async()=>{try{const t=await window.navigator.clipboard.readText();t&&e.replaceSelection(t),this.plugin.app.commands.executeCommandById("editor:focus")}catch(e){console.error("Paste failed:",e)}})},icon:"lucide-clipboard-type"}),this.plugin.addCommand({id:"editor-cut",name:"Cut",callback:()=>{const e=this.getActiveEditor();e&&this.executeCommandWithoutBlur(e,async()=>{try{await window.navigator.clipboard.writeText(e.getSelection()),e.replaceSelection(""),this.plugin.app.commands.executeCommandById("editor:focus")}catch(e){console.error("Cut failed:",e)}})},icon:"lucide-scissors"}),this.plugin.addCommand({id:"insert-callout",name:"Insert Callout(Modal)",icon:"lucide-quote",callback:()=>{new nn(this.plugin).open()}}),this.plugin.addCommand({id:"insert-link",name:"Insert Link(Modal)",icon:"lucide-link",callback:()=>{new sn(this.plugin).open()}}),this.plugin.addCommand({id:"fullscreen-focus",name:"Toggle Fullscreen Focus Mode",hotkeys:[{modifiers:["Mod","Shift"],key:"F11"}],callback:()=>function(t){Object.defineProperty(exports,"__esModule",{value:!0}),exports.toggleFull=exports.isFull=exports.exitFull=exports.beFull=void 0;let o=document.documentElement,n=o.querySelector("head"),i=document.createElement("style"),a="requestFullscreen",s="exitFullscreen",r="fullscreenElement";"webkitRequestFullScreen"in o?(a="webkitRequestFullScreen",s="webkitExitFullscreen",r="webkitFullscreenElement"):"msRequestFullscreen"in o?(a="msRequestFullscreen",s="msExitFullscreen",r="msFullscreenElement"):"mozRequestFullScreen"in o?(a="mozRequestFullScreen",s="mozCancelFullScreen",r="mozFullScreenElement"):"requestFullscreen"in o||console.log("当前浏览器不支持Fullscreen API !");const l=t.workspace.getActiveViewOfType(e.MarkdownView);if(!l)return;let c,d=l.containerEl,u=document.body?.querySelector(".mod-vertical.mod-root .workspace-tab-container");function m(e){return e[a]()}function p(){return o.contains(i)&&(null==n||n.removeChild(i)),document[s]()}function h(e){return e===document[r]}c=new MutationObserver(function(e){e.forEach(function(e){e.addedNodes.forEach(function(e){if(h(u))try{document.body.removeChild(e),d.appendChild(e)}catch(e){console.log(e.message)}})})}),u.addEventListener("fullscreenchange",function(){h(u)||c.disconnect()}),h(u)?(c.disconnect(),p()):(m(u),c.observe(document.body,{childList:!0})),exports.beFull=m,exports.exitFull=p,exports.isFull=h,exports.toggleFull=function(e){return h(e)?(p(),!1):(m(e),!0)}}(app),icon:"fullscreen"}),this.plugin.addCommand({id:"workplace-fullscreen-focus",name:"Toggle Workplace Fullscreen Focus",callback:()=>function(t){on=e.requireApiVersion("0.15.0")?activeWindow.document:window.document;let o=on;t.workspace.leftSplit.collapsed&&t.workspace.rightSplit.collapsed?(t.commands.executeCommandById("app:toggle-right-sidebar"),t.commands.executeCommandById("app:toggle-left-sidebar"),t.workspace.leftRibbon.show(),o.body.classList.contains("auto-hide-header")&&o.body.classList.remove("auto-hide-header")):(o.body.classList.contains("auto-hide-header")||o.body.classList.add("auto-hide-header"),t.workspace.leftRibbon.hide(),t.workspace.leftSplit.collapsed||t.commands.executeCommandById("app:toggle-left-sidebar"),t.workspace.rightSplit.collapsed||t.commands.executeCommandById("app:toggle-right-sidebar"))}(app),hotkeys:[{modifiers:["Mod"],key:"F11"}],icon:"remix-SplitCellsHorizontal"});for(let e=0;e<=6;e++)this.plugin.addCommand({id:`header${e}-text`,name:0===e?"Remove header level":`Header ${e}`,callback:()=>{const t=this.getActiveEditor();t&&this.executeCommandWithoutBlur(t,()=>r("#".repeat(e),t))},icon:0===e?"heading-glyph":`header-${e}`});Object.keys(this._commandsMap).forEach(e=>{this.plugin.addCommand({id:`${e}`,name:`Toggle ${e}`,icon:`${e}-glyph`,callback:()=>{const t=this.getActiveEditor();t&&this.executeCommandWithoutBlur(t,()=>{this.applyCommand(this._commandsMap[e],t)})}})}),this.modCommands.forEach(e=>{this.plugin.addCommand({id:`${e.id}`,name:`${e.name}`,icon:`${e.icon}`,callback:()=>{const t=this.getActiveEditor();t&&this.executeCommandWithoutBlur(t,async()=>{const o=t.getCursor("to");let n=this.getCharacterOffset(e.id);await this.plugin.app.commands.executeCommandById(`${e.id}`),0!=n&&t.setCursor(o.line,o.ch+n)})}})}),this.plugin.addCommand({id:"toggle-format-brush",name:"Toggle Format Brush",icon:"paintbrush",editorCallback:e=>{this.plugin.toggleFormatBrush()}}),this.registerCustomCommands();["toggle-bold","toggle-italics","toggle-strikethrough","toggle-highlight","toggle-code","toggle-blockquote","header0-text","header1-text","header2-text","header3-text","header4-text","header5-text","header6-text","toggle-numbered-list","toggle-bullet-list","format-eraser","indent-list","undent-list","change-font-color","change-background-color",...this.plugin.settings.customCommands.map(e=>`${e.id}`),...Object.keys(this._commandsMap)].forEach(e=>{const t=this.plugin.app.commands.commands[`editing-toolbar:${e}`];if(t&&t.callback){const o=t.callback;t.callback=()=>{o(),this.plugin.setLastExecutedCommand(`editing-toolbar:${e}`)}}})}getCharacterOffset(e){switch(e){case"editor:insert-tag":return 1;case"editor:insert-callout":return 11;default:return 0}}reloadCustomCommands(){this.plugin.settings.customCommands.forEach(e=>{const t=`${e.id}`;this.plugin.app.commands.commands[`editing-toolbar:${t}`]&&delete this.plugin.app.commands.commands[`editing-toolbar:${t}`]}),this.registerCustomCommands()}registerCustomCommands(){this.plugin.settings.customCommands.forEach(e=>{const t=`${e.id}`;this.plugin.addCommand({id:t,name:e.name,icon:e.icon,editorCallback:o=>{if(e.useRegex&&e.regexPattern)o&&this.executeCommandWithoutBlur(o,()=>{this.applyRegexCommand(o,e),this.plugin.setLastExecutedCommand(`editing-toolbar:${t}`)});else{const n={prefix:e.prefix,suffix:e.suffix,char:e.char,line:e.line,islinehead:e.islinehead};this._commandsMap[e.id]=n,o&&this.executeCommandWithoutBlur(o,()=>{this.applyCommand(n,o),this.plugin.setLastExecutedCommand(`editing-toolbar:${t}`)})}}})})}get commandsMap(){return this._commandsMap}}class pn extends e.Modal{constructor(e,t={}){super(e),this.resolver=null,this.settled=!1,this.options=t}openAndWait(){return new Promise(e=>{this.resolver=e,this.open()})}onOpen(){const{contentEl:t,modalEl:o}=this;o.addClass("editing-toolbar-ai-consent-modal"),t.empty(),t.addClass("editing-toolbar-ai-consent");const n=t.createDiv("editing-toolbar-ai-consent-hero");n.createEl("h2",{text:v("Enable AI Editor")}),n.createDiv({cls:"editing-toolbar-ai-consent-desc",text:v("Unlock inline completion, rewrite, frontmatter, and canvas generation right inside your toolbar.")}),n.createDiv({cls:"editing-toolbar-ai-consent-note",text:v("Sign in to PKMer AI for free managed AI, or use your own compatible model.")});const i=t.createDiv("editing-toolbar-ai-consent-section");i.createDiv({cls:"editing-toolbar-ai-consent-section-title",text:v("What you get")});const a=i.createDiv("editing-toolbar-ai-consent-list");this.createListItem(a,v("Inline completion while you write")),this.createListItem(a,v("Rewrite, summarize, and continue text in place")),this.createListItem(a,v("Generate frontmatter, lists, tables, and canvas drafts"));const s=t.createDiv("editing-toolbar-ai-consent-section");s.createDiv({cls:"editing-toolbar-ai-consent-section-title",text:v("Before you enable AI")});const r=s.createDiv("editing-toolbar-ai-consent-list");this.createListItem(r,v("The plugin itself does not intentionally store your note content.")),this.createListItem(r,v("AI requests are sent only to the provider you choose, such as PKMer AI or your custom model.")),this.createListItem(r,v("Those requests remain subject to the provider privacy policy, terms, and model rules.")),this.createListItem(r,v("You can turn AI off at any time in settings."));const l=t.createDiv("editing-toolbar-ai-consent-actions");new e.ButtonComponent(l).setButtonText(v("Not now")).onClick(()=>this.finish(!1)),new e.ButtonComponent(l).setButtonText(v("Agree & Enable AI")).setCta().onClick(()=>this.finish(!0))}onClose(){this.contentEl.empty(),this.resolve(!1)}createListItem(e,t){const o=e.createDiv("editing-toolbar-ai-consent-item");o.createSpan({cls:"editing-toolbar-ai-consent-dot"}),o.createSpan({cls:"editing-toolbar-ai-consent-text",text:t})}finish(e){this.resolve(e),this.close()}resolve(e){this.settled||(this.settled=!0,this.resolver?.(e),this.resolver=null)}}class hn{constructor(e,t){this.customChatCompletionsUrlCache=new Map,this.plugin=e,this.authService=t}async*complete(e,t){const o=await this.requestCompletion(e,t);yield*this.streamText(o,t)}async*rewrite(e,t){const o=await this.requestRewrite(e,t);yield*this.streamText(o,t)}hasCustomProviderConfigured(){return!!this.getCustomProviderValidation().provider}async testCustomProviderConnection(){const e=this.getCustomProviderValidation();if(!e.provider)throw new Error(`Missing custom model settings: ${e.missing.join(", ")}`);await this.requestChatCompletionResult([{role:"system",content:"Reply with OK only."},{role:"user",content:"ping"}],void 0,{maxTokens:1},e.provider)}async listCustomOllamaModels(){const t=this.getCustomProviderValidation().provider;if(!t||"custom"!==t.kind||"ollama"!==t.apiFormat)throw new Error("Ollama custom model settings are not enabled.");const o=this.buildOllamaTagsCandidateUrls(t.baseUrl);let n=null;for(let i=0;i=200&&o.status<300)return this.extractOllamaModelNames(o.json);throw this.createRequestResponseError(o,a)}catch(e){n=e;if(i=200&&s.status<300)return s;throw this.createRequestResponseError(s,t)}buildRequestBody(e,t,o,n={}){return"custom"===t.kind&&"ollama"===t.apiFormat?this.buildOllamaRequestBody(e,t,o,n):{model:t.model,temperature:t.temperature,max_tokens:n.maxTokens,stream:!1,messages:o}}buildOllamaRequestBody(e,t,o,n={}){const i={};return Number.isFinite(t.temperature)&&(i.temperature=t.temperature),"number"==typeof n.maxTokens&&(i.num_predict=n.maxTokens),/\/api\/generate$/i.test(e)?{model:t.model,prompt:this.buildOllamaGeneratePrompt(o),stream:!1,options:Object.keys(i).length>0?i:void 0}:{model:t.model,stream:!1,messages:o,options:Object.keys(i).length>0?i:void 0}}buildOllamaGeneratePrompt(e){const t=e.filter(e=>"string"==typeof e.content&&e.content.trim().length>0).map(e=>`${e.role.toUpperCase()}:\n${e.content.trim()}`).join("\n\n");return t?`${t}\n\nASSISTANT:\n`:"ASSISTANT:\n"}async resolveProvider(e="rewrite"){const t=await this.getPKMerProvider(e);if(t)return t;const o=this.getCustomProviderValidation().provider;if(o)return o;throw new Error("No AI provider is configured. Please log in to PKMer or fill in custom model settings.")}async getPKMerProvider(e){const t=this.plugin.settings.ai;return await this.authService.verify()&&this.authService.aiToken?{kind:"pkmer",apiFormat:"openai-compatible",baseUrl:t.pkmerApiBaseUrl,apiKey:this.authService.aiToken,model:P(t,e),temperature:t.customModel.temperature}:null}getCustomProviderValidation(){if(!this.plugin.settings.ai.enableCustomModel)return{provider:null,missing:["disabled"]};const e=this.plugin.settings.ai.customModel,t=this.authService.customModelApiKey.trim(),o=e.apiFormat??"openai-compatible",n=[];return e.baseUrl.trim()||n.push("baseUrl"),e.model.trim()||n.push("model"),"ollama"===o||t||n.push("apiKey"),n.length>0?{provider:null,missing:n}:{provider:{kind:"custom",apiFormat:o,baseUrl:e.baseUrl.trim(),apiKey:t,model:e.model.trim(),temperature:e.temperature},missing:[]}}buildChatCompletionsUrl(e){return this.buildChatCompletionsCandidateUrls(e)[0]??e.trim().replace(/\/+$/,"")}getChatCompletionsCandidateUrls(e){if("custom"!==e.kind)return[this.buildChatCompletionsUrl(e.baseUrl)];const t=this.getCustomProviderCacheKey(e.baseUrl,e.apiFormat),o=this.customChatCompletionsUrlCache.get(t),n="ollama"===e.apiFormat?this.buildOllamaCandidateUrls(e.baseUrl):this.buildChatCompletionsCandidateUrls(e.baseUrl);return o?[o,...n.filter(e=>e!==o)]:n}buildChatCompletionsCandidateUrls(e){const t=this.normalizeProviderBaseUrl(e);if(!t)return[];if(/\/chat\/completions$/i.test(t))return[t];if(/\/v\d+$/i.test(t))return[`${t}/chat/completions`];const o=`${t}/chat/completions`,n=`${t}/v1/chat/completions`,i=`${t}/v3/chat/completions`;return this.hasPathSegment(t)?[o,n,i]:[n,o,i]}buildOllamaCandidateUrls(e){const t=this.normalizeProviderBaseUrl(e);return t?/\/api\/(chat|generate)$/i.test(t)?[t]:/\/api$/i.test(t)?[`${t}/chat`,`${t}/generate`]:[`${t}/api/chat`,`${t}/api/generate`]:[]}buildOllamaTagsCandidateUrls(e){const t=this.normalizeProviderBaseUrl(e);return t?/\/api\/tags$/i.test(t)?[t]:/\/api\/(chat|generate)$/i.test(t)?[t.replace(/\/(chat|generate)$/i,"/tags")]:/\/api$/i.test(t)?[`${t}/tags`]:[`${t}/api/tags`]:[]}buildRequestHeaders(e){const t={"Content-Type":"application/json"};return e.trim()&&(t.Authorization=`Bearer ${e}`),t}normalizeProviderBaseUrl(e){return e.trim().replace(/\/+$/,"")}hasPathSegment(e){try{const{pathname:t}=new URL(e),o=t.replace(/\/+$/,"");return o.length>0&&"/"!==o}catch{return!1}}getCustomProviderCacheKey(e,t){return`${t}:${this.normalizeProviderBaseUrl(e)}`}rememberCustomChatCompletionsUrl(e,t,o){this.customChatCompletionsUrlCache.set(this.getCustomProviderCacheKey(e,t),o)}isRetryableEndpointError(e){const t=Ho(e);if(404===t||405===t||410===t)return!0;if(400===t||422===t){const t=Wo(e).toLowerCase();return/not found|no route|unknown path|unknown endpoint|invalid url|invalid path|unsupported path|unsupported endpoint/.test(t)}return!1}createRequestResponseError(e,t){const o=e.json?.error?.message||e.json?.message||e.text||`Request failed with status ${e.status} (${t})`,n=new Error("string"==typeof o?o.trim():String(o));return n.status=e.status,n.response=e,n}extractOllamaModelNames(e){const t=(Array.isArray(e?.models)?e.models:[]).map(e=>"string"==typeof e?.name&&e.name.trim()?e.name.trim():"string"==typeof e?.model&&e.model.trim()?e.model.trim():"").filter(e=>!!e);return Array.from(new Set(t)).sort((e,t)=>e.localeCompare(t))}async rethrowUserFacingRequestError(e,t){if(await this.isPKMerQuotaError(e,t))throw new Vo(v("PKMer AI request failed because your quota is insufficient. Please get more quota in PKMer and try again."));throw e}async isPKMerQuotaError(e,t){if(!/pkmer\.cn/i.test(t))return!1;if(403!==Ho(e))return!1;const o=await this.authService.refreshQuota().catch(()=>null),n=o?.quota??o?.remainingQuota;if("number"==typeof n){if(Number((n/5e5).toFixed(2))<=1)return!0}const i=Wo(e).toLowerCase();return/quota|credit|balance|insufficient|额度|点数|余量不足|余额不足/.test(i)}buildCompletionMessages(e,t,o,n){return[{role:"system",content:"You are an inline writing assistant inside Obsidian. Continue the text exactly at the cursor using only the local context around the cursor. Return only the continuation text, with no explanations, no markdown fences, and no quotes. Produce one complete semantic unit that fits the local structure, such as one full sentence, one complete bullet item, one heading continuation, or one short paragraph. Never stop mid-word, mid-sentence, or mid-list-item."},{role:"user",content:`Continue the text at . Use the local cursor context below, not the whole document.${n?"\n\nA draft continuation has already been generated. Continue exactly from where that draft ends. Do not repeat or paraphrase any previously generated text.":""}\n\nText before cursor:\n${`${e}${n}`}\n\n\n\nText after cursor:\n${t}${o}`}]}resolveRewriteScene(e){return e.artifactKind?"artifact":"explain"===e.instruction||"summarize"===e.instruction||"custom"===e.instruction?"reasoning":"rewrite"}buildRewriteMessages(e){const t=this.buildRewriteInstruction(e.instruction,e.customPrompt),o=e.selectedText.trim().length>0,n=e.context?`\n\nLocal document context:\n${e.context}`:"",i=this.buildRewriteSystemPrompt(e.artifactKind),a=this.buildRewriteOutputRequirement(e.artifactKind);return[{role:"system",content:i},{role:"user",content:o?`Instruction:\n${t}\n\nTarget text:\n${e.selectedText}${n}\n\nRequirements:\n- Use the local context only to keep terminology, links, and structure consistent.\n- ${a}`:`Instruction:\n${t}\n\nTarget text:\n(none - use the local cursor context)${n}\n\nRequirements:\n- There is no explicit selection, so generate text that fits naturally at the cursor location.\n- ${a}`}]}buildRewriteSystemPrompt(e){return"canvas"===e?"You are an expert Obsidian Canvas generator. Generate production-ready JSON Canvas files that obey the JSON Canvas structure exactly. Return only valid JSON, never explanations, never markdown fences, and never partial structures. The output must be directly saveable as a .canvas file and open cleanly in Obsidian. Ensure nodes and edges are coherent, spatially organized, and semantically useful rather than generic placeholders.":"base"===e?"You are an expert Obsidian Bases generator. Generate production-ready .base YAML files that obey Obsidian Bases conventions. Return only valid YAML, never explanations, never markdown fences, and never partial structures. The output must be directly saveable as a .base file and open cleanly in Obsidian. Prefer practical fields, formulas, and views over placeholder content.":"frontmatter"===e?"You are an expert Obsidian note metadata assistant. Generate concise, practical YAML frontmatter for the current note. Return only one complete YAML frontmatter block wrapped by --- lines, never explanations, never markdown fences, and never partial YAML. Prefer real-world note properties that help filtering, browsing, and vault consistency. Follow existing folder conventions when they are provided in context.":"You are an editor assistant inside Obsidian. Rewrite, transform, or generate text according to the instruction. Preserve valid Obsidian Flavored Markdown whenever possible, including wikilinks, embeds, callouts, task lists, tables, properties, and inline formatting unless the instruction explicitly changes the format. Return only the final text, with no explanations, no markdown fences, and no surrounding quotes. Never stop mid-list-item, mid-table-row, mid-YAML block, or mid-JSON structure."}buildRewriteOutputRequirement(e){return"canvas"===e?"Return one complete JSON Canvas document with top-level nodes and edges arrays only. Make it visually useful, not minimal.":"base"===e?"Return one complete .base YAML document only. Make it practical and directly usable in Obsidian.":"frontmatter"===e?"Return one complete YAML frontmatter block only, wrapped in --- delimiters, ready to insert at the top of the note.":"Return only the final text ready to paste into the note."}getCompletionTokenBudget(e,t,o,n){if(n>0)return 96;const i=e.trimEnd(),a=t.trimStart(),s=i.split("\n").pop()??"";return/^\s*#{1,6}\s*$/.test(s)?48:/^\s*(?:[-*+] |\d+\. |> )?$/.test(s)?96:!a||o.includes("\n")?192:160}removeRepeatedPrefix(e,t){const o=t;if(!e)return t;if(!o.trim())return"";for(let t=Math.min(e.length,o.length,120);t>0;t--)if(e.slice(-t)===o.slice(0,t))return o.slice(t);return o}looksSemanticallyComplete(e){const t=e.trimEnd();if(!t)return!1;if(/[。!?.!?…'")】》]$/.test(t))return!0;const o=t.split("\n").pop()??"";return!!(/^\s*(?:[-*+] |\d+\. |> )/.test(o)&&o.trim().length>=8)}extractText(e){const t=e?.choices?.[0]?.message?.content??e?.choices?.[0]?.text??e?.message?.content??e?.response??"";return"string"==typeof t?t.trim():Array.isArray(t)?t.map(e=>"string"==typeof e?e:e?.text||e?.content||"").join("").trim():""}trimCompletionToBoundary(e){const t=e.trim();if(!t)return t;const o=Array.from(t.matchAll(/[。!?.!?;;:\n]/g));if(o.length>0){const e=o[o.length-1],n=(e.index??-1)+e[0].length;if(n>=Math.max(12,Math.floor(.45*t.length)))return t.slice(0,n).trimEnd()}const n=t.lastIndexOf(" ");return n>=Math.max(12,Math.floor(.6*t.length))?t.slice(0,n).trimEnd():t}async*streamText(e,t){const o=e.split(/(\s+)/).filter(e=>e.length>0);for(const e of o){if(t?.aborted)throw new DOMException("Aborted","AbortError");yield e,await this.delay(18,t)}}delay(e,t){return new Promise((o,n)=>{if(t?.aborted)return void n(new DOMException("Aborted","AbortError"));const i=window.setTimeout(o,e);t?.addEventListener("abort",()=>{window.clearTimeout(i),n(new DOMException("Aborted","AbortError"))},{once:!0})})}buildRewriteInstruction(e,t){switch(e){case"improve":return"Improve clarity, fluency, and structure while keeping the original meaning.";case"fix-grammar":return"Fix spelling, grammar, and punctuation issues.";case"make-shorter":return"Make the text shorter and more concise.";case"make-longer":return"Expand the text with useful supporting detail.";case"simplify":return"Rewrite the text in simpler language.";case"professional":return"Rewrite the text in a professional tone.";case"casual":return"Rewrite the text in a casual tone.";case"translate-en":return"Translate the text into English.";case"translate-zh":return"Translate the text into Simplified Chinese.";case"translate-ja":return"Translate the text into Japanese.";case"translate-de":return"Translate the text into German.";case"translate-fr":return"Translate the text into French.";case"translate-es":return"Translate the text into Spanish.";case"explain":return"Explain the text in a clear, structured way.";case"summarize":return"Summarize the text while preserving the key points.";case"continue":return"Continue writing in a consistent tone and style.";case"custom":return t?.trim()||"Rewrite the selected text.";default:return"Rewrite the selected text."}}}const gn=new Set(["text","file","link","group"]),fn=new Set(["top","right","bottom","left"]),bn=new Set(["none","arrow"]);function yn(e){const t=vn(e.content);if(!t)throw new Error(v("AI generated content is empty."));return"canvas"===e.kind?function(e,t){let o;try{o=JSON.parse(e)}catch{throw new Error(v("AI returned invalid Canvas JSON."))}if(!o||"object"!=typeof o)throw new Error(v("AI returned invalid Canvas JSON."));const n=o,i=new Set,a=Array.isArray(n.nodes)?n.nodes:[],s=Array.isArray(n.edges)?n.edges:[];let r=a.map((e,o)=>function(e,t,o,n){if(!e||"object"!=typeof e)return null;const i=e,a=gn.has(i.type)?i.type:"text",s=Mn(i.id,t),r="string"==typeof i.color&&i.color.trim()?i.color.trim():void 0;if("group"===a)return{id:s,type:a,x:Nn(i.x,60*n-40),y:Nn(i.y,-60),width:Dn(i.width,680,240,2800),height:Dn(i.height,420,160,2200),label:"string"==typeof i.label?i.label.trim():Tn(o),color:r,background:"string"==typeof i.background?i.background:void 0,backgroundStyle:"string"==typeof i.backgroundStyle?i.backgroundStyle:void 0};if("file"===a&&"string"==typeof i.file&&i.file.trim())return{id:s,type:a,x:Nn(i.x,0),y:Nn(i.y,0),width:Dn(i.width,360,260,560),height:Dn(i.height,240,160,420),file:i.file.trim(),color:r};if("link"===a&&"string"==typeof i.url&&i.url.trim())return{id:s,type:a,x:Nn(i.x,0),y:Nn(i.y,0),width:Dn(i.width,320,240,520),height:Dn(i.height,180,120,320),url:i.url.trim(),color:r};const l=function(e,t,o){const n=[e.text,e.label,e.title,e.name,e.file,e.url];for(const e of n)if("string"==typeof e&&e.trim())return e.trim().replace(/\\n/g,"\n");const i=t.replace(/\r\n?/g,"\n").split("\n").map(e=>e.trim()).filter(Boolean);if(i[o])return i[o];return Sn(t)}(i,o,n),c=En(l);return{id:s,type:"text",x:Nn(i.x,0),y:Nn(i.y,0),width:Dn(i.width,c.width,220,620),height:Dn(i.height,c.height,90,420),text:l,color:r}}(e,i,t,o)).filter(e=>null!==e);0===r.length&&(r=[kn(i,t)]);let l=s.map(e=>function(e,t,o){if(!e||"object"!=typeof e)return null;const n=e,i="string"==typeof n.fromNode?n.fromNode.trim():"",a="string"==typeof n.toNode?n.toNode.trim():"";if(!o.has(i)||!o.has(a)||i===a)return null;return{id:Mn(n.id,t),fromNode:i,toNode:a,fromSide:An(n.fromSide,fn),toSide:An(n.toSide,fn),fromEnd:An(n.fromEnd,bn),toEnd:An(n.toEnd,bn)??"arrow",color:"string"==typeof n.color&&n.color.trim()?n.color.trim():void 0,label:"string"==typeof n.label&&n.label.trim()?n.label.trim():void 0}}(e,i,new Set(r.map(e=>e.id)))).filter(e=>null!==e);0===l.length&&r.filter(e=>"group"!==e.type).length>2&&(l=function(e,t){const o=e.filter(e=>"group"!==e.type);if(o.length<2)return[];const[n,...i]=o;return i.map(e=>({id:Mn(void 0,t),fromNode:n.id,toNode:e.id,toEnd:"arrow"}))}(r,i));const c=function(e,t,o){const n=e.nodes.filter(e=>"group"!==e.type),i=e.nodes.filter(e=>"group"===e.type),a=new Map,s=new Map;for(const e of n)a.set(e.id,new Set),s.set(e.id,0);for(const t of e.edges)a.has(t.fromNode)&&a.has(t.toNode)&&(a.get(t.fromNode)?.add(t.toNode),a.get(t.toNode)?.add(t.fromNode),s.set(t.fromNode,(s.get(t.fromNode)??0)+1),s.set(t.toNode,(s.get(t.toNode)??0)+1));const r=function(e,t,o){if(0===e.length)return null;const n=e.find(e=>(e.text||"").trim().startsWith("# "));if(n)return n;const i=Tn(o).toLowerCase(),a=e.find(e=>(e.text||"").toLowerCase().includes(i));if(a)return a;return[...e].sort((e,o)=>{const n=(t.get(o.id)??0)-(t.get(e.id)??0);return 0!==n?n:(o.text||"").length-(e.text||"").length})[0]}(n,s,t),l=function(e,t,o){if(0===e.length)return[];const n=new Map(e.map(e=>[e.id,e])),i=new Set,a=o?[o,...e.map(e=>e.id).filter(e=>e!==o)]:e.map(e=>e.id),s=[];for(const e of a){if(i.has(e)||!n.has(e))continue;const o=[{id:e,depth:0===s.length?0:s.length}];for(i.add(e);o.length>0;){const e=o.shift();s[e.depth]||(s[e.depth]=[]),s[e.depth].push(n.get(e.id));[...t.get(e.id)??[]].filter(e=>!i.has(e)).sort((e,t)=>(n.get(t)?.text||"").length-(n.get(e)?.text||"").length).forEach(t=>{i.add(t),o.push({id:t,depth:e.depth+1})})}}return s.filter(e=>e&&e.length>0)}(n,a,r?.id),c=420;l.forEach((e,t)=>{const o=e.reduce((e,t)=>e+t.height,0)+42*Math.max(0,e.length-1);let n=-Math.round(o/2);e.forEach(e=>{e.x=t*c,e.y=n,n+=e.height+42})}),1===n.length&&(n[0].x=0,n[0].y=0);const d=function(e,t,o,n){if(0===t.length)return e;const i=function(e){const t=Math.min(...e.map(e=>e.x)),o=Math.min(...e.map(e=>e.y)),n=Math.max(...e.map(e=>e.x+e.width)),i=Math.max(...e.map(e=>e.y+e.height));return{x:t,y:o,width:n-t,height:i-o}}(t);if(0===e.length)return[{id:Mn(void 0,o),type:"group",x:i.x-70,y:i.y-70,width:i.width+140,height:i.height+140,label:Tn(n),color:"4"}];const[a,...s]=e;return a.x=i.x-70,a.y=i.y-70,a.width=i.width+140,a.height=i.height+140,a.label=a.label||Tn(n),s.forEach((e,t)=>{e.x=i.x-70+24*(t+1),e.y=i.y-70+24*(t+1),e.width=Math.max(240,i.width+90-18*(t+1)),e.height=Math.max(180,i.height+90-18*(t+1))}),e}(i,n,o,t);return{nodes:[...d,...n],edges:e.edges}}({nodes:r,edges:l},t,i);return`${JSON.stringify(c,null,2)}\n`}(t,e.sourceText):function(e,t){const o=e.replace(/\r\n?/g,"\n").replace(/\t/g," ").trim();if(!/^(filters|formulas|properties|summaries|views):/m.test(o))return`${function(e){const t=Tn(e);return["filters:"," and: []","properties:"," file.name:",' displayName: "Name"'," file.folder:",' displayName: "Folder"',"views:"," - type: table",` name: "${xn(t)}"`," order:"," - file.name"," - file.folder"].join("\n")}(t)}\n`;let n=o;/^views:/m.test(n)?/^\s*-\s+type:/m.test(n)||(n=n.replace(/^views:\s*$/m,`views:\n${function(e,t){const o=" ".repeat(t);return e.split("\n").map(e=>`${o}${e}`).join("\n")}(wn(),2)}`)):n=`${n}\n\n${["views:",...wn().split("\n")].join("\n")}`;/^\s+order:\s*$/m.test(n)||(n=n.replace(/(^\s*-\s+type:\s+[\w-]+\s*\n\s+name:\s+.+$)/m,"$1\n order:\n - file.name"));return`${n.trim()}\n`}(t,e.sourceText)}function Cn(e,t={}){const o=vn(e);if(!o)throw new Error(v("AI generated content is empty."));const n=o.replace(/^---\s*\n?/i,"").replace(/\n?---\s*$/i,"").trim();if(!n)throw new Error(v("AI generated content is empty."));const i=function(e,t){if(!t||0===Object.keys(t).length)return e;const o=e.replace(/\r\n?/g,"\n").split("\n"),n=new Set(o.map(e=>e.match(/^([A-Za-z0-9_-]+):(?:\s|$)/)?.[1]).filter(e=>!!e));return o.map(e=>{const o=e.match(/^([A-Za-z0-9_-]+):(.*)$/);if(!o)return e;const[,i,a]=o,s=t[i];return s&&s!==i?n.has(s)?e:`${s}:${a}`:e}).join("\n").trim()}(n,t);return`---\n${i}\n---\n\n`}function vn(e){return e.trim().replace(/^```(?:json|yaml|yml)?\s*/i,"").replace(/\s*```\s*$/i,"").trim()}function wn(){return["- type: table",' name: "Overview"'," order:"," - file.name"].join("\n")}function xn(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function kn(e,t){const o=Sn(t),n=En(o);return{id:Mn(void 0,e),type:"text",x:0,y:0,width:n.width,height:n.height,text:o}}function Sn(e){const t=e.replace(/\r\n?/g,"\n").split("\n").map(e=>e.trim()).filter(Boolean).slice(0,6);if(0===t.length)return"# Canvas\n\n- Key point";const[o,...n]=t,i=n.slice(0,4).map(e=>`- ${e.replace(/^[-*+]\s+/,"")}`);return[`# ${In(o,48)}`,i.length?"":null,...i].filter(e=>null!==e).join("\n")}function Tn(e){return In(e.replace(/\r\n?/g,"\n").split("\n").map(e=>e.trim()).find(Boolean)||v("Untitled"),42)}function En(e){const t=e.split("\n"),o=t.reduce((e,t)=>Math.max(e,t.length),0);return{width:Math.min(520,Math.max(260,180+6*o)),height:Math.min(320,Math.max(110,70+28*t.length))}}function In(e,t){const o=e.trim();return o.length<=t?o:`${o.slice(0,Math.max(0,t-1)).trimEnd()}…`}function An(e,t){if("string"!=typeof e)return;const o=e.trim();return t.has(o)?o:void 0}function Mn(e,t){if("string"==typeof e){const o=e.trim().toLowerCase();if(/^[0-9a-f]{16}$/.test(o)&&!t.has(o))return t.add(o),o}let o="";do{o=Pn()}while(t.has(o));return t.add(o),o}function Pn(){const e=new Uint8Array(8);if("undefined"!=typeof crypto&&"function"==typeof crypto.getRandomValues)crypto.getRandomValues(e);else for(let t=0;te.toString(16).padStart(2,"0")).join("")}function Nn(e,t){return"number"==typeof e&&Number.isFinite(e)?e:t}function Dn(e,t,o,n){const i=Nn(e,t);return Math.max(o,Math.min(n,Math.round(i)))}function Ln(e){const t=e.app.workspace.activeLeaf?.view;return function(e){return!!e&&"canvas"===e.getViewType?.()&&"function"==typeof e.getViewData&&"function"==typeof e.setViewData}(t)?t:null}async function Fn(e,t={}){const o=Ln(e);if(!o)throw new Error(v("Canvas AI requires an active Canvas file."));const n=o.file??e.app.workspace.getActiveFile();if(!n||"canvas"!==n.extension)throw new Error(v("Canvas AI requires an active Canvas file."));const i=function(e){let t;try{t=JSON.parse(e)}catch{throw new Error(v("Failed to read the current canvas data."))}if(!t||"object"!=typeof t)throw new Error(v("Failed to read the current canvas data."));const o=t;return{nodes:Array.isArray(o.nodes)?o.nodes.map(e=>function(e){if(!e||"object"!=typeof e)return null;const t=e,o="string"==typeof t.id?t.id.trim():"",n=function(e){if("text"===e||"file"===e||"link"===e||"group"===e)return e;return null}(t.type);if(!o||!n)return null;return{id:o,type:n,x:vi(t.x,0),y:vi(t.y,0),width:vi(t.width,320),height:vi(t.height,180),text:"string"==typeof t.text?t.text:void 0,file:"string"==typeof t.file?t.file:void 0,url:"string"==typeof t.url?t.url:void 0,label:"string"==typeof t.label?t.label:void 0,color:"string"==typeof t.color?t.color:void 0,background:"string"==typeof t.background?t.background:void 0,backgroundStyle:"string"==typeof t.backgroundStyle?t.backgroundStyle:void 0}}(e)).filter(e=>null!==e):[],edges:Array.isArray(o.edges)?o.edges.map(e=>function(e){if(!e||"object"!=typeof e)return null;const t=e,o="string"==typeof t.id?t.id.trim():"",n="string"==typeof t.fromNode?t.fromNode.trim():"",i="string"==typeof t.toNode?t.toNode.trim():"";if(!o||!n||!i)return null;return{id:o,fromNode:n,toNode:i,fromSide:"string"==typeof t.fromSide?t.fromSide:void 0,toSide:"string"==typeof t.toSide?t.toSide:void 0,fromEnd:"string"==typeof t.fromEnd?t.fromEnd:void 0,toEnd:"string"==typeof t.toEnd?t.toEnd:void 0,color:"string"==typeof t.color?t.color:void 0,label:"string"==typeof t.label?t.label:void 0}}(e)).filter(e=>null!==e):[]}}(o.getViewData()),a=new Map(i.nodes.map(e=>[e.id,e])),s=function(e,t){const o=new Set(t.nodes.map(e=>e.id)),n=new Set,i=e,a=Ci(i.canvas);gi(a?.selection,n,o),gi(Ci(a?.selection)?.nodes,n,o),gi(Ci(a?.selection)?.items,n,o),gi(Ci(i.selection)?.nodes,n,o),gi(a?.selectedNodes,n,o),gi(i.selectedNodes,n,o);return[a?.nodes,a?.nodeMap,i.nodes,i.nodeMap].forEach(e=>{fi(e,e=>{const t=Ci(e),i=bi(t,o);if(!i)return;[t.selected,t.isSelected,Ci(t.nodeEl)?.classList,Ci(t.el)?.classList].some(e=>!0===e)&&n.add(i)})}),function(e,t){const o=e.containerEl;if(!o)return[];const n=[".canvas-node.is-selected",".canvas-node.mod-selected",".canvas-node.is-focused",".canvas-node.mod-focused"],i=new Set;return o.querySelectorAll(n.join(",")).forEach(e=>{let o=e;for(;o;){const e=yi(o,t);if(e){i.add(e);break}o=o.parentElement}}),[...i]}(e,o).forEach(e=>n.add(e)),[...n]}(o,i),r=function(e,t){for(const o of t){const t=e.get(o);if(t&&"group"!==t.type)return t}const o=[...e.values()].filter(e=>"group"!==e.type);if(1===o.length)return o[0];return null}(a,s);if(!r&&!1!==t.requireAnchor)throw new Error(v("Please select a canvas node first."));const l=await async function(e,t,o,n){if(0===o.length)return[];const i=new Map(t.nodes.map(e=>[e.id,e])),a=n?[n,...o.filter(e=>e!==n)]:o,s=[];for(const t of a){const o=i.get(t);if(o&&"group"!==o.type&&(s.push(await _n(e,o)),s.length>=6))break}return s}(e,i,s,r?.id??null),c=r?await async function(e,t,o){const n=new Map(t.nodes.map(e=>[e.id,e])),i=new Set;for(const e of t.edges)e.fromNode===o?i.add(e.toNode):e.toNode===o&&i.add(e.fromNode);const a=[...i].map(e=>n.get(e)).filter(e=>!!e&&"group"!==e.type).slice(0,10),s=[];for(const t of a)s.push(await _n(e,t));return s}(e,i,r.id):[],d=r?await _n(e,r):null,u=function(e,t,o,n,i,a){const s=t.nodes.filter(e=>"group"!==e.type),r=s.filter(e=>!o||e.id!==o.id&&!a.some(t=>t.id===e.id)).slice(0,10).map(e=>Wn({id:e.id,type:e.type,text:Un(e.text||e.label||e.file||e.url||""),node:e})),l=a.length>0?a.map((e,t)=>`${t+1}. ${Wn(e)}`).join("\n"):"(none)",c=i.length>0?i.map((e,t)=>`${t+1}. ${Wn(e)}`).join("\n"):"(none)",d=(o?t.edges.filter(e=>e.fromNode===o.id||e.toNode===o.id).slice(0,12):t.edges.slice(0,16)).map(e=>function(e,t){if(t&&(e.fromNode===t||e.toNode===t)){return`- ${e.fromNode===t?"outgoing":"incoming"} ${e.fromNode===t?e.toNode:e.fromNode}${e.label?` [${e.label}]`:""}`}return`- ${e.fromNode} -> ${e.toNode}${e.label?` [${e.label}]`:""}`}(e,o?.id)).join("\n")||"(none)";return[`canvas_file: ${e.path}`,"focus_mode: "+(o?"selected-node":"board-wide"),`selected_node_ids: ${n.join(", ")||"(none)"}`,`board_node_count: ${s.length}`,"selected_nodes:",c,"anchor_node:",o?Wn(o):"(none selected)",o?"anchor_connections:":"sample_board_connections:",d,"neighboring_nodes:",o?l:"(none)",o?"other_board_nodes:":"board_nodes:",r.length>0?r.join("\n"):"(none)"].join("\n\n")}(n,i,d,s,l,c);return{view:o,file:n,document:i,anchorNode:d,selectedNodeIds:s,selectedNodes:l,neighboringNodes:c,contextText:u}}async function On(e,t,o={}){const n=t.document.nodes.filter(e=>"group"!==e.type),i=t.selectedNodeIds.filter(e=>n.some(t=>t.id===e)),a=o.scopeMode??"auto",s=("selection"===a||"board"!==a)&&i.length>0,r=s?i:n.map(e=>e.id),l=function(e,t){const o=new Map(e.nodes.map(e=>[e.id,e])),n=new Set(t),i=new Map,a=new Map;t.forEach(e=>{i.set(e,[]),a.set(e,0)}),e.edges.forEach(e=>{n.has(e.fromNode)&&n.has(e.toNode)&&(i.get(e.fromNode)?.push(e.toNode),a.set(e.toNode,(a.get(e.toNode)??0)+1))}),i.forEach((e,t)=>{e.sort((e,t)=>Vn(o.get(e),o.get(t))),i.set(t,Array.from(new Set(e)))});const s=t.filter(e=>0===(a.get(e)??0)).sort((e,t)=>Vn(o.get(e),o.get(t))),r=t.slice().sort((e,t)=>Vn(o.get(e),o.get(t))),l=new Set,c=[],d=e=>{l.has(e)||(l.add(e),c.push(e),(i.get(e)??[]).forEach(e=>d(e)))};return(s.length>0?s:r).forEach(e=>d(e)),r.forEach(e=>d(e)),c}(t.document,r),c=l.map(e=>t.document.nodes.find(t=>t.id===e)).filter(e=>!!e),d=c.slice(0,48),u=new Set(d.map(e=>e.id)),m=[];for(const[o,n]of d.entries()){const i=await _n(e,n),a=t.document.edges.filter(e=>e.fromNode===n.id&&u.has(e.toNode)).map(e=>`- ${e.toNode}${e.label?` [${e.label}]`:""}`).slice(0,8),s=t.document.edges.filter(e=>e.toNode===n.id&&u.has(e.fromNode)).map(e=>`- ${e.fromNode}${e.label?` [${e.label}]`:""}`).slice(0,8);m.push([`node_index: ${o+1}`,Wn(i),`incoming_links:\n${s.join("\n")||"(none)"}`,`outgoing_links:\n${a.join("\n")||"(none)"}`].join("\n"))}const p=c.length-d.length,h=["document_scope: "+(s?"selected-nodes":"whole-canvas"),`document_node_count: ${c.length}`,"ordered_canvas_nodes:",m.join("\n\n")];return p>0&&h.push(`truncated_nodes: ${p}`),{scope:s?"selection":"board",nodeCount:c.length,text:h.join("\n\n")}}function Rn(e,t){const o=e.document.nodes.filter(e=>"group"!==e.type),n=function(e,t){const o=new Set(t.map(e=>e.id)),n=e.selectedNodeIds.filter(e=>o.has(e));return n.length>0?n:t.map(e=>e.id)}(e,o);if(0===n.length)return{addNodes:[],addEdges:[],layoutNodes:[],replaceExistingEdges:!1};const i=new Map(o.filter(e=>n.includes(e.id)).map(e=>[e.id,e])),a=new Set(n),s=e.document.edges.filter(e=>a.has(e.fromNode)&&a.has(e.toNode)&&e.fromNode!==e.toNode),r=function(e,t,o){const n=e.toLowerCase();if(/(mind\s*map|radial|中心主题|脑图|思维导图|发散)/i.test(n))return"mindmap";if(/(^|\b)(tree|hierarchy|org\s*chart)(\b|$)|树形|树状|层级|父子|组织结构/i.test(n))return"tree";if(/(dag|directed\s+acyclic|flow\s*chart|pipeline|dependency|依赖图|流程图|有向无环|拓扑)/i.test(n))return"dag";const i=Math.max(t.length,1),a=o.length/i,s=t.some(e=>o.filter(t=>t.toNode===e).length>1);if(s||a>1.15)return"dag";return"tree"}(t||e.contextText,n,s),l=new Map,c=new Map;n.forEach(e=>{l.set(e,[]),c.set(e,[])}),s.forEach(e=>{const t=l.get(e.fromNode);t&&!t.includes(e.toNode)&&t.push(e.toNode);const o=c.get(e.toNode);o&&!o.includes(e.fromNode)&&o.push(e.fromNode)}),l.forEach((e,t)=>{l.set(t,e.sort((e,t)=>Vn(i.get(e),i.get(t))))}),c.forEach((e,t)=>{c.set(t,e.sort((e,t)=>Vn(i.get(e),i.get(t))))});const d=function(e,t,o){const n=new Map;e.forEach(e=>{n.set(e,new Set)}),t.forEach(e=>{n.get(e.fromNode)?.add(e.toNode),n.get(e.toNode)?.add(e.fromNode)});const i=e.slice().sort((e,t)=>Vn(o.get(e),o.get(t))),a=new Set,s=[];return i.forEach(e=>{if(a.has(e))return;const t=[e],i=[];for(a.add(e);t.length>0;){const e=t.shift();e&&(i.push(e),n.get(e)?.forEach(e=>{a.has(e)||(a.add(e),t.push(e))}))}i.sort((e,t)=>Vn(o.get(e),o.get(t))),s.push(i)}),s.sort((e,t)=>Vn(o.get(e[0]),o.get(t[0])))}(n,s,i),u=[];let m=0;return d.forEach((t,o)=>{const n=function(e,t,o,n,i,a){const s=new Set(e),r=e.slice().sort((e,o)=>Vn(t.get(e),t.get(o))),l=r.filter(e=>(n.get(e)??[]).every(e=>!s.has(e))),c=(l.length>0?l:r.slice(0,1)).slice();if(a&&s.has(a)){const e=c.indexOf(a);e>=0?(c.splice(e,1),c.unshift(a)):0===l.length&&c.unshift(a)}const d=new Map;e.forEach(e=>{d.set(e,[])});const u=new Map,m=new Set,p=[],h=(e,n)=>{m.add(e),u.set(e,n);const i=(o.get(e)??[]).filter(e=>s.has(e)&&!m.has(e)).sort((e,o)=>Vn(t.get(e),t.get(o)));d.set(e,i),i.forEach(e=>h(e,n+1))};c.forEach(e=>{m.has(e)||(p.push(e),h(e,0))}),r.forEach(e=>{m.has(e)||(p.push(e),h(e,0))});const g=new Map,f=e=>{const t=g.get(e);if(void 0!==t)return t;const o=d.get(e)??[];if(0===o.length)return g.set(e,2),2;const n=o.reduce((e,t)=>e+f(t),0),i=Math.max(2,n+1*Math.max(0,o.length-1));return g.set(e,i),i},b=[],y=(e,t)=>{const o=f(e),n=t+Math.floor((o-1)/2),i=u.get(e)??0;b.push({nodeId:e,column:i,row:n});let a=t;(d.get(e)??[]).forEach(e=>{y(e,a),a+=f(e)+1})};let C=0;p.forEach((e,t)=>{y(e,C),C+=f(e),te.nodeId===t);if(!o)return e;const n=e.filter(e=>e.column===o.column+1).sort((e,t)=>e.row-t.row||e.nodeId.localeCompare(t.nodeId));if(n.length<3)return e;const i=new Set(n.filter((e,t)=>t%2==1).map(e=>e.nodeId));if(0===i.size)return e;const a=new Map;return n.forEach(e=>{a.set(e.row,i.has(e.nodeId)?"left":"right")}),Hn(e.map(e=>{if(e.nodeId===t||e.column<=o.column)return e;const n=[...a.keys()].sort((t,o)=>Math.abs(t-e.row)-Math.abs(o-e.row))[0];return"right"===(a.get(n)??"right")?e:{...e,column:o.column-(e.column-o.column)}}))}(v,c[0]):v}(t,i,l,c,r,e.anchorNode?.id??null),a=n.reduce((e,t)=>Math.max(e,t.row),0);n.forEach(e=>{u.push({...e,row:e.row+m})}),oKn(e)).filter(e=>null!==e).slice(0,8):function(e){const t=Ci(e);if(!t)return[];const o=[t.addNodes,t.nodes,t.items,t.children];for(const e of o)if(Array.isArray(e))return e;return[]}(o).map(e=>Kn(e)).filter(e=>null!==e).slice(0,8),a=n&&0===i.length?n.map(e=>Gn(e)).filter(e=>null!==e).slice(0,48):[],s=a.length>0?a:function(e){const t=Ci(e);if(!t)return[];const o=[t.layoutNodes,t.layout,t.positions];for(const e of o)if(Array.isArray(e))return e;return[]}(o).map(e=>Gn(e)).filter(e=>null!==e).slice(0,48),r=n&&0===i.length&&0===a.length?n.map(e=>Yn(e)).filter(e=>null!==e).slice(0,12):[],l=r.length>0?r:function(e){const t=Ci(e);if(!t)return[];const o=[t.addEdges,t.edges,t.links];for(const e of o)if(Array.isArray(e))return e;return[]}(o).map(e=>Yn(e)).filter(e=>null!==e).slice(0,12),c=function(e){const t=Ci(e);if(!t)return!1;return function(...e){for(const t of e){if("boolean"==typeof t)return t;if("number"==typeof t&&(0===t||1===t))return 1===t;if("string"==typeof t&&t.trim()){const e=t.trim().toLowerCase();if(["true","yes","1"].includes(e))return!0;if(["false","no","0"].includes(e))return!1}}return}(t.replaceExistingEdges,t.replaceEdges,t.resetEdges)??!1}(o);if(0===i.length&&0===l.length&&0===s.length)throw new Error(v("Canvas AI did not return any usable actions."));return{addNodes:i,addEdges:l,layoutNodes:s,replaceExistingEdges:c}}function Bn(e,t={}){const o=Jn(e);let n;try{n=JSON.parse(o)}catch{throw new Error(v("Canvas AI returned invalid JSON."))}const i=n&&"object"==typeof n?n:null,a=i&&Array.isArray(i.nodes)?i.nodes:[],s=new Set,r=a.map((e,t)=>function(e,t,o){if(!e||"object"!=typeof e)return null;const n=e,i=`n_${t+1}`,a=Xn(n.id,n.nodeId,n.key)||i,s=o.has(a)?i:a;if(o.has(s))return null;o.add(s);const r=Zn(Xn(n.title,n.name,n.label),40),l=Zn(Xn(n.content,n.summary,n.body,n.text),90),c=Qn(n.level,n.depth,n.layer)??0,d=Xn(n.type,n.kind).toLowerCase(),u="detail"===d?"detail":"concept"===d||c<=1?"concept":"detail";if(!r)return null;return{id:s,type:u,title:r,content:l,level:c,group:Zn(Xn(n.group,n.category,n.cluster,n.section),24)||void 0}}(e,t,s)).filter(e=>null!==e).slice(0,24);if(0===r.length)throw new Error(v("Canvas AI did not return any usable nodes."));const l=new Set(r.map(e=>e.id)),c=new Set(t.existingNodeIds??[]);return{nodes:r,edges:(i&&Array.isArray(i.edges)?i.edges:[]).map(e=>function(e,t){if(!e||"object"!=typeof e)return null;const o=e,n=Xn(o.from,o.source,o.parent,o.fromNode),i=Xn(o.to,o.target,o.child,o.toNode),a=Zn(Xn(o.relation,o.label,o.type),24);if(!(n&&i&&n!==i&&t.has(n)&&t.has(i)))return null;return{from:n,to:i,relation:a||void 0}}(e,l)).filter(e=>null!==e).slice(0,48),boardConnections:(i&&Array.isArray(i.boardConnections)?i.boardConnections:[]).map(e=>function(e,t,o){if(!e||"object"!=typeof e)return null;const n=e,i=Xn(n.nodeId,n.from,n.node,n.generatedNodeId),a=Xn(n.existingNodeId,n.toExisting,n.targetNodeId,n.existingId),s=Zn(Xn(n.relation,n.label,n.type),24);if(!(i&&a&&t.has(i)&&o.has(a)))return null;return{nodeId:i,existingNodeId:a,relation:s||void 0}}(e,l,c)).filter(e=>null!==e).slice(0,12)}}async function $n(e,t,o){const n={nodes:t.document.nodes.map(e=>({...e})),edges:t.document.edges.map(e=>({...e}))},i=new Set([...n.nodes.map(e=>e.id),...n.edges.map(e=>e.id)]),a=li(n),s=n.nodes.filter(e=>"group"!==e.type),r=new Map(s.map(e=>[e.id,e])),l=function(e,t){const o=new Map(e.map((e,t)=>[e.id,t])),n=Math.min(...e.map(e=>e.level)),i=e.filter(e=>e.level===n);if(1===i.length)return i[0].id;return i.slice().sort((e,n)=>{const i=ci(e.id,t),a=ci(n.id,t);return a!==i?a-i:(o.get(e.id)??0)-(o.get(n.id)??0)})[0]?.id??e[0].id}(o.nodes,o.edges),c=function(e,t,o){const n=new Map(e.map(e=>[e.id,e])),i=new Map(e.map((e,t)=>[e.id,t])),a=new Map;return a.set(o,null),e.forEach(s=>{if(s.id===o)return;const r=i.get(s.id)??0,l=new Set;t.forEach(e=>{if(e.to===s.id){const t=n.get(e.from);t&&t.level{const o=n.get(e),a=n.get(t);if(!o||!a)return 0;if(a.level!==o.level)return a.level-o.level;const s=Math.abs((i.get(e)??0)-r),l=Math.abs((i.get(t)??0)-r);return s!==l?s-l:(i.get(t)??0)-(i.get(e)??0)})[0];if(c)return void a.set(s.id,c);const d=e.slice(0,r).filter(e=>e.levelt.level!==e.level?t.level-e.level:(i.get(t.id)??0)-(i.get(e.id)??0))[0]?.id??o;a.set(s.id,d)}),a}(o.nodes,o.edges,l),d=function(e,t){const o=new Map,n=new Map(e.map(e=>[e.id,e])),i=new Map(e.map((e,t)=>[e.id,t]));return e.forEach(e=>{o.set(e.id,[])}),t.forEach((e,t)=>{if(!e)return;const n=o.get(e)??[];n.push(t),o.set(e,n)}),o.forEach((e,t)=>{e.sort((e,t)=>{const o=n.get(e),a=n.get(t);return o&&a?o.level!==a.level?o.level-a.level:o.type!==a.type?"concept"===o.type?-1:1:(i.get(e)??0)-(i.get(t)??0):(i.get(e)??0)-(i.get(t)??0)}),o.set(t,e)}),o}(o.nodes,c),u=new Map,m=new Set;o.nodes.forEach(e=>{const t=ai({title:e.title,body:e.content,color:di(e)}),o=function(e){const t=e.split("\n"),o=t.reduce((e,t)=>Math.max(e,t.length),0),n=Math.min(460,Math.max(220,150+5*o)),i=Math.min(280,Math.max(104,64+24*t.length));return{width:n,height:i}}(t),n={id:ri(i),type:"text",x:0,y:0,width:o.width,height:o.height,text:t,color:di(e)};u.set(e.id,n),m.add(n.id)});const p=u.get(l);if(!p)throw new Error(v("Canvas AI did not return any usable nodes."));const h=function(e,t,o){const n=new Map;t.forEach(e=>{n.set(`${e.from}__${e.to}`,e.relation)});const i=[];return e.forEach(e=>{const t=o.get(e.id);if(!t)return;const a=n.get(`${t}__${e.id}`)??n.get(`${e.id}__${t}`)??void 0;i.push({from:t,to:e.id,relation:ui(a)})}),i}(o.nodes,o.edges,c),g=function(e,t,o,n){const i=new Map,a=new Map,s=[],r=e=>{const t=i.get(e);if(void 0!==t)return t;const n=o.get(e);if(!n)return i.set(e,0),0;const a=r(n)+1;return i.set(e,a),a},l=e=>{const t=a.get(e);if(void 0!==t)return t;const o=n.get(e)??[];if(0===o.length)return a.set(e,1),1;const i=Math.max(1,o.reduce((e,t)=>e+l(t),0)+1*Math.max(o.length-1,0));return a.set(e,i),i},c=(e,t)=>{const o=l(e);s.push({nodeId:e,column:r(e),row:t+Math.floor((o-1)/2)});let i=t;(n.get(e)??[]).forEach(e=>{c(e,i),i+=l(e)+1})};c(e,0);const d=new Set(s.map(e=>e.nodeId));let u=l(e)+1;return t.forEach(e=>{d.has(e.id)||(c(e.id,u),s.forEach(e=>{d.add(e.nodeId)}),u+=l(e.id)+1)}),Hn(s)}(l,o.nodes,c,d),f=g.map(e=>{const t=u.get(e.nodeId);return t?{...e,nodeId:t.id}:null}).filter(e=>null!==e);ti({nodes:[...u.values()],edges:[]},f,{columnGap:96,rowGap:64,baseX:0,baseY:0});const b=function(e,t,o){if(0===e.length)return o;const n=new Map;e.forEach(e=>{n.set(e.existingNodeId,(n.get(e.existingNodeId)??0)+1)});const i=[...n.entries()].sort((e,t)=>t[1]!==e[1]?t[1]-e[1]:e[0].localeCompare(t[0]))[0]?.[0];if(!i)return o;return t.get(i)??o}(o.boardConnections,r,a),y=ii(b),C="__canvas_board__"!==b.id,w=s.length>0,x=C?Math.round(b.x+b.width+180+p.width/2):w?Math.round(a.x+260):Math.round(220+p.width/2),k=C?Math.round(y.y):w?Math.round(a.y):Math.round(120+p.height/2),S=ii(p),T=x-S.x,E=k-S.y;u.forEach(e=>{e.x=Math.round(e.x+T),e.y=Math.round(e.y+E)}),function(e,t){if(0===t.length||0===e.size)return;const o=[...e.values()],n=t.map(e=>({x:e.x,y:e.y,width:e.width,height:e.height}));let i=0;for(;o.some(e=>si(e,n))&&i<16;)o.forEach(e=>{e.x+=180}),i+=1}(u,s);const I=function(e,t,o,n,i){const a=function(e,t,o,n){const i=new Map(e.map(e=>[e.id,e])),a=new Map,s=new Map,r=o.get(t)??[];r.forEach(e=>{const t=i.get(e);if(!t)return;const r=function(e,t){const o=[],n=[e];for(;n.length>0;){const e=n.pop();if(!e)continue;o.push(e);const i=t.get(e)??[];for(let e=i.length-1;e>=0;e-=1)n.push(i[e])}return o}(e,o).filter(e=>n.has(e));if(r.length<2)return;const l=t.group?.trim()||t.title.trim();if(!l)return;const c=a.get(l)??[];a.set(l,Array.from(new Set([...c,...r]))),s.set(l,mi(t))});const l=[...a.entries()].map(([e,t])=>({label:e,nodeIds:t,color:s.get(e)})).filter(e=>e.nodeIds.length>1);if(l.length>0)return l;const c=new Map;return e.forEach(e=>{if(e.id===t||!e.group?.trim())return;if(!n.get(e.id))return;const o=e.group.trim(),i=c.get(o)??[];i.push(e.id),c.set(o,i)}),[...c.entries()].map(([e,t])=>({label:e,nodeIds:t,color:mi(i.get(t[0])??null)})).filter(e=>e.nodeIds.length>1)}(e,t,o,n);return a.map(e=>{const t=function(e){const t=Math.min(...e.map(e=>e.x)),o=Math.min(...e.map(e=>e.y)),n=Math.max(...e.map(e=>e.x+e.width)),i=Math.max(...e.map(e=>e.y+e.height));return{x:t,y:o,width:n-t,height:i-o}}(e.nodeIds.map(e=>n.get(e)).filter(e=>!!e));return{id:ri(i),type:"group",x:Math.round(t.x-48),y:Math.round(t.y-64),width:Math.max(260,Math.round(t.width+96)),height:Math.max(180,Math.round(t.height+112)),label:e.label,color:e.color}})}(o.nodes,l,d,u,i);I.forEach(e=>{n.nodes.push(e)}),u.forEach(e=>{n.nodes.push(e)});const A=[];h.forEach(e=>{const t=u.get(e.from),o=u.get(e.to);if(!t||!o||t.id===o.id)return;if(hi(n.edges,t.id,o.id,e.relation))return;const a={id:ri(i),fromNode:t.id,toNode:o.id,toEnd:"arrow",label:e.relation?.trim()||void 0};A.push(a),n.edges.push(a)}),o.boardConnections.forEach(e=>{const t=u.get(e.nodeId),o=r.get(e.existingNodeId);if(!t||!o||t.id===o.id)return;const a=ui(e.relation);if(hi(n.edges,t.id,o.id,a)||hi(n.edges,o.id,t.id,a))return;const s={id:ri(i),fromNode:o.id,toNode:t.id,toEnd:"arrow",label:a};A.push(s),n.edges.push(s)}),oi(n,m);const M=`${JSON.stringify(n,null,2)}\n`;try{t.view.setViewData(M,!1),await t.view.save(!1)}catch{await e.app.vault.modify(t.file,M)}return{addedNodeCount:u.size+I.length,addedEdgeCount:A.length,movedNodeCount:0}}async function _n(e,t){return{id:t.id,type:t.type,text:await zn(e,t),node:t}}async function zn(t,o){if("text"===o.type)return Un(o.text||"");if("file"===o.type){const n=o.file?.trim()||"",i=n?t.app.vault.getAbstractFileByPath(n):null;if(i instanceof e.TFile)try{const e=Un(await t.app.vault.cachedRead(i));return e?`[file] ${i.path}\n${e}`:`[file] ${i.path}`}catch{return`[file] ${n}`}return`[file] ${n||"(missing file path)"}`}if("link"===o.type){return[o.label?.trim(),o.url?.trim()].filter(Boolean).join("\n")}return(o.label||"").trim()}function Un(e){const t=e.trim();return t?cn(t,{previewCharsPerSection:600,maxSections:6,verbatimThreshold:2400}):""}function Vn(e,t){return e||t?e?t?e.x!==t.x?e.x-t.x:e.y!==t.y?e.y-t.y:e.id.localeCompare(t.id):-1:1:0}function Hn(e){return e.slice().sort((e,t)=>e.column!==t.column?e.column-t.column:e.row!==t.row?e.row-t.row:e.nodeId.localeCompare(t.nodeId))}function Wn(e){const t=`(${Math.round(e.node.x)}, ${Math.round(e.node.y)})`,o=`${Math.round(e.node.width)}x${Math.round(e.node.height)}`,n=e.text||"(empty)";return[`id: ${e.id}`,`type: ${e.type}`,`position: ${t}`,`size: ${o}`,"content:",n].join("\n")}function jn(e){if(!e||"object"!=typeof e)return null;const t=e,o=Xn(t.title,t.name,t.heading),n=Xn(t.body,t.summary,t.details,t.text),i=Xn(t.relation,t.edgeLabel,t.label),a=Xn(t.color);let s=o,r=n;if(!s&&r){const e=r.split("\n").map(e=>e.trim()).filter(Boolean);s=e[0]||"",r=e.slice(1).join("\n").trim()}return s?{title:s.trim(),body:r?.trim()||void 0,relation:i?.trim()||void 0,color:a?.trim()||void 0}:null}function Kn(e){const t=jn(e);if(!t)return null;const o=Ci(e),n=Xn(o?.connectTo,o?.targetNode,o?.targetId,o?.attachTo,o?.parentNode);return{...t,connectTo:n||void 0}}function Yn(e){const t=Ci(e);if(!t)return null;const o=Xn(t.fromNode,t.from,t.sourceNode,t.sourceId),n=Xn(t.toNode,t.to,t.targetNode,t.targetId),i=Xn(t.label,t.relation,t.edgeLabel);return o&&n?{fromNode:o,toNode:n,label:i||void 0}:null}function Gn(e){const t=Ci(e);if(!t)return null;const o=Ci(t.position)??Ci(t.layout),n=Xn(t.nodeId,t.id,t.node,t.targetNode,t.targetId,Ci(t.node)?.id),i=Qn(t.column,t.col,t.lane,t.columnIndex,t.xIndex,o?.column,o?.col,o?.lane,o?.columnIndex,o?.xIndex),a=Qn(t.row,t.index,t.order,t.rowIndex,t.yIndex,o?.row,o?.index,o?.order,o?.rowIndex,o?.yIndex);return n&&null!==i&&null!==a?{nodeId:n,column:i,row:a}:null}function Jn(e){return e.trim().replace(/^```(?:json)?\s*/i,"").replace(/\s*```$/i,"").trim()}function Zn(e,t){const o=e.replace(/\s+/g," ").trim();return o?o.length>t?`${o.slice(0,Math.max(t-3,1)).trim()}...`:o:""}function Xn(...e){for(const t of e)if("string"==typeof t&&t.trim())return t.trim();return""}function Qn(...e){for(const t of e){if("number"==typeof t&&Number.isFinite(t))return Math.max(0,Math.round(t));if("string"==typeof t&&t.trim()){const e=Number(t);if(Number.isFinite(e))return Math.max(0,Math.round(e))}}return null}function ei(e,t,o,n){const i=e.nodes.filter(e=>"group"!==e.type).map(e=>({x:e.x,y:e.y,width:e.width,height:e.height})),a=o.map(e=>{const t=ai(e),o=function(e){const t=e.split("\n"),o=t.reduce((e,t)=>Math.max(e,t.length),0),n=Math.min(520,Math.max(280,180+6*o)),i=Math.min(340,Math.max(120,76+28*t.length));return{width:n,height:i}}(t);return{id:ri(n),type:"text",x:0,y:0,width:o.width,height:o.height,text:t,color:e.color}});let s=t.x+t.width+220;const r=a.reduce((e,t)=>e+t.height,0)+48*Math.max(0,a.length-1),l=Math.round(t.y+t.height/2-r/2);for(a.forEach((e,t)=>{const o=a.slice(0,t).reduce((e,t)=>e+t.height,0);e.x=s,e.y=Math.round(l+o+48*t)});a.some(e=>si(e,i));)s+=180,a.forEach(e=>{e.x=s});return a}function ti(e,t,o={}){if(0===t.length)return 0;const n=new Map(e.nodes.filter(e=>"group"!==e.type).map(e=>[e.id,e])),i=new Map;t.forEach(e=>{n.has(e.nodeId)&&i.set(e.nodeId,e)});const a=[...i.values()].map(e=>({layoutNode:e,node:n.get(e.nodeId)??null})).filter(e=>!!e.node);if(0===a.length)return 0;const s=o.columnGap??320,r=o.rowGap??96,l=o.baseX??Math.min(...a.map(e=>e.node.x)),c=o.baseY??Math.min(...a.map(e=>e.node.y)),d=[...new Set(a.map(e=>e.layoutNode.column))].sort((e,t)=>e-t),u=[...new Set(a.map(e=>e.layoutNode.row))].sort((e,t)=>e-t),m=new Map,p=new Map,h=new Map,g=new Map;d.forEach(e=>{h.set(e,Math.max(...a.filter(t=>t.layoutNode.column===e).map(e=>e.node.width)))}),u.forEach(e=>{g.set(e,Math.max(...a.filter(t=>t.layoutNode.row===e).map(e=>e.node.height)))});let f=l;d.forEach((e,t)=>{if(0===t)return void m.set(e,Math.round(f));const o=d[t-1];f+=(h.get(o)??0)+s*Math.max(e-o,1),m.set(e,Math.round(f))});let b=c;u.forEach((e,t)=>{if(0===t)return void p.set(e,Math.round(b));const o=u[t-1];b+=(g.get(o)??0)+r*Math.max(e-o,1),p.set(e,Math.round(b))});let y=0;return a.forEach(e=>{const t=m.get(e.layoutNode.column),o=p.get(e.layoutNode.row);void 0!==t&&void 0!==o&&(e.node.x===t&&e.node.y===o||(e.node.x=t,e.node.y=o,y+=1))}),y}function oi(e,t){const o=new Map(e.nodes.map(e=>[e.id,e]));e.edges.forEach(e=>{(t.has(e.fromNode)||t.has(e.toNode))&&ni(e,o)})}function ni(e,t){const o=t.get(e.fromNode),n=t.get(e.toNode);if(!o||!n)return;const i=function(e,t){const o=ii(e),n=ii(t),i=n.x-o.x,a=n.y-o.y,s=Math.abs(i)-(e.width+t.width)/2,r=Math.abs(a)-(e.height+t.height)/2;if(s>=r)return i>=0?{fromSide:"right",toSide:"left"}:{fromSide:"left",toSide:"right"};return a>=0?{fromSide:"bottom",toSide:"top"}:{fromSide:"top",toSide:"bottom"}}(o,n);e.fromSide=i.fromSide,e.toSide=i.toSide}function ii(e){return{x:e.x+e.width/2,y:e.y+e.height/2}}function ai(e){const t=e.title.trim(),o=e.body?.trim();return o?`# ${t}\n\n${o}`:`# ${t}`}function si(e,t){return t.some(t=>function(e,t){const o=24;return!(e.x+e.width+o<=t.x||t.x+t.width+o<=e.x||e.y+e.height+o<=t.y||t.y+t.height+o<=e.y)}(e,t))}function ri(e){let t="";do{const e=new Uint8Array(8);crypto.getRandomValues(e),t=Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}while(e.has(t));return e.add(t),t}function li(e){const t=e.nodes.filter(e=>"group"!==e.type);if(0===t.length)return{id:"__canvas_board__",type:"group",x:0,y:0,width:0,height:0};const o=Math.max(...t.map(e=>e.x+e.width)),n=Math.min(...t.map(e=>e.y)),i=Math.max(...t.map(e=>e.y+e.height));return{id:"__canvas_board__",type:"group",x:Math.round(o),y:Math.round((n+i)/2),width:0,height:0}}function ci(e,t){return t.reduce((t,o)=>o.from===e?t+2:o.to===e?t+1:t,0)}function di(e){return 0===e.level?"2":"concept"===e.type?"4":"detail"===e.type?"5":void 0}function ui(e){const t=e?.replace(/\s+/g," ").trim();if(!t)return;const o=t.toLowerCase();return["contains","include","includes","including","belongs to","relates to","related to"].includes(o)?void 0:["supports","support"].includes(o)?"支撑":["follows","follow","next","next step"].includes(o)?"后续":["causes","cause","leads to"].includes(o)?"导致":["contrasts","contrast","compares","compare"].includes(o)?"对比":t}function mi(e){return e?e.level<=1?"6":di(e)??"6":"6"}function pi(e,t,o){const n=(e||"").trim();return n&&"anchor"!==n.toLowerCase()&&"current"!==n.toLowerCase()?o.has(n)?n:null:t??null}function hi(e,t,o,n){return e.some(e=>e.fromNode===t&&e.toNode===o&&(e.label||"")===(n?.trim()||""))}function gi(e,t,o){fi(e,e=>{const n=bi(e,o);n&&t.add(n)})}function fi(e,t){e&&(Array.isArray(e)||e instanceof Set?e.forEach(t):e instanceof Map?e.forEach(e=>t(e)):"object"==typeof e&&t(e))}function bi(e,t){if("string"==typeof e&&t.has(e))return e;const o=Ci(e);if(!o)return null;const n=[o.id,Ci(o.node)?.id,Ci(o.data)?.id];for(const e of n)if("string"==typeof e&&t.has(e))return e;return null}function yi(e,t){const o=[e.dataset.nodeId,e.dataset.id,e.getAttribute("data-node-id"),e.getAttribute("data-id"),e.id];for(const e of o)if(e&&t.has(e))return e;for(const o of e.getAttributeNames()){const n=e.getAttribute(o);if(n&&t.has(n))return n}for(const o of Object.values(e.dataset))if(o&&t.has(o))return o;return null}function Ci(e){return e&&"object"==typeof e?e:null}function vi(e,t){return"number"==typeof e&&Number.isFinite(e)?e:t}function wi(e){return 0===e.trim().length}function xi(e){const t=e.trimStart().match(/^(#{1,6})\s+(.+)$/);return t?{level:t[1].length,title:t[2].trim()}:null}function ki(e,t){let o=t;for(;o>1;){const t=e.state.doc.line(o-1).text;if(wi(t)||xi(t))break;o--}return o}function Si(e,t){let o=t;for(;o=1&&n<=e.state.doc.lines;){const t=e.state.doc.line(n).text;if(!wi(t)){if(xi(t))return t;return Ti(e,ki(e,n),Si(e,n))}n+=o}return""}function Ii(e,t){const o=new Map;for(let n=1;n<=t;n++){const t=xi(e.state.doc.line(n).text);if(t){o.set(t.level,t.title);for(let e=t.level+1;e<=6;e++)o.delete(e)}}return Array.from(o.entries()).sort((e,t)=>e[0]-t[0]).map(e=>e[1])}function Ai(e,t,o,n){const i=e.state.doc,a=i.length,s=Math.max(0,Math.min(t,a)),r=Math.max(0,Math.min(o,a)),l=s!==r,c=n?.preferBlockWhenCollapsed??!1;let d=s,u=r,m=l?e.state.sliceDoc(s,r):"",p=l?"selection":"cursor";if(!l&&c){const t=i.lineAt(s);if(!wi(t.text)){const o=ki(e,t.number),n=Si(e,t.number);d=i.line(o).from,u=i.line(n).to,m=e.state.sliceDoc(d,u),m.trim()?p="block":(d=s,u=r)}}const h="cursor"===p?s:d,g=i.lineAt(h),f=u>d?Math.max(d,u-1):d,b=i.lineAt(d).number,y=i.lineAt(f).number,C=ki(e,g.number),v=Si(e,g.number),w=Ti(e,C,v),x=Ii(e,g.number),k=Ei(e,b-1,-1),S=Ei(e,y+1,1),T="cursor"===p?i.line(C).from:Math.max(0,d-1200),E="cursor"===p?i.line(v).to:Math.min(a,u+900),I=e.state.sliceDoc(T,d),A=e.state.sliceDoc(u,E);return{from:d,to:u,selectedText:m,hasSelection:l,source:p,context:[`target_source: ${p}`,`target_lines: ${b}-${y}`,`heading_path: ${x.join(" > ")||"(root)"}`,"surrounding_block:",w,"text_before_target:",I,"text_after_target:",A,"previous_block:",k,"next_block:",S].join("\n")}}class Mi{constructor(e){this.cachedVerified=null,this.refreshPromise=null,this.callbackServer=null,this.pendingCodeVerifier=null,this.pendingState=null,this.pendingAuthorizationUrl=null,this.memAccessToken=null,this.memRefreshToken=null,this.memAiToken=null,this.memCustomModelApiKey=null,this.plugin=e}get secrets(){return this.plugin.app.secretStorage}get supportsSecretStorage(){return!!this.secrets&&"function"==typeof this.secrets.getSecret&&"function"==typeof this.secrets.setSecret}loadSecrets(){this.supportsSecretStorage&&(this.memAccessToken=this.secrets.getSecret(R)??null,this.memRefreshToken=this.secrets.getSecret(q)??null,this.memAiToken=this.secrets.getSecret(B)??null,this.memCustomModelApiKey=this.secrets.getSecret($)??null)}async syncLoginState(){if(this.memAccessToken&&!this.plugin.settings.ai.pkmer.userInfo){await this.verify()?(await this.fetchUserInfo(),this.cachedVerified=!0):this.clearSecrets()}}setAccessToken(e){this.memAccessToken=e,this.supportsSecretStorage&&this.secrets.setSecret(R,e)}setRefreshToken(e){this.memRefreshToken=e,this.supportsSecretStorage&&this.secrets.setSecret(q,e)}setAiToken(e){this.memAiToken=e,this.supportsSecretStorage&&this.secrets.setSecret(B,e)}clearSecrets(){this.memAccessToken=null,this.memRefreshToken=null,this.memAiToken=null,this.memCustomModelApiKey=null,this.supportsSecretStorage&&(this.secrets.setSecret(R,""),this.secrets.setSecret(q,""),this.secrets.setSecret(B,""),this.secrets.setSecret($,""))}get accessToken(){return this.memAccessToken??""}get aiToken(){return this.memAiToken??""}get customModelApiKey(){return this.memCustomModelApiKey??""}get hasSecureStorage(){return this.supportsSecretStorage}async migrateCustomModelApiKeyFromSettings(){const e=this.plugin.settings.ai.customModel.apiKey?.trim();e&&this.supportsSecretStorage&&(this.setCustomModelApiKey(e),this.plugin.settings.ai.customModel.apiKey="",await this.plugin.saveSettings())}setCustomModelApiKey(e){const t=e.trim();this.memCustomModelApiKey=t||null,this.supportsSecretStorage&&this.secrets.setSecret($,t)}clearCustomModelApiKey(){this.memCustomModelApiKey=null,this.supportsSecretStorage&&this.secrets.setSecret($,"")}async verify(){const e=this.plugin.settings.ai.pkmer;if(!this.memAccessToken)return this.cachedVerified=!1,!1;if(e.tokenExpiresAt&&Date.now(){this.isPendingStateMatch(t)&&this.clearPendingOAuthRequest()},3e5);const s=this.startCallbackServer();window.open(a);try{const t=await s;if(!t)return void new e.Notice(v("Login cancelled or timed out."));await this.exchangeCodeForTokens(t,W)?(await this.fetchUserInfo(),new e.Notice(v("Successfully logged in to PKMer!"))):new e.Notice(v("Failed to complete login. Please try again."))}catch(t){console.error("PKMer OAuth login error:",t),new e.Notice(v("Login failed. Please try again."))}finally{this.clearPendingOAuthRequest({closeServer:!0})}}async handleOAuthCallback(t,o){if(!this.isPendingStateMatch(o))return new e.Notice(v("OAuth state mismatch. Please try logging in again.")),this.clearPendingOAuthRequest(),!1;try{return await this.exchangeCodeForTokens(t,j)?(await this.fetchUserInfo(),new e.Notice(v("Successfully logged in to PKMer!")),!0):(new e.Notice(v("Failed to complete login. Please try again.")),!1)}catch(t){return console.error("PKMer OAuth callback error:",t),new e.Notice(v("Login failed. Please try again.")),!1}finally{this.clearPendingOAuthRequest()}}async logout(){this.clearSecrets(),this.plugin.settings.ai.pkmer={...this.plugin.settings.ai.pkmer,tokenExpiresAt:0,userInfo:null},this.cachedVerified=!1,await this.plugin.saveSettings(),new e.Notice(v("Logged out from PKMer."))}async refreshQuota(){return await this.verify()?(await this.fetchUserInfo(),this.plugin.settings.ai.pkmer.userInfo?.ai_quota??null):null}onunload(){this.closeCallbackServer()}async computeCodeChallenge(e){return function(e){const t=new Uint8Array(e);let o="";for(let e=0;e{try{const o=window.require("node:http").createServer((e,o)=>{const n=new URL(e.url,`http://localhost:${K}`);if("/editing-toolbar/callback"!==n.pathname)return o.writeHead(404),void o.end();const i=n.searchParams.get("code"),a=n.searchParams.get("state");if(this.isPendingStateMatch(a))if(i)o.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),o.end("Login successful!
You can close this window and return to Obsidian.
{#if metadata}
+function create_catch_block(ctx) {
+ return {
+ c: noop,
+ m: noop,
+ p: noop,
+ i: noop,
+ o: noop,
+ d: noop
+ };
+}
+
+// (8:37) ; export let metadata; {#if metadata}
+function create_pending_block(ctx) {
+ return {
+ c: noop,
+ m: noop,
+ p: noop,
+ i: noop,
+ o: noop,
+ d: noop
+ };
+}
+
+function create_fragment$5(ctx) {
+ let current_block_type_index;
+ let if_block;
+ let if_block_anchor;
+ let current;
+ const if_block_creators = [create_if_block$1, create_else_block];
+ const if_blocks = [];
+
+ function select_block_type(ctx, dirty) {
+ if (/*metadata*/ ctx[0]) return 0;
+ return 1;
+ }
+
+ current_block_type_index = select_block_type(ctx);
+ if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
+
+ return {
+ c() {
+ if_block.c();
+ if_block_anchor = empty();
+ },
+ m(target, anchor) {
+ if_blocks[current_block_type_index].m(target, anchor);
+ insert(target, if_block_anchor, anchor);
+ current = true;
+ },
+ p(ctx, [dirty]) {
+ let previous_block_index = current_block_type_index;
+ current_block_type_index = select_block_type(ctx);
+
+ if (current_block_type_index === previous_block_index) {
+ if_blocks[current_block_type_index].p(ctx, dirty);
+ } else {
+ group_outros();
+
+ transition_out(if_blocks[previous_block_index], 1, 1, () => {
+ if_blocks[previous_block_index] = null;
+ });
+
+ check_outros();
+ if_block = if_blocks[current_block_type_index];
+
+ if (!if_block) {
+ if_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);
+ if_block.c();
+ } else {
+ if_block.p(ctx, dirty);
+ }
+
+ transition_in(if_block, 1);
+ if_block.m(if_block_anchor.parentNode, if_block_anchor);
+ }
+ },
+ i(local) {
+ if (current) return;
+ transition_in(if_block);
+ current = true;
+ },
+ o(local) {
+ transition_out(if_block);
+ current = false;
+ },
+ d(detaching) {
+ if_blocks[current_block_type_index].d(detaching);
+ if (detaching) detach(if_block_anchor);
+ }
+ };
+}
+
+function instance$5($$self, $$props, $$invalidate) {
+ let { $$slots: slots = {}, $$scope } = $$props;
+
+ let { metadata } = $$props;
+
+ $$self.$$set = $$props => {
+ if ("metadata" in $$props) $$invalidate(0, metadata = $$props.metadata);
+ if ("$$scope" in $$props) $$invalidate(1, $$scope = $$props.$$scope);
+ };
+
+ return [metadata, $$scope, slots];
+}
+
+class MetadataResolver extends SvelteComponent {
+ constructor(options) {
+ super();
+ init(this, options, instance$5, create_fragment$5, not_equal, { metadata: 0 });
+ }
+}
+
+function isMacOS() {
+ return navigator.appVersion.indexOf("Mac") !== -1;
+}
+function isMetaPressed(e) {
+ return isMacOS() ? e.metaKey : e.ctrlKey;
+}
+function getDaysOfWeek(..._args) {
+ return window.moment.weekdaysShort(true);
+}
+function isWeekend(date) {
+ return date.isoWeekday() === 6 || date.isoWeekday() === 7;
+}
+function getStartOfWeek(days) {
+ return days[0].weekday(0);
+}
+/**
+ * Generate a 2D array of daily information to power
+ * the calendar view.
+ */
+function getMonth(displayedMonth, ..._args) {
+ const locale = window.moment().locale();
+ const month = [];
+ let week;
+ const startOfMonth = displayedMonth.clone().locale(locale).date(1);
+ const startOffset = startOfMonth.weekday();
+ let date = startOfMonth.clone().subtract(startOffset, "days");
+ for (let _day = 0; _day < 42; _day++) {
+ if (_day % 7 === 0) {
+ week = {
+ days: [],
+ weekNum: date.week(),
+ };
+ month.push(week);
+ }
+ week.days.push(date);
+ date = date.clone().add(1, "days");
+ }
+ return month;
+}
+
+/* src/components/Day.svelte generated by Svelte v3.35.0 */
+
+function add_css$4() {
+ var style = element("style");
+ style.id = "svelte-q3wqg9-style";
+ style.textContent = ".day.svelte-q3wqg9{background-color:var(--color-background-day);border-radius:4px;color:var(--color-text-day);cursor:pointer;font-size:0.8em;height:100%;padding:4px;position:relative;text-align:center;transition:background-color 0.1s ease-in, color 0.1s ease-in;vertical-align:baseline}.day.svelte-q3wqg9:hover{background-color:var(--interactive-hover)}.day.active.svelte-q3wqg9:hover{background-color:var(--interactive-accent-hover)}.adjacent-month.svelte-q3wqg9{opacity:0.25}.today.svelte-q3wqg9{color:var(--color-text-today)}.day.svelte-q3wqg9:active,.active.svelte-q3wqg9,.active.today.svelte-q3wqg9{color:var(--text-on-accent);background-color:var(--interactive-accent)}.dot-container.svelte-q3wqg9{display:flex;flex-wrap:wrap;justify-content:center;line-height:6px;min-height:6px}";
+ append(document.head, style);
+}
+
+function get_each_context$2(ctx, list, i) {
+ const child_ctx = ctx.slice();
+ child_ctx[11] = list[i];
+ return child_ctx;
+}
+
+// (36:8) {#each metadata.dots as dot}
+function create_each_block$2(ctx) {
+ let dot;
+ let current;
+ const dot_spread_levels = [/*dot*/ ctx[11]];
+ let dot_props = {};
+
+ for (let i = 0; i < dot_spread_levels.length; i += 1) {
+ dot_props = assign(dot_props, dot_spread_levels[i]);
+ }
+
+ dot = new Dot({ props: dot_props });
+
+ return {
+ c() {
+ create_component(dot.$$.fragment);
+ },
+ m(target, anchor) {
+ mount_component(dot, target, anchor);
+ current = true;
+ },
+ p(ctx, dirty) {
+ const dot_changes = (dirty & /*metadata*/ 128)
+ ? get_spread_update(dot_spread_levels, [get_spread_object(/*dot*/ ctx[11])])
+ : {};
+
+ dot.$set(dot_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(dot.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(dot.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ destroy_component(dot, detaching);
+ }
+ };
+}
+
+// (22:2)
+function create_default_slot$1(ctx) {
+ let div1;
+ let t0_value = /*date*/ ctx[0].format("D") + "";
+ let t0;
+ let t1;
+ let div0;
+ let div1_class_value;
+ let current;
+ let mounted;
+ let dispose;
+ let each_value = /*metadata*/ ctx[7].dots;
+ let each_blocks = [];
+
+ for (let i = 0; i < each_value.length; i += 1) {
+ each_blocks[i] = create_each_block$2(get_each_context$2(ctx, each_value, i));
+ }
+
+ const out = i => transition_out(each_blocks[i], 1, 1, () => {
+ each_blocks[i] = null;
+ });
+
+ let div1_levels = [
+ {
+ class: div1_class_value = `day ${/*metadata*/ ctx[7].classes.join(" ")}`
+ },
+ /*metadata*/ ctx[7].dataAttributes || {}
+ ];
+
+ let div1_data = {};
+
+ for (let i = 0; i < div1_levels.length; i += 1) {
+ div1_data = assign(div1_data, div1_levels[i]);
+ }
+
+ return {
+ c() {
+ div1 = element("div");
+ t0 = text(t0_value);
+ t1 = space();
+ div0 = element("div");
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].c();
+ }
+
+ attr(div0, "class", "dot-container svelte-q3wqg9");
+ set_attributes(div1, div1_data);
+ toggle_class(div1, "active", /*selectedId*/ ctx[6] === getDateUID_1(/*date*/ ctx[0], "day"));
+ toggle_class(div1, "adjacent-month", !/*date*/ ctx[0].isSame(/*displayedMonth*/ ctx[5], "month"));
+ toggle_class(div1, "today", /*date*/ ctx[0].isSame(/*today*/ ctx[4], "day"));
+ toggle_class(div1, "svelte-q3wqg9", true);
+ },
+ m(target, anchor) {
+ insert(target, div1, anchor);
+ append(div1, t0);
+ append(div1, t1);
+ append(div1, div0);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].m(div0, null);
+ }
+
+ current = true;
+
+ if (!mounted) {
+ dispose = [
+ listen(div1, "click", function () {
+ if (is_function(/*onClick*/ ctx[2] && /*click_handler*/ ctx[8])) (/*onClick*/ ctx[2] && /*click_handler*/ ctx[8]).apply(this, arguments);
+ }),
+ listen(div1, "contextmenu", function () {
+ if (is_function(/*onContextMenu*/ ctx[3] && /*contextmenu_handler*/ ctx[9])) (/*onContextMenu*/ ctx[3] && /*contextmenu_handler*/ ctx[9]).apply(this, arguments);
+ }),
+ listen(div1, "pointerover", function () {
+ if (is_function(/*onHover*/ ctx[1] && /*pointerover_handler*/ ctx[10])) (/*onHover*/ ctx[1] && /*pointerover_handler*/ ctx[10]).apply(this, arguments);
+ })
+ ];
+
+ mounted = true;
+ }
+ },
+ p(new_ctx, dirty) {
+ ctx = new_ctx;
+ if ((!current || dirty & /*date*/ 1) && t0_value !== (t0_value = /*date*/ ctx[0].format("D") + "")) set_data(t0, t0_value);
+
+ if (dirty & /*metadata*/ 128) {
+ each_value = /*metadata*/ ctx[7].dots;
+ let i;
+
+ for (i = 0; i < each_value.length; i += 1) {
+ const child_ctx = get_each_context$2(ctx, each_value, i);
+
+ if (each_blocks[i]) {
+ each_blocks[i].p(child_ctx, dirty);
+ transition_in(each_blocks[i], 1);
+ } else {
+ each_blocks[i] = create_each_block$2(child_ctx);
+ each_blocks[i].c();
+ transition_in(each_blocks[i], 1);
+ each_blocks[i].m(div0, null);
+ }
+ }
+
+ group_outros();
+
+ for (i = each_value.length; i < each_blocks.length; i += 1) {
+ out(i);
+ }
+
+ check_outros();
+ }
+
+ set_attributes(div1, div1_data = get_spread_update(div1_levels, [
+ (!current || dirty & /*metadata*/ 128 && div1_class_value !== (div1_class_value = `day ${/*metadata*/ ctx[7].classes.join(" ")}`)) && { class: div1_class_value },
+ dirty & /*metadata*/ 128 && (/*metadata*/ ctx[7].dataAttributes || {})
+ ]));
+
+ toggle_class(div1, "active", /*selectedId*/ ctx[6] === getDateUID_1(/*date*/ ctx[0], "day"));
+ toggle_class(div1, "adjacent-month", !/*date*/ ctx[0].isSame(/*displayedMonth*/ ctx[5], "month"));
+ toggle_class(div1, "today", /*date*/ ctx[0].isSame(/*today*/ ctx[4], "day"));
+ toggle_class(div1, "svelte-q3wqg9", true);
+ },
+ i(local) {
+ if (current) return;
+
+ for (let i = 0; i < each_value.length; i += 1) {
+ transition_in(each_blocks[i]);
+ }
+
+ current = true;
+ },
+ o(local) {
+ each_blocks = each_blocks.filter(Boolean);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ transition_out(each_blocks[i]);
+ }
+
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(div1);
+ destroy_each(each_blocks, detaching);
+ mounted = false;
+ run_all(dispose);
+ }
+ };
+}
+
+function create_fragment$4(ctx) {
+ let td;
+ let metadataresolver;
+ let current;
+
+ metadataresolver = new MetadataResolver({
+ props: {
+ metadata: /*metadata*/ ctx[7],
+ $$slots: {
+ default: [
+ create_default_slot$1,
+ ({ metadata }) => ({ 7: metadata }),
+ ({ metadata }) => metadata ? 128 : 0
+ ]
+ },
+ $$scope: { ctx }
+ }
+ });
+
+ return {
+ c() {
+ td = element("td");
+ create_component(metadataresolver.$$.fragment);
+ },
+ m(target, anchor) {
+ insert(target, td, anchor);
+ mount_component(metadataresolver, td, null);
+ current = true;
+ },
+ p(ctx, [dirty]) {
+ const metadataresolver_changes = {};
+ if (dirty & /*metadata*/ 128) metadataresolver_changes.metadata = /*metadata*/ ctx[7];
+
+ if (dirty & /*$$scope, metadata, selectedId, date, displayedMonth, today, onClick, onContextMenu, onHover*/ 16639) {
+ metadataresolver_changes.$$scope = { dirty, ctx };
+ }
+
+ metadataresolver.$set(metadataresolver_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(metadataresolver.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(metadataresolver.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(td);
+ destroy_component(metadataresolver);
+ }
+ };
+}
+
+function instance$4($$self, $$props, $$invalidate) {
+
+
+ let { date } = $$props;
+ let { metadata } = $$props;
+ let { onHover } = $$props;
+ let { onClick } = $$props;
+ let { onContextMenu } = $$props;
+ let { today } = $$props;
+ let { displayedMonth = null } = $$props;
+ let { selectedId = null } = $$props;
+ const click_handler = e => onClick(date, isMetaPressed(e));
+ const contextmenu_handler = e => onContextMenu(date, e);
+ const pointerover_handler = e => onHover(date, e.target, isMetaPressed(e));
+
+ $$self.$$set = $$props => {
+ if ("date" in $$props) $$invalidate(0, date = $$props.date);
+ if ("metadata" in $$props) $$invalidate(7, metadata = $$props.metadata);
+ if ("onHover" in $$props) $$invalidate(1, onHover = $$props.onHover);
+ if ("onClick" in $$props) $$invalidate(2, onClick = $$props.onClick);
+ if ("onContextMenu" in $$props) $$invalidate(3, onContextMenu = $$props.onContextMenu);
+ if ("today" in $$props) $$invalidate(4, today = $$props.today);
+ if ("displayedMonth" in $$props) $$invalidate(5, displayedMonth = $$props.displayedMonth);
+ if ("selectedId" in $$props) $$invalidate(6, selectedId = $$props.selectedId);
+ };
+
+ return [
+ date,
+ onHover,
+ onClick,
+ onContextMenu,
+ today,
+ displayedMonth,
+ selectedId,
+ metadata,
+ click_handler,
+ contextmenu_handler,
+ pointerover_handler
+ ];
+}
+
+class Day extends SvelteComponent {
+ constructor(options) {
+ super();
+ if (!document.getElementById("svelte-q3wqg9-style")) add_css$4();
+
+ init(this, options, instance$4, create_fragment$4, not_equal, {
+ date: 0,
+ metadata: 7,
+ onHover: 1,
+ onClick: 2,
+ onContextMenu: 3,
+ today: 4,
+ displayedMonth: 5,
+ selectedId: 6
+ });
+ }
+}
+
+/* src/components/Arrow.svelte generated by Svelte v3.35.0 */
+
+function add_css$3() {
+ var style = element("style");
+ style.id = "svelte-156w7na-style";
+ style.textContent = ".arrow.svelte-156w7na.svelte-156w7na{align-items:center;cursor:pointer;display:flex;justify-content:center;width:24px}.arrow.is-mobile.svelte-156w7na.svelte-156w7na{width:32px}.right.svelte-156w7na.svelte-156w7na{transform:rotate(180deg)}.arrow.svelte-156w7na svg.svelte-156w7na{color:var(--color-arrow);height:16px;width:16px}";
+ append(document.head, style);
+}
+
+function create_fragment$3(ctx) {
+ let div;
+ let svg;
+ let path;
+ let mounted;
+ let dispose;
+
+ return {
+ c() {
+ div = element("div");
+ svg = svg_element("svg");
+ path = svg_element("path");
+ attr(path, "fill", "currentColor");
+ attr(path, "d", "M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z");
+ attr(svg, "focusable", "false");
+ attr(svg, "role", "img");
+ attr(svg, "xmlns", "http://www.w3.org/2000/svg");
+ attr(svg, "viewBox", "0 0 320 512");
+ attr(svg, "class", "svelte-156w7na");
+ attr(div, "class", "arrow svelte-156w7na");
+ attr(div, "aria-label", /*tooltip*/ ctx[1]);
+ toggle_class(div, "is-mobile", /*isMobile*/ ctx[3]);
+ toggle_class(div, "right", /*direction*/ ctx[2] === "right");
+ },
+ m(target, anchor) {
+ insert(target, div, anchor);
+ append(div, svg);
+ append(svg, path);
+
+ if (!mounted) {
+ dispose = listen(div, "click", function () {
+ if (is_function(/*onClick*/ ctx[0])) /*onClick*/ ctx[0].apply(this, arguments);
+ });
+
+ mounted = true;
+ }
+ },
+ p(new_ctx, [dirty]) {
+ ctx = new_ctx;
+
+ if (dirty & /*tooltip*/ 2) {
+ attr(div, "aria-label", /*tooltip*/ ctx[1]);
+ }
+
+ if (dirty & /*direction*/ 4) {
+ toggle_class(div, "right", /*direction*/ ctx[2] === "right");
+ }
+ },
+ i: noop,
+ o: noop,
+ d(detaching) {
+ if (detaching) detach(div);
+ mounted = false;
+ dispose();
+ }
+ };
+}
+
+function instance$3($$self, $$props, $$invalidate) {
+ let { onClick } = $$props;
+ let { tooltip } = $$props;
+ let { direction } = $$props;
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let isMobile = window.app.isMobile;
+
+ $$self.$$set = $$props => {
+ if ("onClick" in $$props) $$invalidate(0, onClick = $$props.onClick);
+ if ("tooltip" in $$props) $$invalidate(1, tooltip = $$props.tooltip);
+ if ("direction" in $$props) $$invalidate(2, direction = $$props.direction);
+ };
+
+ return [onClick, tooltip, direction, isMobile];
+}
+
+class Arrow extends SvelteComponent {
+ constructor(options) {
+ super();
+ if (!document.getElementById("svelte-156w7na-style")) add_css$3();
+ init(this, options, instance$3, create_fragment$3, safe_not_equal, { onClick: 0, tooltip: 1, direction: 2 });
+ }
+}
+
+/* src/components/Nav.svelte generated by Svelte v3.35.0 */
+
+function add_css$2() {
+ var style = element("style");
+ style.id = "svelte-1vwr9dd-style";
+ style.textContent = ".nav.svelte-1vwr9dd.svelte-1vwr9dd{align-items:center;display:flex;margin:0.6em 0 1em;padding:0 8px;width:100%}.nav.is-mobile.svelte-1vwr9dd.svelte-1vwr9dd{padding:0}.title.svelte-1vwr9dd.svelte-1vwr9dd{color:var(--color-text-title);font-size:1.5em;margin:0}.is-mobile.svelte-1vwr9dd .title.svelte-1vwr9dd{font-size:1.3em}.month.svelte-1vwr9dd.svelte-1vwr9dd{font-weight:500;text-transform:capitalize}.year.svelte-1vwr9dd.svelte-1vwr9dd{color:var(--interactive-accent)}.right-nav.svelte-1vwr9dd.svelte-1vwr9dd{display:flex;justify-content:center;margin-left:auto}.reset-button.svelte-1vwr9dd.svelte-1vwr9dd{cursor:pointer;border-radius:4px;color:var(--text-muted);font-size:0.7em;font-weight:600;letter-spacing:1px;margin:0 4px;padding:0px 4px;text-transform:uppercase}.is-mobile.svelte-1vwr9dd .reset-button.svelte-1vwr9dd{display:none}";
+ append(document.head, style);
+}
+
+function create_fragment$2(ctx) {
+ let div2;
+ let h3;
+ let span0;
+ let t0_value = /*displayedMonth*/ ctx[0].format("MMM") + "";
+ let t0;
+ let t1;
+ let span1;
+ let t2_value = /*displayedMonth*/ ctx[0].format("YYYY") + "";
+ let t2;
+ let t3;
+ let div1;
+ let arrow0;
+ let t4;
+ let div0;
+ let t6;
+ let arrow1;
+ let current;
+ let mounted;
+ let dispose;
+
+ arrow0 = new Arrow({
+ props: {
+ direction: "left",
+ onClick: /*decrementDisplayedMonth*/ ctx[3],
+ tooltip: "Previous Month"
+ }
+ });
+
+ arrow1 = new Arrow({
+ props: {
+ direction: "right",
+ onClick: /*incrementDisplayedMonth*/ ctx[2],
+ tooltip: "Next Month"
+ }
+ });
+
+ return {
+ c() {
+ div2 = element("div");
+ h3 = element("h3");
+ span0 = element("span");
+ t0 = text(t0_value);
+ t1 = space();
+ span1 = element("span");
+ t2 = text(t2_value);
+ t3 = space();
+ div1 = element("div");
+ create_component(arrow0.$$.fragment);
+ t4 = space();
+ div0 = element("div");
+ div0.textContent = `${/*todayDisplayStr*/ ctx[4]}`;
+ t6 = space();
+ create_component(arrow1.$$.fragment);
+ attr(span0, "class", "month svelte-1vwr9dd");
+ attr(span1, "class", "year svelte-1vwr9dd");
+ attr(h3, "class", "title svelte-1vwr9dd");
+ attr(div0, "class", "reset-button svelte-1vwr9dd");
+ attr(div1, "class", "right-nav svelte-1vwr9dd");
+ attr(div2, "class", "nav svelte-1vwr9dd");
+ toggle_class(div2, "is-mobile", /*isMobile*/ ctx[5]);
+ },
+ m(target, anchor) {
+ insert(target, div2, anchor);
+ append(div2, h3);
+ append(h3, span0);
+ append(span0, t0);
+ append(h3, t1);
+ append(h3, span1);
+ append(span1, t2);
+ append(div2, t3);
+ append(div2, div1);
+ mount_component(arrow0, div1, null);
+ append(div1, t4);
+ append(div1, div0);
+ append(div1, t6);
+ mount_component(arrow1, div1, null);
+ current = true;
+
+ if (!mounted) {
+ dispose = [
+ listen(h3, "click", function () {
+ if (is_function(/*resetDisplayedMonth*/ ctx[1])) /*resetDisplayedMonth*/ ctx[1].apply(this, arguments);
+ }),
+ listen(div0, "click", function () {
+ if (is_function(/*resetDisplayedMonth*/ ctx[1])) /*resetDisplayedMonth*/ ctx[1].apply(this, arguments);
+ })
+ ];
+
+ mounted = true;
+ }
+ },
+ p(new_ctx, [dirty]) {
+ ctx = new_ctx;
+ if ((!current || dirty & /*displayedMonth*/ 1) && t0_value !== (t0_value = /*displayedMonth*/ ctx[0].format("MMM") + "")) set_data(t0, t0_value);
+ if ((!current || dirty & /*displayedMonth*/ 1) && t2_value !== (t2_value = /*displayedMonth*/ ctx[0].format("YYYY") + "")) set_data(t2, t2_value);
+ const arrow0_changes = {};
+ if (dirty & /*decrementDisplayedMonth*/ 8) arrow0_changes.onClick = /*decrementDisplayedMonth*/ ctx[3];
+ arrow0.$set(arrow0_changes);
+ const arrow1_changes = {};
+ if (dirty & /*incrementDisplayedMonth*/ 4) arrow1_changes.onClick = /*incrementDisplayedMonth*/ ctx[2];
+ arrow1.$set(arrow1_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(arrow0.$$.fragment, local);
+ transition_in(arrow1.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(arrow0.$$.fragment, local);
+ transition_out(arrow1.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(div2);
+ destroy_component(arrow0);
+ destroy_component(arrow1);
+ mounted = false;
+ run_all(dispose);
+ }
+ };
+}
+
+function instance$2($$self, $$props, $$invalidate) {
+
+ let { displayedMonth } = $$props;
+ let { today } = $$props;
+ let { resetDisplayedMonth } = $$props;
+ let { incrementDisplayedMonth } = $$props;
+ let { decrementDisplayedMonth } = $$props;
+
+ // Get the word 'Today' but localized to the current language
+ const todayDisplayStr = today.calendar().split(/\d|\s/)[0];
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let isMobile = window.app.isMobile;
+
+ $$self.$$set = $$props => {
+ if ("displayedMonth" in $$props) $$invalidate(0, displayedMonth = $$props.displayedMonth);
+ if ("today" in $$props) $$invalidate(6, today = $$props.today);
+ if ("resetDisplayedMonth" in $$props) $$invalidate(1, resetDisplayedMonth = $$props.resetDisplayedMonth);
+ if ("incrementDisplayedMonth" in $$props) $$invalidate(2, incrementDisplayedMonth = $$props.incrementDisplayedMonth);
+ if ("decrementDisplayedMonth" in $$props) $$invalidate(3, decrementDisplayedMonth = $$props.decrementDisplayedMonth);
+ };
+
+ return [
+ displayedMonth,
+ resetDisplayedMonth,
+ incrementDisplayedMonth,
+ decrementDisplayedMonth,
+ todayDisplayStr,
+ isMobile,
+ today
+ ];
+}
+
+class Nav extends SvelteComponent {
+ constructor(options) {
+ super();
+ if (!document.getElementById("svelte-1vwr9dd-style")) add_css$2();
+
+ init(this, options, instance$2, create_fragment$2, safe_not_equal, {
+ displayedMonth: 0,
+ today: 6,
+ resetDisplayedMonth: 1,
+ incrementDisplayedMonth: 2,
+ decrementDisplayedMonth: 3
+ });
+ }
+}
+
+/* src/components/WeekNum.svelte generated by Svelte v3.35.0 */
+
+function add_css$1() {
+ var style = element("style");
+ style.id = "svelte-egt0yd-style";
+ style.textContent = "td.svelte-egt0yd{border-right:1px solid var(--background-modifier-border)}.week-num.svelte-egt0yd{background-color:var(--color-background-weeknum);border-radius:4px;color:var(--color-text-weeknum);cursor:pointer;font-size:0.65em;height:100%;padding:4px;text-align:center;transition:background-color 0.1s ease-in, color 0.1s ease-in;vertical-align:baseline}.week-num.svelte-egt0yd:hover{background-color:var(--interactive-hover)}.week-num.active.svelte-egt0yd:hover{background-color:var(--interactive-accent-hover)}.active.svelte-egt0yd{color:var(--text-on-accent);background-color:var(--interactive-accent)}.dot-container.svelte-egt0yd{display:flex;flex-wrap:wrap;justify-content:center;line-height:6px;min-height:6px}";
+ append(document.head, style);
+}
+
+function get_each_context$1(ctx, list, i) {
+ const child_ctx = ctx.slice();
+ child_ctx[11] = list[i];
+ return child_ctx;
+}
+
+// (35:8) {#each metadata.dots as dot}
+function create_each_block$1(ctx) {
+ let dot;
+ let current;
+ const dot_spread_levels = [/*dot*/ ctx[11]];
+ let dot_props = {};
+
+ for (let i = 0; i < dot_spread_levels.length; i += 1) {
+ dot_props = assign(dot_props, dot_spread_levels[i]);
+ }
+
+ dot = new Dot({ props: dot_props });
+
+ return {
+ c() {
+ create_component(dot.$$.fragment);
+ },
+ m(target, anchor) {
+ mount_component(dot, target, anchor);
+ current = true;
+ },
+ p(ctx, dirty) {
+ const dot_changes = (dirty & /*metadata*/ 64)
+ ? get_spread_update(dot_spread_levels, [get_spread_object(/*dot*/ ctx[11])])
+ : {};
+
+ dot.$set(dot_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(dot.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(dot.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ destroy_component(dot, detaching);
+ }
+ };
+}
+
+// (24:2)
+function create_default_slot(ctx) {
+ let div1;
+ let t0;
+ let t1;
+ let div0;
+ let div1_class_value;
+ let current;
+ let mounted;
+ let dispose;
+ let each_value = /*metadata*/ ctx[6].dots;
+ let each_blocks = [];
+
+ for (let i = 0; i < each_value.length; i += 1) {
+ each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i));
+ }
+
+ const out = i => transition_out(each_blocks[i], 1, 1, () => {
+ each_blocks[i] = null;
+ });
+
+ return {
+ c() {
+ div1 = element("div");
+ t0 = text(/*weekNum*/ ctx[0]);
+ t1 = space();
+ div0 = element("div");
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].c();
+ }
+
+ attr(div0, "class", "dot-container svelte-egt0yd");
+ attr(div1, "class", div1_class_value = "" + (null_to_empty(`week-num ${/*metadata*/ ctx[6].classes.join(" ")}`) + " svelte-egt0yd"));
+ toggle_class(div1, "active", /*selectedId*/ ctx[5] === getDateUID_1(/*days*/ ctx[1][0], "week"));
+ },
+ m(target, anchor) {
+ insert(target, div1, anchor);
+ append(div1, t0);
+ append(div1, t1);
+ append(div1, div0);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].m(div0, null);
+ }
+
+ current = true;
+
+ if (!mounted) {
+ dispose = [
+ listen(div1, "click", function () {
+ if (is_function(/*onClick*/ ctx[3] && /*click_handler*/ ctx[8])) (/*onClick*/ ctx[3] && /*click_handler*/ ctx[8]).apply(this, arguments);
+ }),
+ listen(div1, "contextmenu", function () {
+ if (is_function(/*onContextMenu*/ ctx[4] && /*contextmenu_handler*/ ctx[9])) (/*onContextMenu*/ ctx[4] && /*contextmenu_handler*/ ctx[9]).apply(this, arguments);
+ }),
+ listen(div1, "pointerover", function () {
+ if (is_function(/*onHover*/ ctx[2] && /*pointerover_handler*/ ctx[10])) (/*onHover*/ ctx[2] && /*pointerover_handler*/ ctx[10]).apply(this, arguments);
+ })
+ ];
+
+ mounted = true;
+ }
+ },
+ p(new_ctx, dirty) {
+ ctx = new_ctx;
+ if (!current || dirty & /*weekNum*/ 1) set_data(t0, /*weekNum*/ ctx[0]);
+
+ if (dirty & /*metadata*/ 64) {
+ each_value = /*metadata*/ ctx[6].dots;
+ let i;
+
+ for (i = 0; i < each_value.length; i += 1) {
+ const child_ctx = get_each_context$1(ctx, each_value, i);
+
+ if (each_blocks[i]) {
+ each_blocks[i].p(child_ctx, dirty);
+ transition_in(each_blocks[i], 1);
+ } else {
+ each_blocks[i] = create_each_block$1(child_ctx);
+ each_blocks[i].c();
+ transition_in(each_blocks[i], 1);
+ each_blocks[i].m(div0, null);
+ }
+ }
+
+ group_outros();
+
+ for (i = each_value.length; i < each_blocks.length; i += 1) {
+ out(i);
+ }
+
+ check_outros();
+ }
+
+ if (!current || dirty & /*metadata*/ 64 && div1_class_value !== (div1_class_value = "" + (null_to_empty(`week-num ${/*metadata*/ ctx[6].classes.join(" ")}`) + " svelte-egt0yd"))) {
+ attr(div1, "class", div1_class_value);
+ }
+
+ if (dirty & /*metadata, selectedId, getDateUID, days*/ 98) {
+ toggle_class(div1, "active", /*selectedId*/ ctx[5] === getDateUID_1(/*days*/ ctx[1][0], "week"));
+ }
+ },
+ i(local) {
+ if (current) return;
+
+ for (let i = 0; i < each_value.length; i += 1) {
+ transition_in(each_blocks[i]);
+ }
+
+ current = true;
+ },
+ o(local) {
+ each_blocks = each_blocks.filter(Boolean);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ transition_out(each_blocks[i]);
+ }
+
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(div1);
+ destroy_each(each_blocks, detaching);
+ mounted = false;
+ run_all(dispose);
+ }
+ };
+}
+
+function create_fragment$1(ctx) {
+ let td;
+ let metadataresolver;
+ let current;
+
+ metadataresolver = new MetadataResolver({
+ props: {
+ metadata: /*metadata*/ ctx[6],
+ $$slots: {
+ default: [
+ create_default_slot,
+ ({ metadata }) => ({ 6: metadata }),
+ ({ metadata }) => metadata ? 64 : 0
+ ]
+ },
+ $$scope: { ctx }
+ }
+ });
+
+ return {
+ c() {
+ td = element("td");
+ create_component(metadataresolver.$$.fragment);
+ attr(td, "class", "svelte-egt0yd");
+ },
+ m(target, anchor) {
+ insert(target, td, anchor);
+ mount_component(metadataresolver, td, null);
+ current = true;
+ },
+ p(ctx, [dirty]) {
+ const metadataresolver_changes = {};
+ if (dirty & /*metadata*/ 64) metadataresolver_changes.metadata = /*metadata*/ ctx[6];
+
+ if (dirty & /*$$scope, metadata, selectedId, days, onClick, startOfWeek, onContextMenu, onHover, weekNum*/ 16639) {
+ metadataresolver_changes.$$scope = { dirty, ctx };
+ }
+
+ metadataresolver.$set(metadataresolver_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(metadataresolver.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(metadataresolver.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(td);
+ destroy_component(metadataresolver);
+ }
+ };
+}
+
+function instance$1($$self, $$props, $$invalidate) {
+
+
+ let { weekNum } = $$props;
+ let { days } = $$props;
+ let { metadata } = $$props;
+ let { onHover } = $$props;
+ let { onClick } = $$props;
+ let { onContextMenu } = $$props;
+ let { selectedId = null } = $$props;
+ let startOfWeek;
+ const click_handler = e => onClick(startOfWeek, isMetaPressed(e));
+ const contextmenu_handler = e => onContextMenu(days[0], e);
+ const pointerover_handler = e => onHover(startOfWeek, e.target, isMetaPressed(e));
+
+ $$self.$$set = $$props => {
+ if ("weekNum" in $$props) $$invalidate(0, weekNum = $$props.weekNum);
+ if ("days" in $$props) $$invalidate(1, days = $$props.days);
+ if ("metadata" in $$props) $$invalidate(6, metadata = $$props.metadata);
+ if ("onHover" in $$props) $$invalidate(2, onHover = $$props.onHover);
+ if ("onClick" in $$props) $$invalidate(3, onClick = $$props.onClick);
+ if ("onContextMenu" in $$props) $$invalidate(4, onContextMenu = $$props.onContextMenu);
+ if ("selectedId" in $$props) $$invalidate(5, selectedId = $$props.selectedId);
+ };
+
+ $$self.$$.update = () => {
+ if ($$self.$$.dirty & /*days*/ 2) {
+ $$invalidate(7, startOfWeek = getStartOfWeek(days));
+ }
+ };
+
+ return [
+ weekNum,
+ days,
+ onHover,
+ onClick,
+ onContextMenu,
+ selectedId,
+ metadata,
+ startOfWeek,
+ click_handler,
+ contextmenu_handler,
+ pointerover_handler
+ ];
+}
+
+class WeekNum extends SvelteComponent {
+ constructor(options) {
+ super();
+ if (!document.getElementById("svelte-egt0yd-style")) add_css$1();
+
+ init(this, options, instance$1, create_fragment$1, not_equal, {
+ weekNum: 0,
+ days: 1,
+ metadata: 6,
+ onHover: 2,
+ onClick: 3,
+ onContextMenu: 4,
+ selectedId: 5
+ });
+ }
+}
+
+async function metadataReducer(promisedMetadata) {
+ const meta = {
+ dots: [],
+ classes: [],
+ dataAttributes: {},
+ };
+ const metas = await Promise.all(promisedMetadata);
+ return metas.reduce((acc, meta) => ({
+ classes: [...acc.classes, ...(meta.classes || [])],
+ dataAttributes: Object.assign(acc.dataAttributes, meta.dataAttributes),
+ dots: [...acc.dots, ...(meta.dots || [])],
+ }), meta);
+}
+function getDailyMetadata(sources, date, ..._args) {
+ return metadataReducer(sources.map((source) => source.getDailyMetadata(date)));
+}
+function getWeeklyMetadata(sources, date, ..._args) {
+ return metadataReducer(sources.map((source) => source.getWeeklyMetadata(date)));
+}
+
+/* src/components/Calendar.svelte generated by Svelte v3.35.0 */
+
+function add_css() {
+ var style = element("style");
+ style.id = "svelte-pcimu8-style";
+ style.textContent = ".container.svelte-pcimu8{--color-background-heading:transparent;--color-background-day:transparent;--color-background-weeknum:transparent;--color-background-weekend:transparent;--color-dot:var(--text-muted);--color-arrow:var(--text-muted);--color-button:var(--text-muted);--color-text-title:var(--text-normal);--color-text-heading:var(--text-muted);--color-text-day:var(--text-normal);--color-text-today:var(--interactive-accent);--color-text-weeknum:var(--text-muted)}.container.svelte-pcimu8{padding:0 8px}.container.is-mobile.svelte-pcimu8{padding:0}th.svelte-pcimu8{text-align:center}.weekend.svelte-pcimu8{background-color:var(--color-background-weekend)}.calendar.svelte-pcimu8{border-collapse:collapse;width:100%}th.svelte-pcimu8{background-color:var(--color-background-heading);color:var(--color-text-heading);font-size:0.6em;letter-spacing:1px;padding:4px;text-transform:uppercase}";
+ append(document.head, style);
+}
+
+function get_each_context(ctx, list, i) {
+ const child_ctx = ctx.slice();
+ child_ctx[18] = list[i];
+ return child_ctx;
+}
+
+function get_each_context_1(ctx, list, i) {
+ const child_ctx = ctx.slice();
+ child_ctx[21] = list[i];
+ return child_ctx;
+}
+
+function get_each_context_2(ctx, list, i) {
+ const child_ctx = ctx.slice();
+ child_ctx[24] = list[i];
+ return child_ctx;
+}
+
+function get_each_context_3(ctx, list, i) {
+ const child_ctx = ctx.slice();
+ child_ctx[27] = list[i];
+ return child_ctx;
+}
+
+// (55:6) {#if showWeekNums}
+function create_if_block_2(ctx) {
+ let col;
+
+ return {
+ c() {
+ col = element("col");
+ },
+ m(target, anchor) {
+ insert(target, col, anchor);
+ },
+ d(detaching) {
+ if (detaching) detach(col);
+ }
+ };
+}
+
+// (58:6) {#each month[1].days as date}
+function create_each_block_3(ctx) {
+ let col;
+
+ return {
+ c() {
+ col = element("col");
+ attr(col, "class", "svelte-pcimu8");
+ toggle_class(col, "weekend", isWeekend(/*date*/ ctx[27]));
+ },
+ m(target, anchor) {
+ insert(target, col, anchor);
+ },
+ p(ctx, dirty) {
+ if (dirty & /*isWeekend, month*/ 16384) {
+ toggle_class(col, "weekend", isWeekend(/*date*/ ctx[27]));
+ }
+ },
+ d(detaching) {
+ if (detaching) detach(col);
+ }
+ };
+}
+
+// (64:8) {#if showWeekNums}
+function create_if_block_1(ctx) {
+ let th;
+
+ return {
+ c() {
+ th = element("th");
+ th.textContent = "W";
+ attr(th, "class", "svelte-pcimu8");
+ },
+ m(target, anchor) {
+ insert(target, th, anchor);
+ },
+ d(detaching) {
+ if (detaching) detach(th);
+ }
+ };
+}
+
+// (67:8) {#each daysOfWeek as dayOfWeek}
+function create_each_block_2(ctx) {
+ let th;
+ let t_value = /*dayOfWeek*/ ctx[24] + "";
+ let t;
+
+ return {
+ c() {
+ th = element("th");
+ t = text(t_value);
+ attr(th, "class", "svelte-pcimu8");
+ },
+ m(target, anchor) {
+ insert(target, th, anchor);
+ append(th, t);
+ },
+ p(ctx, dirty) {
+ if (dirty & /*daysOfWeek*/ 32768 && t_value !== (t_value = /*dayOfWeek*/ ctx[24] + "")) set_data(t, t_value);
+ },
+ d(detaching) {
+ if (detaching) detach(th);
+ }
+ };
+}
+
+// (75:10) {#if showWeekNums}
+function create_if_block(ctx) {
+ let weeknum;
+ let current;
+
+ const weeknum_spread_levels = [
+ /*week*/ ctx[18],
+ {
+ metadata: getWeeklyMetadata(/*sources*/ ctx[8], /*week*/ ctx[18].days[0], /*today*/ ctx[10])
+ },
+ { onClick: /*onClickWeek*/ ctx[7] },
+ {
+ onContextMenu: /*onContextMenuWeek*/ ctx[5]
+ },
+ { onHover: /*onHoverWeek*/ ctx[3] },
+ { selectedId: /*selectedId*/ ctx[9] }
+ ];
+
+ let weeknum_props = {};
+
+ for (let i = 0; i < weeknum_spread_levels.length; i += 1) {
+ weeknum_props = assign(weeknum_props, weeknum_spread_levels[i]);
+ }
+
+ weeknum = new WeekNum({ props: weeknum_props });
+
+ return {
+ c() {
+ create_component(weeknum.$$.fragment);
+ },
+ m(target, anchor) {
+ mount_component(weeknum, target, anchor);
+ current = true;
+ },
+ p(ctx, dirty) {
+ const weeknum_changes = (dirty & /*month, getWeeklyMetadata, sources, today, onClickWeek, onContextMenuWeek, onHoverWeek, selectedId*/ 18344)
+ ? get_spread_update(weeknum_spread_levels, [
+ dirty & /*month*/ 16384 && get_spread_object(/*week*/ ctx[18]),
+ dirty & /*getWeeklyMetadata, sources, month, today*/ 17664 && {
+ metadata: getWeeklyMetadata(/*sources*/ ctx[8], /*week*/ ctx[18].days[0], /*today*/ ctx[10])
+ },
+ dirty & /*onClickWeek*/ 128 && { onClick: /*onClickWeek*/ ctx[7] },
+ dirty & /*onContextMenuWeek*/ 32 && {
+ onContextMenu: /*onContextMenuWeek*/ ctx[5]
+ },
+ dirty & /*onHoverWeek*/ 8 && { onHover: /*onHoverWeek*/ ctx[3] },
+ dirty & /*selectedId*/ 512 && { selectedId: /*selectedId*/ ctx[9] }
+ ])
+ : {};
+
+ weeknum.$set(weeknum_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(weeknum.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(weeknum.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ destroy_component(weeknum, detaching);
+ }
+ };
+}
+
+// (85:10) {#each week.days as day (day.format())}
+function create_each_block_1(key_1, ctx) {
+ let first;
+ let day;
+ let current;
+
+ day = new Day({
+ props: {
+ date: /*day*/ ctx[21],
+ today: /*today*/ ctx[10],
+ displayedMonth: /*displayedMonth*/ ctx[0],
+ onClick: /*onClickDay*/ ctx[6],
+ onContextMenu: /*onContextMenuDay*/ ctx[4],
+ onHover: /*onHoverDay*/ ctx[2],
+ metadata: getDailyMetadata(/*sources*/ ctx[8], /*day*/ ctx[21], /*today*/ ctx[10]),
+ selectedId: /*selectedId*/ ctx[9]
+ }
+ });
+
+ return {
+ key: key_1,
+ first: null,
+ c() {
+ first = empty();
+ create_component(day.$$.fragment);
+ this.first = first;
+ },
+ m(target, anchor) {
+ insert(target, first, anchor);
+ mount_component(day, target, anchor);
+ current = true;
+ },
+ p(new_ctx, dirty) {
+ ctx = new_ctx;
+ const day_changes = {};
+ if (dirty & /*month*/ 16384) day_changes.date = /*day*/ ctx[21];
+ if (dirty & /*today*/ 1024) day_changes.today = /*today*/ ctx[10];
+ if (dirty & /*displayedMonth*/ 1) day_changes.displayedMonth = /*displayedMonth*/ ctx[0];
+ if (dirty & /*onClickDay*/ 64) day_changes.onClick = /*onClickDay*/ ctx[6];
+ if (dirty & /*onContextMenuDay*/ 16) day_changes.onContextMenu = /*onContextMenuDay*/ ctx[4];
+ if (dirty & /*onHoverDay*/ 4) day_changes.onHover = /*onHoverDay*/ ctx[2];
+ if (dirty & /*sources, month, today*/ 17664) day_changes.metadata = getDailyMetadata(/*sources*/ ctx[8], /*day*/ ctx[21], /*today*/ ctx[10]);
+ if (dirty & /*selectedId*/ 512) day_changes.selectedId = /*selectedId*/ ctx[9];
+ day.$set(day_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in(day.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out(day.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(first);
+ destroy_component(day, detaching);
+ }
+ };
+}
+
+// (73:6) {#each month as week (week.weekNum)}
+function create_each_block(key_1, ctx) {
+ let tr;
+ let t0;
+ let each_blocks = [];
+ let each_1_lookup = new Map();
+ let t1;
+ let current;
+ let if_block = /*showWeekNums*/ ctx[1] && create_if_block(ctx);
+ let each_value_1 = /*week*/ ctx[18].days;
+ const get_key = ctx => /*day*/ ctx[21].format();
+
+ for (let i = 0; i < each_value_1.length; i += 1) {
+ let child_ctx = get_each_context_1(ctx, each_value_1, i);
+ let key = get_key(child_ctx);
+ each_1_lookup.set(key, each_blocks[i] = create_each_block_1(key, child_ctx));
+ }
+
+ return {
+ key: key_1,
+ first: null,
+ c() {
+ tr = element("tr");
+ if (if_block) if_block.c();
+ t0 = space();
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].c();
+ }
+
+ t1 = space();
+ this.first = tr;
+ },
+ m(target, anchor) {
+ insert(target, tr, anchor);
+ if (if_block) if_block.m(tr, null);
+ append(tr, t0);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].m(tr, null);
+ }
+
+ append(tr, t1);
+ current = true;
+ },
+ p(new_ctx, dirty) {
+ ctx = new_ctx;
+
+ if (/*showWeekNums*/ ctx[1]) {
+ if (if_block) {
+ if_block.p(ctx, dirty);
+
+ if (dirty & /*showWeekNums*/ 2) {
+ transition_in(if_block, 1);
+ }
+ } else {
+ if_block = create_if_block(ctx);
+ if_block.c();
+ transition_in(if_block, 1);
+ if_block.m(tr, t0);
+ }
+ } else if (if_block) {
+ group_outros();
+
+ transition_out(if_block, 1, 1, () => {
+ if_block = null;
+ });
+
+ check_outros();
+ }
+
+ if (dirty & /*month, today, displayedMonth, onClickDay, onContextMenuDay, onHoverDay, getDailyMetadata, sources, selectedId*/ 18261) {
+ each_value_1 = /*week*/ ctx[18].days;
+ group_outros();
+ each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value_1, each_1_lookup, tr, outro_and_destroy_block, create_each_block_1, t1, get_each_context_1);
+ check_outros();
+ }
+ },
+ i(local) {
+ if (current) return;
+ transition_in(if_block);
+
+ for (let i = 0; i < each_value_1.length; i += 1) {
+ transition_in(each_blocks[i]);
+ }
+
+ current = true;
+ },
+ o(local) {
+ transition_out(if_block);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ transition_out(each_blocks[i]);
+ }
+
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(tr);
+ if (if_block) if_block.d();
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].d();
+ }
+ }
+ };
+}
+
+function create_fragment$7(ctx) {
+ let div;
+ let nav;
+ let t0;
+ let table;
+ let colgroup;
+ let t1;
+ let t2;
+ let thead;
+ let tr;
+ let t3;
+ let t4;
+ let tbody;
+ let each_blocks = [];
+ let each2_lookup = new Map();
+ let current;
+
+ nav = new Nav({
+ props: {
+ today: /*today*/ ctx[10],
+ displayedMonth: /*displayedMonth*/ ctx[0],
+ incrementDisplayedMonth: /*incrementDisplayedMonth*/ ctx[11],
+ decrementDisplayedMonth: /*decrementDisplayedMonth*/ ctx[12],
+ resetDisplayedMonth: /*resetDisplayedMonth*/ ctx[13]
+ }
+ });
+
+ let if_block0 = /*showWeekNums*/ ctx[1] && create_if_block_2();
+ let each_value_3 = /*month*/ ctx[14][1].days;
+ let each_blocks_2 = [];
+
+ for (let i = 0; i < each_value_3.length; i += 1) {
+ each_blocks_2[i] = create_each_block_3(get_each_context_3(ctx, each_value_3, i));
+ }
+
+ let if_block1 = /*showWeekNums*/ ctx[1] && create_if_block_1();
+ let each_value_2 = /*daysOfWeek*/ ctx[15];
+ let each_blocks_1 = [];
+
+ for (let i = 0; i < each_value_2.length; i += 1) {
+ each_blocks_1[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
+ }
+
+ let each_value = /*month*/ ctx[14];
+ const get_key = ctx => /*week*/ ctx[18].weekNum;
+
+ for (let i = 0; i < each_value.length; i += 1) {
+ let child_ctx = get_each_context(ctx, each_value, i);
+ let key = get_key(child_ctx);
+ each2_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));
+ }
+
+ return {
+ c() {
+ div = element("div");
+ create_component(nav.$$.fragment);
+ t0 = space();
+ table = element("table");
+ colgroup = element("colgroup");
+ if (if_block0) if_block0.c();
+ t1 = space();
+
+ for (let i = 0; i < each_blocks_2.length; i += 1) {
+ each_blocks_2[i].c();
+ }
+
+ t2 = space();
+ thead = element("thead");
+ tr = element("tr");
+ if (if_block1) if_block1.c();
+ t3 = space();
+
+ for (let i = 0; i < each_blocks_1.length; i += 1) {
+ each_blocks_1[i].c();
+ }
+
+ t4 = space();
+ tbody = element("tbody");
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].c();
+ }
+
+ attr(table, "class", "calendar svelte-pcimu8");
+ attr(div, "id", "calendar-container");
+ attr(div, "class", "container svelte-pcimu8");
+ toggle_class(div, "is-mobile", /*isMobile*/ ctx[16]);
+ },
+ m(target, anchor) {
+ insert(target, div, anchor);
+ mount_component(nav, div, null);
+ append(div, t0);
+ append(div, table);
+ append(table, colgroup);
+ if (if_block0) if_block0.m(colgroup, null);
+ append(colgroup, t1);
+
+ for (let i = 0; i < each_blocks_2.length; i += 1) {
+ each_blocks_2[i].m(colgroup, null);
+ }
+
+ append(table, t2);
+ append(table, thead);
+ append(thead, tr);
+ if (if_block1) if_block1.m(tr, null);
+ append(tr, t3);
+
+ for (let i = 0; i < each_blocks_1.length; i += 1) {
+ each_blocks_1[i].m(tr, null);
+ }
+
+ append(table, t4);
+ append(table, tbody);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].m(tbody, null);
+ }
+
+ current = true;
+ },
+ p(ctx, [dirty]) {
+ const nav_changes = {};
+ if (dirty & /*today*/ 1024) nav_changes.today = /*today*/ ctx[10];
+ if (dirty & /*displayedMonth*/ 1) nav_changes.displayedMonth = /*displayedMonth*/ ctx[0];
+ nav.$set(nav_changes);
+
+ if (/*showWeekNums*/ ctx[1]) {
+ if (if_block0) ; else {
+ if_block0 = create_if_block_2();
+ if_block0.c();
+ if_block0.m(colgroup, t1);
+ }
+ } else if (if_block0) {
+ if_block0.d(1);
+ if_block0 = null;
+ }
+
+ if (dirty & /*isWeekend, month*/ 16384) {
+ each_value_3 = /*month*/ ctx[14][1].days;
+ let i;
+
+ for (i = 0; i < each_value_3.length; i += 1) {
+ const child_ctx = get_each_context_3(ctx, each_value_3, i);
+
+ if (each_blocks_2[i]) {
+ each_blocks_2[i].p(child_ctx, dirty);
+ } else {
+ each_blocks_2[i] = create_each_block_3(child_ctx);
+ each_blocks_2[i].c();
+ each_blocks_2[i].m(colgroup, null);
+ }
+ }
+
+ for (; i < each_blocks_2.length; i += 1) {
+ each_blocks_2[i].d(1);
+ }
+
+ each_blocks_2.length = each_value_3.length;
+ }
+
+ if (/*showWeekNums*/ ctx[1]) {
+ if (if_block1) ; else {
+ if_block1 = create_if_block_1();
+ if_block1.c();
+ if_block1.m(tr, t3);
+ }
+ } else if (if_block1) {
+ if_block1.d(1);
+ if_block1 = null;
+ }
+
+ if (dirty & /*daysOfWeek*/ 32768) {
+ each_value_2 = /*daysOfWeek*/ ctx[15];
+ let i;
+
+ for (i = 0; i < each_value_2.length; i += 1) {
+ const child_ctx = get_each_context_2(ctx, each_value_2, i);
+
+ if (each_blocks_1[i]) {
+ each_blocks_1[i].p(child_ctx, dirty);
+ } else {
+ each_blocks_1[i] = create_each_block_2(child_ctx);
+ each_blocks_1[i].c();
+ each_blocks_1[i].m(tr, null);
+ }
+ }
+
+ for (; i < each_blocks_1.length; i += 1) {
+ each_blocks_1[i].d(1);
+ }
+
+ each_blocks_1.length = each_value_2.length;
+ }
+
+ if (dirty & /*month, today, displayedMonth, onClickDay, onContextMenuDay, onHoverDay, getDailyMetadata, sources, selectedId, getWeeklyMetadata, onClickWeek, onContextMenuWeek, onHoverWeek, showWeekNums*/ 18431) {
+ each_value = /*month*/ ctx[14];
+ group_outros();
+ each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each2_lookup, tbody, outro_and_destroy_block, create_each_block, null, get_each_context);
+ check_outros();
+ }
+ },
+ i(local) {
+ if (current) return;
+ transition_in(nav.$$.fragment, local);
+
+ for (let i = 0; i < each_value.length; i += 1) {
+ transition_in(each_blocks[i]);
+ }
+
+ current = true;
+ },
+ o(local) {
+ transition_out(nav.$$.fragment, local);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ transition_out(each_blocks[i]);
+ }
+
+ current = false;
+ },
+ d(detaching) {
+ if (detaching) detach(div);
+ destroy_component(nav);
+ if (if_block0) if_block0.d();
+ destroy_each(each_blocks_2, detaching);
+ if (if_block1) if_block1.d();
+ destroy_each(each_blocks_1, detaching);
+
+ for (let i = 0; i < each_blocks.length; i += 1) {
+ each_blocks[i].d();
+ }
+ }
+ };
+}
+
+function instance$7($$self, $$props, $$invalidate) {
+
+
+ let { localeData } = $$props;
+ let { showWeekNums = false } = $$props;
+ let { onHoverDay } = $$props;
+ let { onHoverWeek } = $$props;
+ let { onContextMenuDay } = $$props;
+ let { onContextMenuWeek } = $$props;
+ let { onClickDay } = $$props;
+ let { onClickWeek } = $$props;
+ let { sources = [] } = $$props;
+ let { selectedId } = $$props;
+ let { today = window.moment() } = $$props;
+ let { displayedMonth = today } = $$props;
+ let month;
+ let daysOfWeek;
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ let isMobile = window.app.isMobile;
+
+ function incrementDisplayedMonth() {
+ $$invalidate(0, displayedMonth = displayedMonth.clone().add(1, "month"));
+ }
+
+ function decrementDisplayedMonth() {
+ $$invalidate(0, displayedMonth = displayedMonth.clone().subtract(1, "month"));
+ }
+
+ function resetDisplayedMonth() {
+ $$invalidate(0, displayedMonth = today.clone());
+ }
+
+ $$self.$$set = $$props => {
+ if ("localeData" in $$props) $$invalidate(17, localeData = $$props.localeData);
+ if ("showWeekNums" in $$props) $$invalidate(1, showWeekNums = $$props.showWeekNums);
+ if ("onHoverDay" in $$props) $$invalidate(2, onHoverDay = $$props.onHoverDay);
+ if ("onHoverWeek" in $$props) $$invalidate(3, onHoverWeek = $$props.onHoverWeek);
+ if ("onContextMenuDay" in $$props) $$invalidate(4, onContextMenuDay = $$props.onContextMenuDay);
+ if ("onContextMenuWeek" in $$props) $$invalidate(5, onContextMenuWeek = $$props.onContextMenuWeek);
+ if ("onClickDay" in $$props) $$invalidate(6, onClickDay = $$props.onClickDay);
+ if ("onClickWeek" in $$props) $$invalidate(7, onClickWeek = $$props.onClickWeek);
+ if ("sources" in $$props) $$invalidate(8, sources = $$props.sources);
+ if ("selectedId" in $$props) $$invalidate(9, selectedId = $$props.selectedId);
+ if ("today" in $$props) $$invalidate(10, today = $$props.today);
+ if ("displayedMonth" in $$props) $$invalidate(0, displayedMonth = $$props.displayedMonth);
+ };
+
+ $$self.$$.update = () => {
+ if ($$self.$$.dirty & /*displayedMonth, localeData*/ 131073) {
+ $$invalidate(14, month = getMonth(displayedMonth, localeData));
+ }
+
+ if ($$self.$$.dirty & /*today, localeData*/ 132096) {
+ $$invalidate(15, daysOfWeek = getDaysOfWeek(today, localeData));
+ }
+ };
+
+ return [
+ displayedMonth,
+ showWeekNums,
+ onHoverDay,
+ onHoverWeek,
+ onContextMenuDay,
+ onContextMenuWeek,
+ onClickDay,
+ onClickWeek,
+ sources,
+ selectedId,
+ today,
+ incrementDisplayedMonth,
+ decrementDisplayedMonth,
+ resetDisplayedMonth,
+ month,
+ daysOfWeek,
+ isMobile,
+ localeData
+ ];
+}
+
+class Calendar$1 extends SvelteComponent {
+ constructor(options) {
+ super();
+ if (!document.getElementById("svelte-pcimu8-style")) add_css();
+
+ init(this, options, instance$7, create_fragment$7, not_equal, {
+ localeData: 17,
+ showWeekNums: 1,
+ onHoverDay: 2,
+ onHoverWeek: 3,
+ onContextMenuDay: 4,
+ onContextMenuWeek: 5,
+ onClickDay: 6,
+ onClickWeek: 7,
+ sources: 8,
+ selectedId: 9,
+ today: 10,
+ displayedMonth: 0,
+ incrementDisplayedMonth: 11,
+ decrementDisplayedMonth: 12,
+ resetDisplayedMonth: 13
+ });
+ }
+
+ get incrementDisplayedMonth() {
+ return this.$$.ctx[11];
+ }
+
+ get decrementDisplayedMonth() {
+ return this.$$.ctx[12];
+ }
+
+ get resetDisplayedMonth() {
+ return this.$$.ctx[13];
+ }
+}
+
+const langToMomentLocale = {
+ en: "en-gb",
+ zh: "zh-cn",
+ "zh-TW": "zh-tw",
+ ru: "ru",
+ ko: "ko",
+ it: "it",
+ id: "id",
+ ro: "ro",
+ "pt-BR": "pt-br",
+ cz: "cs",
+ da: "da",
+ de: "de",
+ es: "es",
+ fr: "fr",
+ no: "nn",
+ pl: "pl",
+ pt: "pt",
+ tr: "tr",
+ hi: "hi",
+ nl: "nl",
+ ar: "ar",
+ ja: "ja",
+};
+const weekdays = [
+ "sunday",
+ "monday",
+ "tuesday",
+ "wednesday",
+ "thursday",
+ "friday",
+ "saturday",
+];
+function overrideGlobalMomentWeekStart(weekStart) {
+ const { moment } = window;
+ const currentLocale = moment.locale();
+ // Save the initial locale weekspec so that we can restore
+ // it when toggling between the different options in settings.
+ if (!window._bundledLocaleWeekSpec) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ window._bundledLocaleWeekSpec = moment.localeData()._week;
+ }
+ if (weekStart === "locale") {
+ moment.updateLocale(currentLocale, {
+ week: window._bundledLocaleWeekSpec,
+ });
+ }
+ else {
+ moment.updateLocale(currentLocale, {
+ week: {
+ dow: weekdays.indexOf(weekStart) || 0,
+ },
+ });
+ }
+}
+/**
+ * Sets the locale used by the calendar. This allows the calendar to
+ * default to the user's locale (e.g. Start Week on Sunday/Monday/Friday)
+ *
+ * @param localeOverride locale string (e.g. "en-US")
+ */
+function configureGlobalMomentLocale(localeOverride = "system-default", weekStart = "locale") {
+ var _a;
+ const obsidianLang = localStorage.getItem("language") || "en";
+ const systemLang = (_a = navigator.language) === null || _a === void 0 ? void 0 : _a.toLowerCase();
+ let momentLocale = langToMomentLocale[obsidianLang];
+ if (localeOverride !== "system-default") {
+ momentLocale = localeOverride;
+ }
+ else if (systemLang.startsWith(obsidianLang)) {
+ // If the system locale is more specific (en-gb vs en), use the system locale.
+ momentLocale = systemLang;
+ }
+ const currentLocale = window.moment.locale(momentLocale);
+ console.debug(`[Calendar] Trying to switch Moment.js global locale to ${momentLocale}, got ${currentLocale}`);
+ overrideGlobalMomentWeekStart(weekStart);
+ return currentLocale;
+}
+
+/* src/ui/Calendar.svelte generated by Svelte v3.35.0 */
+
+function create_fragment(ctx) {
+ let calendarbase;
+ let updating_displayedMonth;
+ let current;
+
+ function calendarbase_displayedMonth_binding(value) {
+ /*calendarbase_displayedMonth_binding*/ ctx[12](value);
+ }
+
+ let calendarbase_props = {
+ sources: /*sources*/ ctx[1],
+ today: /*today*/ ctx[9],
+ onHoverDay: /*onHoverDay*/ ctx[2],
+ onHoverWeek: /*onHoverWeek*/ ctx[3],
+ onContextMenuDay: /*onContextMenuDay*/ ctx[6],
+ onContextMenuWeek: /*onContextMenuWeek*/ ctx[7],
+ onClickDay: /*onClickDay*/ ctx[4],
+ onClickWeek: /*onClickWeek*/ ctx[5],
+ localeData: /*today*/ ctx[9].localeData(),
+ selectedId: /*$activeFile*/ ctx[10],
+ showWeekNums: /*$settings*/ ctx[8].showWeeklyNote
+ };
+
+ if (/*displayedMonth*/ ctx[0] !== void 0) {
+ calendarbase_props.displayedMonth = /*displayedMonth*/ ctx[0];
+ }
+
+ calendarbase = new Calendar$1({ props: calendarbase_props });
+ binding_callbacks$1.push(() => bind(calendarbase, "displayedMonth", calendarbase_displayedMonth_binding));
+
+ return {
+ c() {
+ create_component$1(calendarbase.$$.fragment);
+ },
+ m(target, anchor) {
+ mount_component$1(calendarbase, target, anchor);
+ current = true;
+ },
+ p(ctx, [dirty]) {
+ const calendarbase_changes = {};
+ if (dirty & /*sources*/ 2) calendarbase_changes.sources = /*sources*/ ctx[1];
+ if (dirty & /*today*/ 512) calendarbase_changes.today = /*today*/ ctx[9];
+ if (dirty & /*onHoverDay*/ 4) calendarbase_changes.onHoverDay = /*onHoverDay*/ ctx[2];
+ if (dirty & /*onHoverWeek*/ 8) calendarbase_changes.onHoverWeek = /*onHoverWeek*/ ctx[3];
+ if (dirty & /*onContextMenuDay*/ 64) calendarbase_changes.onContextMenuDay = /*onContextMenuDay*/ ctx[6];
+ if (dirty & /*onContextMenuWeek*/ 128) calendarbase_changes.onContextMenuWeek = /*onContextMenuWeek*/ ctx[7];
+ if (dirty & /*onClickDay*/ 16) calendarbase_changes.onClickDay = /*onClickDay*/ ctx[4];
+ if (dirty & /*onClickWeek*/ 32) calendarbase_changes.onClickWeek = /*onClickWeek*/ ctx[5];
+ if (dirty & /*today*/ 512) calendarbase_changes.localeData = /*today*/ ctx[9].localeData();
+ if (dirty & /*$activeFile*/ 1024) calendarbase_changes.selectedId = /*$activeFile*/ ctx[10];
+ if (dirty & /*$settings*/ 256) calendarbase_changes.showWeekNums = /*$settings*/ ctx[8].showWeeklyNote;
+
+ if (!updating_displayedMonth && dirty & /*displayedMonth*/ 1) {
+ updating_displayedMonth = true;
+ calendarbase_changes.displayedMonth = /*displayedMonth*/ ctx[0];
+ add_flush_callback(() => updating_displayedMonth = false);
+ }
+
+ calendarbase.$set(calendarbase_changes);
+ },
+ i(local) {
+ if (current) return;
+ transition_in$1(calendarbase.$$.fragment, local);
+ current = true;
+ },
+ o(local) {
+ transition_out$1(calendarbase.$$.fragment, local);
+ current = false;
+ },
+ d(detaching) {
+ destroy_component$1(calendarbase, detaching);
+ }
+ };
+}
+
+function instance($$self, $$props, $$invalidate) {
+ let $settings;
+ let $activeFile;
+ component_subscribe($$self, settings, $$value => $$invalidate(8, $settings = $$value));
+ component_subscribe($$self, activeFile, $$value => $$invalidate(10, $activeFile = $$value));
+
+
+ let today;
+ let { displayedMonth = today } = $$props;
+ let { sources } = $$props;
+ let { onHoverDay } = $$props;
+ let { onHoverWeek } = $$props;
+ let { onClickDay } = $$props;
+ let { onClickWeek } = $$props;
+ let { onContextMenuDay } = $$props;
+ let { onContextMenuWeek } = $$props;
+
+ function tick() {
+ $$invalidate(9, today = window.moment());
+ }
+
+ function getToday(settings) {
+ configureGlobalMomentLocale(settings.localeOverride, settings.weekStart);
+ dailyNotes.reindex();
+ weeklyNotes.reindex();
+ return window.moment();
+ }
+
+ // 1 minute heartbeat to keep `today` reflecting the current day
+ let heartbeat = setInterval(
+ () => {
+ tick();
+ const isViewingCurrentMonth = displayedMonth.isSame(today, "day");
+
+ if (isViewingCurrentMonth) {
+ // if it's midnight on the last day of the month, this will
+ // update the display to show the new month.
+ $$invalidate(0, displayedMonth = today);
+ }
+ },
+ 1000 * 60
+ );
+
+ onDestroy(() => {
+ clearInterval(heartbeat);
+ });
+
+ function calendarbase_displayedMonth_binding(value) {
+ displayedMonth = value;
+ $$invalidate(0, displayedMonth);
+ }
+
+ $$self.$$set = $$props => {
+ if ("displayedMonth" in $$props) $$invalidate(0, displayedMonth = $$props.displayedMonth);
+ if ("sources" in $$props) $$invalidate(1, sources = $$props.sources);
+ if ("onHoverDay" in $$props) $$invalidate(2, onHoverDay = $$props.onHoverDay);
+ if ("onHoverWeek" in $$props) $$invalidate(3, onHoverWeek = $$props.onHoverWeek);
+ if ("onClickDay" in $$props) $$invalidate(4, onClickDay = $$props.onClickDay);
+ if ("onClickWeek" in $$props) $$invalidate(5, onClickWeek = $$props.onClickWeek);
+ if ("onContextMenuDay" in $$props) $$invalidate(6, onContextMenuDay = $$props.onContextMenuDay);
+ if ("onContextMenuWeek" in $$props) $$invalidate(7, onContextMenuWeek = $$props.onContextMenuWeek);
+ };
+
+ $$self.$$.update = () => {
+ if ($$self.$$.dirty & /*$settings*/ 256) {
+ $$invalidate(9, today = getToday($settings));
+ }
+ };
+
+ return [
+ displayedMonth,
+ sources,
+ onHoverDay,
+ onHoverWeek,
+ onClickDay,
+ onClickWeek,
+ onContextMenuDay,
+ onContextMenuWeek,
+ $settings,
+ today,
+ $activeFile,
+ tick,
+ calendarbase_displayedMonth_binding
+ ];
+}
+
+class Calendar extends SvelteComponent$1 {
+ constructor(options) {
+ super();
+
+ init$1(this, options, instance, create_fragment, not_equal$1, {
+ displayedMonth: 0,
+ sources: 1,
+ onHoverDay: 2,
+ onHoverWeek: 3,
+ onClickDay: 4,
+ onClickWeek: 5,
+ onContextMenuDay: 6,
+ onContextMenuWeek: 7,
+ tick: 11
+ });
+ }
+
+ get tick() {
+ return this.$$.ctx[11];
+ }
+}
+
+function showFileMenu(app, file, position) {
+ const fileMenu = new obsidian.Menu(app);
+ fileMenu.addItem((item) => item
+ .setTitle("Delete")
+ .setIcon("trash")
+ .onClick(() => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ app.fileManager.promptForFileDeletion(file);
+ }));
+ app.workspace.trigger("file-menu", fileMenu, file, "calendar-context-menu", null);
+ fileMenu.showAtPosition(position);
+}
+
+const getStreakClasses = (file) => {
+ return classList({
+ "has-note": !!file,
+ });
+};
+const streakSource = {
+ getDailyMetadata: async (date) => {
+ const file = getDailyNote_1(date, get_store_value(dailyNotes));
+ return {
+ classes: getStreakClasses(file),
+ dots: [],
+ };
+ },
+ getWeeklyMetadata: async (date) => {
+ const file = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ return {
+ classes: getStreakClasses(file),
+ dots: [],
+ };
+ },
+};
+
+function getNoteTags(note) {
+ var _a;
+ if (!note) {
+ return [];
+ }
+ const { metadataCache } = window.app;
+ const frontmatter = (_a = metadataCache.getFileCache(note)) === null || _a === void 0 ? void 0 : _a.frontmatter;
+ const tags = [];
+ if (frontmatter) {
+ const frontmatterTags = obsidian.parseFrontMatterTags(frontmatter) || [];
+ tags.push(...frontmatterTags);
+ }
+ // strip the '#' at the beginning
+ return tags.map((tag) => tag.substring(1));
+}
+function getFormattedTagAttributes(note) {
+ const attrs = {};
+ const tags = getNoteTags(note);
+ const [emojiTags, nonEmojiTags] = partition(tags, (tag) => /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/.test(tag));
+ if (nonEmojiTags) {
+ attrs["data-tags"] = nonEmojiTags.join(" ");
+ }
+ if (emojiTags) {
+ attrs["data-emoji-tag"] = emojiTags[0];
+ }
+ return attrs;
+}
+const customTagsSource = {
+ getDailyMetadata: async (date) => {
+ const file = getDailyNote_1(date, get_store_value(dailyNotes));
+ return {
+ dataAttributes: getFormattedTagAttributes(file),
+ dots: [],
+ };
+ },
+ getWeeklyMetadata: async (date) => {
+ const file = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ return {
+ dataAttributes: getFormattedTagAttributes(file),
+ dots: [],
+ };
+ },
+};
+
+async function getNumberOfRemainingTasks(note) {
+ if (!note) {
+ return 0;
+ }
+ const { vault } = window.app;
+ const fileContents = await vault.cachedRead(note);
+ return (fileContents.match(/(-|\*) \[ \]/g) || []).length;
+}
+async function getDotsForDailyNote$1(dailyNote) {
+ if (!dailyNote) {
+ return [];
+ }
+ const numTasks = await getNumberOfRemainingTasks(dailyNote);
+ const dots = [];
+ if (numTasks) {
+ dots.push({
+ className: "task",
+ color: "default",
+ isFilled: false,
+ });
+ }
+ return dots;
+}
+const tasksSource = {
+ getDailyMetadata: async (date) => {
+ const file = getDailyNote_1(date, get_store_value(dailyNotes));
+ const dots = await getDotsForDailyNote$1(file);
+ return {
+ dots,
+ };
+ },
+ getWeeklyMetadata: async (date) => {
+ const file = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ const dots = await getDotsForDailyNote$1(file);
+ return {
+ dots,
+ };
+ },
+};
+
+const NUM_MAX_DOTS = 5;
+async function getWordLengthAsDots(note) {
+ const { wordsPerDot = DEFAULT_WORDS_PER_DOT } = get_store_value(settings);
+ if (!note || wordsPerDot <= 0) {
+ return 0;
+ }
+ const fileContents = await window.app.vault.cachedRead(note);
+ const wordCount = getWordCount(fileContents);
+ const numDots = wordCount / wordsPerDot;
+ return clamp(Math.floor(numDots), 1, NUM_MAX_DOTS);
+}
+async function getDotsForDailyNote(dailyNote) {
+ if (!dailyNote) {
+ return [];
+ }
+ const numSolidDots = await getWordLengthAsDots(dailyNote);
+ const dots = [];
+ for (let i = 0; i < numSolidDots; i++) {
+ dots.push({
+ color: "default",
+ isFilled: true,
+ });
+ }
+ return dots;
+}
+const wordCountSource = {
+ getDailyMetadata: async (date) => {
+ const file = getDailyNote_1(date, get_store_value(dailyNotes));
+ const dots = await getDotsForDailyNote(file);
+ return {
+ dots,
+ };
+ },
+ getWeeklyMetadata: async (date) => {
+ const file = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ const dots = await getDotsForDailyNote(file);
+ return {
+ dots,
+ };
+ },
+};
+
+class CalendarView extends obsidian.ItemView {
+ constructor(leaf) {
+ super(leaf);
+ this.openOrCreateDailyNote = this.openOrCreateDailyNote.bind(this);
+ this.openOrCreateWeeklyNote = this.openOrCreateWeeklyNote.bind(this);
+ this.onNoteSettingsUpdate = this.onNoteSettingsUpdate.bind(this);
+ this.onFileCreated = this.onFileCreated.bind(this);
+ this.onFileDeleted = this.onFileDeleted.bind(this);
+ this.onFileModified = this.onFileModified.bind(this);
+ this.onFileOpen = this.onFileOpen.bind(this);
+ this.onHoverDay = this.onHoverDay.bind(this);
+ this.onHoverWeek = this.onHoverWeek.bind(this);
+ this.onContextMenuDay = this.onContextMenuDay.bind(this);
+ this.onContextMenuWeek = this.onContextMenuWeek.bind(this);
+ this.registerEvent(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ this.app.workspace.on("periodic-notes:settings-updated", this.onNoteSettingsUpdate));
+ this.registerEvent(this.app.vault.on("create", this.onFileCreated));
+ this.registerEvent(this.app.vault.on("delete", this.onFileDeleted));
+ this.registerEvent(this.app.vault.on("modify", this.onFileModified));
+ this.registerEvent(this.app.workspace.on("file-open", this.onFileOpen));
+ this.settings = null;
+ settings.subscribe((val) => {
+ this.settings = val;
+ // Refresh the calendar if settings change
+ if (this.calendar) {
+ this.calendar.tick();
+ }
+ });
+ }
+ getViewType() {
+ return VIEW_TYPE_CALENDAR;
+ }
+ getDisplayText() {
+ return "Calendar";
+ }
+ getIcon() {
+ return "calendar-with-checkmark";
+ }
+ onClose() {
+ if (this.calendar) {
+ this.calendar.$destroy();
+ }
+ return Promise.resolve();
+ }
+ async onOpen() {
+ // Integration point: external plugins can listen for `calendar:open`
+ // to feed in additional sources.
+ const sources = [
+ customTagsSource,
+ streakSource,
+ wordCountSource,
+ tasksSource,
+ ];
+ this.app.workspace.trigger(TRIGGER_ON_OPEN, sources);
+ this.calendar = new Calendar({
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ target: this.contentEl,
+ props: {
+ onClickDay: this.openOrCreateDailyNote,
+ onClickWeek: this.openOrCreateWeeklyNote,
+ onHoverDay: this.onHoverDay,
+ onHoverWeek: this.onHoverWeek,
+ onContextMenuDay: this.onContextMenuDay,
+ onContextMenuWeek: this.onContextMenuWeek,
+ sources,
+ },
+ });
+ }
+ onHoverDay(date, targetEl, isMetaPressed) {
+ if (!isMetaPressed) {
+ return;
+ }
+ const { format } = getDailyNoteSettings_1();
+ const note = getDailyNote_1(date, get_store_value(dailyNotes));
+ this.app.workspace.trigger("link-hover", this, targetEl, date.format(format), note === null || note === void 0 ? void 0 : note.path);
+ }
+ onHoverWeek(date, targetEl, isMetaPressed) {
+ if (!isMetaPressed) {
+ return;
+ }
+ const note = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ const { format } = getWeeklyNoteSettings_1();
+ this.app.workspace.trigger("link-hover", this, targetEl, date.format(format), note === null || note === void 0 ? void 0 : note.path);
+ }
+ onContextMenuDay(date, event) {
+ const note = getDailyNote_1(date, get_store_value(dailyNotes));
+ if (!note) {
+ // If no file exists for a given day, show nothing.
+ return;
+ }
+ showFileMenu(this.app, note, {
+ x: event.pageX,
+ y: event.pageY,
+ });
+ }
+ onContextMenuWeek(date, event) {
+ const note = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ if (!note) {
+ // If no file exists for a given day, show nothing.
+ return;
+ }
+ showFileMenu(this.app, note, {
+ x: event.pageX,
+ y: event.pageY,
+ });
+ }
+ onNoteSettingsUpdate() {
+ dailyNotes.reindex();
+ weeklyNotes.reindex();
+ this.updateActiveFile();
+ }
+ async onFileDeleted(file) {
+ if (getDateFromFile_1(file, "day")) {
+ dailyNotes.reindex();
+ this.updateActiveFile();
+ }
+ if (getDateFromFile_1(file, "week")) {
+ weeklyNotes.reindex();
+ this.updateActiveFile();
+ }
+ }
+ async onFileModified(file) {
+ const date = getDateFromFile_1(file, "day") || getDateFromFile_1(file, "week");
+ if (date && this.calendar) {
+ this.calendar.tick();
+ }
+ }
+ onFileCreated(file) {
+ if (this.app.workspace.layoutReady && this.calendar) {
+ if (getDateFromFile_1(file, "day")) {
+ dailyNotes.reindex();
+ this.calendar.tick();
+ }
+ if (getDateFromFile_1(file, "week")) {
+ weeklyNotes.reindex();
+ this.calendar.tick();
+ }
+ }
+ }
+ onFileOpen(_file) {
+ if (this.app.workspace.layoutReady) {
+ this.updateActiveFile();
+ }
+ }
+ updateActiveFile() {
+ const { view } = this.app.workspace.activeLeaf;
+ let file = null;
+ if (view instanceof obsidian.FileView) {
+ file = view.file;
+ }
+ activeFile.setFile(file);
+ if (this.calendar) {
+ this.calendar.tick();
+ }
+ }
+ revealActiveNote() {
+ const { moment } = window;
+ const { activeLeaf } = this.app.workspace;
+ if (activeLeaf.view instanceof obsidian.FileView) {
+ // Check to see if the active note is a daily-note
+ let date = getDateFromFile_1(activeLeaf.view.file, "day");
+ if (date) {
+ this.calendar.$set({ displayedMonth: date });
+ return;
+ }
+ // Check to see if the active note is a weekly-note
+ const { format } = getWeeklyNoteSettings_1();
+ date = moment(activeLeaf.view.file.basename, format, true);
+ if (date.isValid()) {
+ this.calendar.$set({ displayedMonth: date });
+ return;
+ }
+ }
+ }
+ async openOrCreateWeeklyNote(date, inNewSplit) {
+ const { workspace } = this.app;
+ const startOfWeek = date.clone().startOf("week");
+ const existingFile = getWeeklyNote_1(date, get_store_value(weeklyNotes));
+ if (!existingFile) {
+ // File doesn't exist
+ tryToCreateWeeklyNote(startOfWeek, inNewSplit, this.settings, (file) => {
+ activeFile.setFile(file);
+ });
+ return;
+ }
+ const leaf = inNewSplit
+ ? workspace.splitActiveLeaf()
+ : workspace.getUnpinnedLeaf();
+ await leaf.openFile(existingFile);
+ activeFile.setFile(existingFile);
+ }
+ async openOrCreateDailyNote(date, inNewSplit) {
+ const { workspace } = this.app;
+ const existingFile = getDailyNote_1(date, get_store_value(dailyNotes));
+ if (!existingFile) {
+ // File doesn't exist
+ tryToCreateDailyNote(date, inNewSplit, this.settings, (dailyNote) => {
+ activeFile.setFile(dailyNote);
+ });
+ return;
+ }
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const mode = this.app.vault.getConfig("defaultViewMode");
+ const leaf = inNewSplit
+ ? workspace.splitActiveLeaf()
+ : workspace.getUnpinnedLeaf();
+ await leaf.openFile(existingFile, { mode });
+ activeFile.setFile(existingFile);
+ }
+}
+
+class CalendarPlugin extends obsidian.Plugin {
+ onunload() {
+ this.app.workspace
+ .getLeavesOfType(VIEW_TYPE_CALENDAR)
+ .forEach((leaf) => leaf.detach());
+ }
+ async onload() {
+ this.register(settings.subscribe((value) => {
+ this.options = value;
+ }));
+ this.registerView(VIEW_TYPE_CALENDAR, (leaf) => (this.view = new CalendarView(leaf)));
+ this.addCommand({
+ id: "show-calendar-view",
+ name: "Open view",
+ checkCallback: (checking) => {
+ if (checking) {
+ return (this.app.workspace.getLeavesOfType(VIEW_TYPE_CALENDAR).length === 0);
+ }
+ this.initLeaf();
+ },
+ });
+ this.addCommand({
+ id: "open-weekly-note",
+ name: "Open Weekly Note",
+ checkCallback: (checking) => {
+ if (checking) {
+ return !appHasPeriodicNotesPluginLoaded();
+ }
+ this.view.openOrCreateWeeklyNote(window.moment(), false);
+ },
+ });
+ this.addCommand({
+ id: "reveal-active-note",
+ name: "Reveal active note",
+ callback: () => this.view.revealActiveNote(),
+ });
+ await this.loadOptions();
+ this.addSettingTab(new CalendarSettingsTab(this.app, this));
+ if (this.app.workspace.layoutReady) {
+ this.initLeaf();
+ }
+ else {
+ this.registerEvent(this.app.workspace.on("layout-ready", this.initLeaf.bind(this)));
+ }
+ }
+ initLeaf() {
+ if (this.app.workspace.getLeavesOfType(VIEW_TYPE_CALENDAR).length) {
+ return;
+ }
+ this.app.workspace.getRightLeaf(false).setViewState({
+ type: VIEW_TYPE_CALENDAR,
+ });
+ }
+ async loadOptions() {
+ const options = await this.loadData();
+ settings.update((old) => {
+ return Object.assign(Object.assign({}, old), (options || {}));
+ });
+ await this.saveData(this.options);
+ }
+ async writeOptions(changeOpts) {
+ settings.update((old) => (Object.assign(Object.assign({}, old), changeOpts(old))));
+ await this.saveData(this.options);
+ }
+}
+
+module.exports = CalendarPlugin;
+
+/* nosourcemap */
\ No newline at end of file
diff --git a/liph/.obsidian/plugins/calendar/manifest.json b/liph/.obsidian/plugins/calendar/manifest.json
new file mode 100755
index 0000000..028bfa5
--- /dev/null
+++ b/liph/.obsidian/plugins/calendar/manifest.json
@@ -0,0 +1,10 @@
+{
+ "id": "calendar",
+ "name": "Calendar",
+ "description": "Calendar view of your daily notes",
+ "version": "1.5.10",
+ "author": "Liam Cain",
+ "authorUrl": "https://github.com/liamcain/",
+ "isDesktopOnly": false,
+ "minAppVersion": "0.9.11"
+}
diff --git a/liph/.obsidian/plugins/colored-tags/data.json b/liph/.obsidian/plugins/colored-tags/data.json
new file mode 100644
index 0000000..7e5bcc9
--- /dev/null
+++ b/liph/.obsidian/plugins/colored-tags/data.json
@@ -0,0 +1,33 @@
+{
+ "palette": {
+ "seed": 0,
+ "selected": "adaptive-soft",
+ "custom": "e12729-f37324-f8cc1b-72b043-007f4e"
+ },
+ "mixColors": true,
+ "transition": true,
+ "accessibility": {
+ "highTextContrast": false
+ },
+ "knownTags": {
+ "anette": 1,
+ "camper": 2,
+ "daily": 3,
+ "diemaske": 4,
+ "finance": 5,
+ "legung": 6,
+ "meetings": 7,
+ "note": 8,
+ "notes": 9,
+ "personal": 10,
+ "project": 11,
+ "runenorakel": 12
+ },
+ "tagColors": {
+ "breath": 1,
+ "camper": 0,
+ "finance": 2,
+ "personal": 4
+ },
+ "_version": 4
+}
\ No newline at end of file
diff --git a/liph/.obsidian/plugins/colored-tags/main.js b/liph/.obsidian/plugins/colored-tags/main.js
new file mode 100755
index 0000000..f3c9b15
--- /dev/null
+++ b/liph/.obsidian/plugins/colored-tags/main.js
@@ -0,0 +1,5388 @@
+/*
+THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
+if you want to view the source, please visit the github repository of this plugin
+*/
+
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __hasOwnProp = Object.prototype.hasOwnProperty;
+var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
+var __export = (target, all) => {
+ for (var name in all)
+ __defProp(target, name, { get: all[name], enumerable: true });
+};
+var __copyProps = (to2, from, except, desc) => {
+ if (from && typeof from === "object" || typeof from === "function") {
+ for (let key of __getOwnPropNames(from))
+ if (!__hasOwnProp.call(to2, key) && key !== except)
+ __defProp(to2, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
+ }
+ return to2;
+};
+var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+var __publicField = (obj, key, value) => {
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
+ return value;
+};
+
+// src/main.ts
+var main_exports = {};
+__export(main_exports, {
+ default: () => ColoredTagsPlugin
+});
+module.exports = __toCommonJS(main_exports);
+var import_obsidian3 = require("obsidian");
+
+// src/ColoredClassApplierPlugin.ts
+var import_view = require("@codemirror/view");
+
+// src/tag-appliers/TagApplier.ts
+var COLORED_TAG_PREFIX = "colored-tag-";
+function normalizeTagText(text) {
+ if (!text) {
+ return null;
+ }
+ const trimmed = text.trim();
+ const normalized = trimmed.replace(/^#/, "");
+ return normalized.length ? normalized.toLowerCase() : null;
+}
+function buildColoredTagClass(tagName) {
+ return `${COLORED_TAG_PREFIX}${tagName}`;
+}
+function cleanupColoredTagClasses(targets, newClassName) {
+ for (const el of targets) {
+ for (const cls of Array.from(el.classList)) {
+ if (cls.startsWith(COLORED_TAG_PREFIX) && cls !== newClassName) {
+ el.classList.remove(cls);
+ }
+ }
+ }
+}
+function applyColoredTagClass(targets, tagText) {
+ const normalizedTag = normalizeTagText(tagText);
+ if (!normalizedTag) {
+ return;
+ }
+ const className = buildColoredTagClass(normalizedTag);
+ cleanupColoredTagClasses(targets, className);
+ for (const el of targets) {
+ if (!el.classList.contains(className)) {
+ el.classList.add(className);
+ }
+ }
+}
+var defaultTagTextGetter = (el) => {
+ const text = el.textContent;
+ return text ? text.trim() : null;
+};
+function applyColoredTagClassesInRoot(root, selector, getTagText = defaultTagTextGetter, getTagTargets = (el) => [el]) {
+ const candidates = [];
+ if (root instanceof HTMLElement && root.matches(selector)) {
+ candidates.push(root);
+ }
+ root.querySelectorAll(selector).forEach((el) => {
+ candidates.push(el);
+ });
+ for (const el of candidates) {
+ applyColoredTagClass(getTagTargets(el), getTagText(el));
+ }
+}
+var TagApplier = class {
+ constructor(options) {
+ this.pendingNodes = /* @__PURE__ */ new Set();
+ this.flushHandle = null;
+ var _a, _b;
+ this.selector = options.selector;
+ this.getTagText = (_a = options.getTagText) != null ? _a : defaultTagTextGetter;
+ this.getTagTargets = (_b = options.getTagTargets) != null ? _b : (el) => [el];
+ }
+ apply(root = document.body) {
+ applyColoredTagClassesInRoot(
+ root,
+ this.selector,
+ this.getTagText,
+ this.getTagTargets
+ );
+ }
+ start(root = document.body) {
+ this.stop();
+ this.apply(root);
+ this.observer = new MutationObserver((mutations) => {
+ for (const mutation of mutations) {
+ this.handleMutation(mutation);
+ }
+ });
+ this.observer.observe(root, {
+ childList: true,
+ subtree: true,
+ characterData: true
+ });
+ }
+ stop() {
+ var _a;
+ (_a = this.observer) == null ? void 0 : _a.disconnect();
+ this.observer = void 0;
+ }
+ handleMutation(mutation) {
+ if (mutation.type === "characterData") {
+ const parent = mutation.target.parentNode;
+ if (parent) {
+ this.scheduleApply(parent);
+ }
+ return;
+ }
+ mutation.addedNodes.forEach((node) => {
+ if (node instanceof HTMLElement || node instanceof DocumentFragment) {
+ this.scheduleApply(node);
+ }
+ });
+ }
+ scheduleApply(target) {
+ this.pendingNodes.add(target);
+ if (this.flushHandle !== null) {
+ return;
+ }
+ const requestId = window.requestAnimationFrame(() => {
+ this.flushHandle = null;
+ const nodes = Array.from(this.pendingNodes);
+ this.pendingNodes.clear();
+ for (const node of nodes) {
+ this.apply(node);
+ }
+ });
+ this.flushHandle = requestId;
+ if (this.pendingNodes.size === 0) {
+ this.flushHandle = null;
+ }
+ }
+};
+
+// src/ColoredClassApplierPlugin.ts
+function applyColoredClasses(domElement) {
+ const nodes = Array.from(
+ domElement.getElementsByClassName("cm-hashtag")
+ );
+ let currentTagNodes = [];
+ for (const el of nodes) {
+ const text = el.innerText.trim();
+ if (text === "#") {
+ currentTagNodes = [el];
+ continue;
+ }
+ if (!currentTagNodes.length) {
+ continue;
+ }
+ currentTagNodes.push(el);
+ const combinedText = currentTagNodes.map((node) => node.innerText).join("");
+ if (!normalizeTagText(combinedText)) {
+ currentTagNodes = [];
+ continue;
+ }
+ applyColoredTagClass(currentTagNodes, combinedText);
+ }
+}
+var coloredClassApplierPlugin = import_view.ViewPlugin.fromClass(
+ class {
+ constructor(view) {
+ applyColoredClasses(view.dom);
+ }
+ update(update) {
+ applyColoredClasses(update.view.dom);
+ }
+ }
+);
+
+// src/tag-appliers/BaseViewTagApplier.ts
+var BASE_TAG_SELECTOR = '.bases-table a.tag, .bases-table-container a.tag, .value-list-container a.tag, a.tag[href^="#"]';
+function getTagNameFromElement(el) {
+ return normalizeTagText(el.textContent);
+}
+var singleUseApplier = new TagApplier({
+ selector: BASE_TAG_SELECTOR,
+ getTagText: getTagNameFromElement
+});
+var BaseViewTagApplier = class {
+ constructor() {
+ this.applier = new TagApplier({
+ selector: BASE_TAG_SELECTOR,
+ getTagText: getTagNameFromElement
+ });
+ }
+ start(root = document.body) {
+ this.applier.start(root);
+ }
+ stop() {
+ this.applier.stop();
+ }
+};
+
+// src/ColoredTagsPluginSettingTab.ts
+var import_obsidian2 = require("obsidian");
+
+// src/defaultSettings.ts
+var DEFAULT_SETTINGS = {
+ palette: {
+ seed: 0,
+ selected: "adaptive-soft" /* ADAPTIVE_SOFT */,
+ custom: "e12729-f37324-f8cc1b-72b043-007f4e"
+ },
+ mixColors: true,
+ transition: true,
+ accessibility: {
+ highTextContrast: false
+ },
+ knownTags: {},
+ tagColors: {},
+ _version: 4
+};
+
+// src/i18n/de.json
+var de_default = {
+ settings: {
+ palette: {
+ heading: "Palette",
+ description: "Palette ausw\xE4hlen",
+ options: {
+ adaptiveSoft: "\u{1F338} Adaptiv (weich)",
+ adaptiveBright: "\u{1F33A} Adaptiv (kr\xE4ftig)",
+ custom: "Benutzerdefiniert"
+ },
+ shift: {
+ name: "Palettenverschiebung",
+ description: "Wenn die Farben einiger Tags nicht passen, kannst du die Palette verschieben."
+ },
+ custom: {
+ name: "Benutzerdefinierte Palette",
+ placeholder: "Palette einf\xFCgen",
+ description: "Das Format ist XXXXXX-XXXXXX-XXXXXX f\xFCr jede RGB-Farbe.",
+ community: {
+ heading: "Community-Paletten",
+ description: "Hier findest du Paletten aus der GitHub-Diskussion. Klicke auf eine Karte, um sie sofort zu \xFCbernehmen oder {{communityLinkStart}}teile deine eigene{{communityLinkEnd}}.",
+ loading: "Paletten werden geladen\u2026",
+ error: "Paletten konnten nicht geladen werden. \xD6ffne die Diskussion, um sie manuell anzusehen.",
+ empty: "Noch keine Community-Paletten vorhanden. Sei der Erste, der eine teilt!",
+ applyHint: "Klicke, um diese Palette anzuwenden"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Barrierefreiheit",
+ description: "Barrierefreiheitsoptionen anzeigen",
+ highTextContrast: {
+ name: "Hoher Textkontrast",
+ description: "Nur wei\xDFe und schwarze Textfarben verwenden"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimentell",
+ description: "Gef\xE4hrliche Aktionen oder extrem instabile Optionen, die jederzeit ge\xE4ndert oder entfernt werden k\xF6nnen",
+ mixColors: {
+ name: "Farben mischen",
+ description: "Hilft dabei, den Text lesbar zu machen"
+ },
+ gradientTransition: {
+ name: "Gradienten\xFCbergang"
+ },
+ tagColors: {
+ name: "Farbzuweisungen",
+ description: "W\xE4hle eine Farbe aus der aktuellen Palette. Beim Wechsel der Palette passt sich der Tag automatisch an.",
+ placeholder: "Tag eingeben",
+ clear: "Farbe zur\xFCcksetzen",
+ empty: "Noch keine Farbzuweisungen",
+ applyHint: "Farbe anwenden"
+ },
+ reset: {
+ name: "Konfiguration zur\xFCcksetzen",
+ description: "\u{1F6A8} Alle Farben aller Tags werden neu berechnet, als w\xE4re es der erste Start des Plugins. Erfordert einen Neustart von Obsidian.",
+ button: "Zur\xFCcksetzen"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: Eine neue Version ist verf\xFCgbar",
+ resetDone: "\u2705 Zur\xFCcksetzen abgeschlossen\nBitte Obsidian neu starten",
+ communityPaletteApplied: "\u{1F3A8} Community-Palette von {{author}} erfolgreich angewendet"
+ }
+};
+
+// src/i18n/en.json
+var en_default = {
+ settings: {
+ palette: {
+ heading: "Palette",
+ description: "Select palette",
+ options: {
+ adaptiveSoft: "\u{1F338} Adaptive soft",
+ adaptiveBright: "\u{1F33A} Adaptive bright",
+ custom: "Custom"
+ },
+ shift: {
+ name: "Palette shift",
+ description: "If the colors of some tags don't fit, you can shift the palette."
+ },
+ custom: {
+ name: "Custom palette",
+ placeholder: "Paste palette",
+ description: "The format is XXXXXX-XXXXXX-XXXXXX for each RGB color.",
+ community: {
+ heading: "Community palettes",
+ description: "Below are palettes collected from the GitHub discussion. Click any card to apply it instantly or {{communityLinkStart}}share your own{{communityLinkEnd}}.",
+ loading: "Loading palettes\u2026",
+ error: "Unable to load palettes right now. Feel free to open the discussion to browse them manually.",
+ empty: "No shared palettes yet. Be the first to post one!",
+ applyHint: "Click to apply this palette"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accessibility",
+ description: "Show accessibility options",
+ highTextContrast: {
+ name: "High text contrast",
+ description: "Use only white and black colors for texts"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimental",
+ description: "Dangerous actions or insanely unstable options that could be changed or removed in any time",
+ mixColors: {
+ name: "Mix colors",
+ description: "It helps to make text readable"
+ },
+ gradientTransition: {
+ name: "Gradient transition"
+ },
+ tagColors: {
+ name: "Tag assignments",
+ description: "Choose a color from the current palette. If you change the palette, the tag adapts to the nearest color.",
+ placeholder: "Start typing a tag",
+ clear: "Reset color",
+ empty: "No tag overrides yet",
+ applyHint: "Apply color"
+ },
+ reset: {
+ name: "Reset config",
+ description: "\u{1F6A8} All colors of all tags will be recalculated as if it was the first launch of the plugin. Requires restart of Obsidian.",
+ button: "Reset"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: a new version is available",
+ resetDone: "\u2705 Reset is done\nPlease restart Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Applied community palette by {{author}}"
+ }
+};
+
+// src/i18n/es.json
+var es_default = {
+ settings: {
+ palette: {
+ heading: "Paleta",
+ description: "Seleccionar paleta",
+ options: {
+ adaptiveSoft: "\u{1F338} Suave adaptable",
+ adaptiveBright: "\u{1F33A} Brillante adaptable",
+ custom: "Personalizada"
+ },
+ shift: {
+ name: "Desplazamiento de paleta",
+ description: "Si los colores de algunas etiquetas no encajan, puedes desplazar la paleta."
+ },
+ custom: {
+ name: "Paleta personalizada",
+ placeholder: "Pegar paleta",
+ description: "El formato es XXXXXX-XXXXXX-XXXXXX para cada color RGB.",
+ community: {
+ heading: "Paletas de la comunidad",
+ description: "A continuaci\xF3n hay paletas recopiladas de la discusi\xF3n en GitHub. Haz clic en cualquier tarjeta para aplicarla al instante o {{communityLinkStart}}comparte la tuya{{communityLinkEnd}}.",
+ loading: "Cargando paletas\u2026",
+ error: "No se pueden cargar las paletas en este momento. Si\xE9ntete libre de abrir la discusi\xF3n para explorarlas manualmente.",
+ empty: "A\xFAn no hay paletas compartidas. \xA1S\xE9 el primero en publicar una!",
+ applyHint: "Haz clic para aplicar esta paleta"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accesibilidad",
+ description: "Mostrar opciones de accesibilidad",
+ highTextContrast: {
+ name: "Alto contraste de texto",
+ description: "Usar solo colores blanco y negro para los textos"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimental",
+ description: "Acciones peligrosas u opciones inestables que podr\xEDan cambiarse o eliminarse en cualquier momento",
+ mixColors: {
+ name: "Mezclar colores",
+ description: "Ayuda a hacer el texto legible"
+ },
+ gradientTransition: {
+ name: "Transici\xF3n de degradado"
+ },
+ tagColors: {
+ name: "Asignaciones de etiquetas",
+ description: "Elige un color de la paleta actual. Si cambias la paleta, la etiqueta se adapta al color m\xE1s cercano.",
+ placeholder: "Empieza a escribir una etiqueta",
+ clear: "Restablecer color",
+ empty: "A\xFAn no hay anulaciones de etiquetas",
+ applyHint: "Aplicar color"
+ },
+ reset: {
+ name: "Restablecer configuraci\xF3n",
+ description: "\u{1F6A8} Todos los colores de todas las etiquetas se recalcular\xE1n como si fuera el primer lanzamiento del plugin. Requiere reiniciar Obsidian.",
+ button: "Restablecer"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: hay una nueva versi\xF3n disponible",
+ resetDone: "\u2705 Restablecimiento completado\nPor favor, reinicia Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Paleta de la comunidad aplicada por {{author}}"
+ }
+};
+
+// src/i18n/fr.json
+var fr_default = {
+ settings: {
+ palette: {
+ heading: "Palette",
+ description: "S\xE9lectionner une palette",
+ options: {
+ adaptiveSoft: "\u{1F338} Doux adaptatif",
+ adaptiveBright: "\u{1F33A} Vif adaptatif",
+ custom: "Personnalis\xE9e"
+ },
+ shift: {
+ name: "D\xE9calage de palette",
+ description: "Si les couleurs de certaines \xE9tiquettes ne conviennent pas, vous pouvez d\xE9caler la palette."
+ },
+ custom: {
+ name: "Palette personnalis\xE9e",
+ placeholder: "Coller une palette",
+ description: "Le format est XXXXXX-XXXXXX-XXXXXX pour chaque couleur RVB.",
+ community: {
+ heading: "Palettes communautaires",
+ description: "Ci-dessous se trouvent des palettes collect\xE9es depuis la discussion GitHub. Cliquez sur une carte pour l'appliquer instantan\xE9ment ou {{communityLinkStart}}partagez la v\xF4tre{{communityLinkEnd}}.",
+ loading: "Chargement des palettes\u2026",
+ error: "Impossible de charger les palettes pour le moment. N'h\xE9sitez pas \xE0 ouvrir la discussion pour les parcourir manuellement.",
+ empty: "Aucune palette partag\xE9e pour l'instant. Soyez le premier \xE0 en publier une !",
+ applyHint: "Cliquez pour appliquer cette palette"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accessibilit\xE9",
+ description: "Afficher les options d'accessibilit\xE9",
+ highTextContrast: {
+ name: "Contraste de texte \xE9lev\xE9",
+ description: "Utiliser uniquement le blanc et le noir pour les textes"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Exp\xE9rimental",
+ description: "Actions dangereuses ou options instables qui pourraient \xEAtre modifi\xE9es ou supprim\xE9es \xE0 tout moment",
+ mixColors: {
+ name: "M\xE9langer les couleurs",
+ description: "Cela aide \xE0 rendre le texte lisible"
+ },
+ gradientTransition: {
+ name: "Transition de d\xE9grad\xE9"
+ },
+ tagColors: {
+ name: "Affectations d'\xE9tiquettes",
+ description: "Choisissez une couleur de la palette actuelle. Si vous changez la palette, l'\xE9tiquette s'adapte \xE0 la couleur la plus proche.",
+ placeholder: "Commencez \xE0 taper une \xE9tiquette",
+ clear: "R\xE9initialiser la couleur",
+ empty: "Aucune substitution d'\xE9tiquette pour l'instant",
+ applyHint: "Appliquer la couleur"
+ },
+ reset: {
+ name: "R\xE9initialiser la config",
+ description: "\u{1F6A8} Toutes les couleurs de toutes les \xE9tiquettes seront recalcul\xE9es comme lors du premier lancement du plugin. N\xE9cessite le red\xE9marrage d'Obsidian.",
+ button: "R\xE9initialiser"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}} : une nouvelle version est disponible",
+ resetDone: "\u2705 R\xE9initialisation termin\xE9e\nVeuillez red\xE9marrer Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Palette communautaire appliqu\xE9e par {{author}}"
+ }
+};
+
+// src/i18n/it.json
+var it_default = {
+ settings: {
+ palette: {
+ heading: "Tavolozza",
+ description: "Seleziona tavolozza",
+ options: {
+ adaptiveSoft: "\u{1F338} Adattiva tenue",
+ adaptiveBright: "\u{1F33A} Adattiva vivace",
+ custom: "Personalizzata"
+ },
+ shift: {
+ name: "Spostamento tavolozza",
+ description: "Se i colori di alcune etichette non vanno bene, puoi spostare la tavolozza."
+ },
+ custom: {
+ name: "Tavolozza personalizzata",
+ placeholder: "Incolla tavolozza",
+ description: "Il formato \xE8 XXXXXX-XXXXXX-XXXXXX per ogni colore RGB.",
+ community: {
+ heading: "Tavolozze della comunit\xE0",
+ description: "Di seguito sono riportate le tavolozze raccolte dalla discussione su GitHub. Clicca su qualsiasi scheda per applicarla istantaneamente o {{communityLinkStart}}condividi la tua{{communityLinkEnd}}.",
+ loading: "Caricamento tavolozze\u2026",
+ error: "Impossibile caricare le tavolozze al momento. Sentiti libero di aprire la discussione per sfogliarle manualmente.",
+ empty: "Nessuna tavolozza condivisa ancora. Sii il primo a pubblicarne una!",
+ applyHint: "Clicca per applicare questa tavolozza"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Accessibilit\xE0",
+ description: "Mostra opzioni di accessibilit\xE0",
+ highTextContrast: {
+ name: "Alto contrasto del testo",
+ description: "Usa solo bianco e nero per i testi"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Sperimentale",
+ description: "Azioni pericolose o opzioni instabili che potrebbero essere modificate o rimosse in qualsiasi momento",
+ mixColors: {
+ name: "Mescola colori",
+ description: "Aiuta a rendere leggibile il testo"
+ },
+ gradientTransition: {
+ name: "Transizione gradiente"
+ },
+ tagColors: {
+ name: "Assegnazioni etichette",
+ description: "Scegli un colore dalla tavolozza corrente. Se cambi la tavolozza, l'etichetta si adatta al colore pi\xF9 vicino.",
+ placeholder: "Inizia a digitare un'etichetta",
+ clear: "Reimposta colore",
+ empty: "Nessuna sovrascrittura etichetta ancora",
+ applyHint: "Applica colore"
+ },
+ reset: {
+ name: "Reimposta configurazione",
+ description: "\u{1F6A8} Tutti i colori di tutte le etichette verranno ricalcolati come se fosse il primo avvio del plugin. Richiede il riavvio di Obsidian.",
+ button: "Reimposta"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: \xE8 disponibile una nuova versione",
+ resetDone: "\u2705 Reimpostazione completata\nSi prega di riavviare Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Tavolozza della comunit\xE0 applicata da {{author}}"
+ }
+};
+
+// src/i18n/ja.json
+var ja_default = {
+ settings: {
+ palette: {
+ heading: "\u30D1\u30EC\u30C3\u30C8",
+ description: "\u30D1\u30EC\u30C3\u30C8\u3092\u9078\u629E",
+ options: {
+ adaptiveSoft: "\u{1F338} \u30A2\u30C0\u30D7\u30C6\u30A3\u30D6\u30BD\u30D5\u30C8",
+ adaptiveBright: "\u{1F33A} \u30A2\u30C0\u30D7\u30C6\u30A3\u30D6\u30D6\u30E9\u30A4\u30C8",
+ custom: "\u30AB\u30B9\u30BF\u30E0"
+ },
+ shift: {
+ name: "\u30D1\u30EC\u30C3\u30C8\u30B7\u30D5\u30C8",
+ description: "\u4E00\u90E8\u306E\u30BF\u30B0\u306E\u8272\u304C\u5408\u308F\u306A\u3044\u5834\u5408\u306F\u3001\u30D1\u30EC\u30C3\u30C8\u3092\u30B7\u30D5\u30C8\u3067\u304D\u307E\u3059\u3002"
+ },
+ custom: {
+ name: "\u30AB\u30B9\u30BF\u30E0\u30D1\u30EC\u30C3\u30C8",
+ placeholder: "\u30D1\u30EC\u30C3\u30C8\u3092\u8CBC\u308A\u4ED8\u3051",
+ description: "\u5F62\u5F0F\u306F\u5404RGB\u30AB\u30E9\u30FC\u306B\u3064\u304D XXXXXX-XXXXXX-XXXXXX \u3067\u3059\u3002",
+ community: {
+ heading: "\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u30D1\u30EC\u30C3\u30C8",
+ description: "\u4EE5\u4E0B\u306FGitHub\u306E\u30C7\u30A3\u30B9\u30AB\u30C3\u30B7\u30E7\u30F3\u304B\u3089\u53CE\u96C6\u3055\u308C\u305F\u30D1\u30EC\u30C3\u30C8\u3067\u3059\u3002\u30AB\u30FC\u30C9\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u5373\u5EA7\u306B\u9069\u7528\u3059\u308B\u304B\u3001{{communityLinkStart}}\u81EA\u5206\u306E\u3082\u306E\u3092\u5171\u6709{{communityLinkEnd}}\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
+ loading: "\u30D1\u30EC\u30C3\u30C8\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D\u2026",
+ error: "\u73FE\u5728\u30D1\u30EC\u30C3\u30C8\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3002\u30C7\u30A3\u30B9\u30AB\u30C3\u30B7\u30E7\u30F3\u3092\u958B\u3044\u3066\u624B\u52D5\u3067\u95B2\u89A7\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
+ empty: "\u5171\u6709\u3055\u308C\u305F\u30D1\u30EC\u30C3\u30C8\u306F\u307E\u3060\u3042\u308A\u307E\u305B\u3093\u3002\u6700\u521D\u306B\u6295\u7A3F\u3057\u307E\u3057\u3087\u3046\uFF01",
+ applyHint: "\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u3053\u306E\u30D1\u30EC\u30C3\u30C8\u3092\u9069\u7528"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} \u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3",
+ description: "\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u8868\u793A",
+ highTextContrast: {
+ name: "\u9AD8\u30C6\u30AD\u30B9\u30C8\u30B3\u30F3\u30C8\u30E9\u30B9\u30C8",
+ description: "\u30C6\u30AD\u30B9\u30C8\u306B\u767D\u3068\u9ED2\u306E\u8272\u306E\u307F\u3092\u4F7F\u7528"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} \u5B9F\u9A13\u7684",
+ description: "\u3044\u3064\u3067\u3082\u5909\u66F4\u307E\u305F\u306F\u524A\u9664\u3055\u308C\u308B\u53EF\u80FD\u6027\u306E\u3042\u308B\u5371\u967A\u306A\u30A2\u30AF\u30B7\u30E7\u30F3\u307E\u305F\u306F\u4E0D\u5B89\u5B9A\u306A\u30AA\u30D7\u30B7\u30E7\u30F3",
+ mixColors: {
+ name: "\u8272\u3092\u6DF7\u305C\u308B",
+ description: "\u30C6\u30AD\u30B9\u30C8\u3092\u8AAD\u307F\u3084\u3059\u304F\u3059\u308B\u306E\u306B\u5F79\u7ACB\u3061\u307E\u3059"
+ },
+ gradientTransition: {
+ name: "\u30B0\u30E9\u30C7\u30FC\u30B7\u30E7\u30F3\u9077\u79FB"
+ },
+ tagColors: {
+ name: "\u30BF\u30B0\u306E\u5272\u308A\u5F53\u3066",
+ description: "\u73FE\u5728\u306E\u30D1\u30EC\u30C3\u30C8\u304B\u3089\u8272\u3092\u9078\u629E\u3057\u307E\u3059\u3002\u30D1\u30EC\u30C3\u30C8\u3092\u5909\u66F4\u3059\u308B\u3068\u3001\u30BF\u30B0\u306F\u6700\u3082\u8FD1\u3044\u8272\u306B\u9069\u5FDC\u3057\u307E\u3059\u3002",
+ placeholder: "\u30BF\u30B0\u306E\u5165\u529B\u3092\u958B\u59CB",
+ clear: "\u8272\u3092\u30EA\u30BB\u30C3\u30C8",
+ empty: "\u30BF\u30B0\u306E\u30AA\u30FC\u30D0\u30FC\u30E9\u30A4\u30C9\u306F\u307E\u3060\u3042\u308A\u307E\u305B\u3093",
+ applyHint: "\u8272\u3092\u9069\u7528"
+ },
+ reset: {
+ name: "\u8A2D\u5B9A\u3092\u30EA\u30BB\u30C3\u30C8",
+ description: "\u{1F6A8} \u3059\u3079\u3066\u306E\u30BF\u30B0\u306E\u3059\u3079\u3066\u306E\u8272\u304C\u3001\u30D7\u30E9\u30B0\u30A4\u30F3\u306E\u521D\u56DE\u8D77\u52D5\u6642\u306E\u3088\u3046\u306B\u518D\u8A08\u7B97\u3055\u308C\u307E\u3059\u3002Obsidian\u306E\u518D\u8D77\u52D5\u304C\u5FC5\u8981\u3067\u3059\u3002",
+ button: "\u30EA\u30BB\u30C3\u30C8"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: \u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5229\u7528\u53EF\u80FD\u3067\u3059",
+ resetDone: "\u2705 \u30EA\u30BB\u30C3\u30C8\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\nObsidian\u3092\u518D\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044",
+ communityPaletteApplied: "\u{1F3A8} {{author}} \u306B\u3088\u308B\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u30D1\u30EC\u30C3\u30C8\u304C\u9069\u7528\u3055\u308C\u307E\u3057\u305F"
+ }
+};
+
+// src/i18n/pt.json
+var pt_default = {
+ settings: {
+ palette: {
+ heading: "Paleta",
+ description: "Selecionar paleta",
+ options: {
+ adaptiveSoft: "\u{1F338} Suave adapt\xE1vel",
+ adaptiveBright: "\u{1F33A} Brilhante adapt\xE1vel",
+ custom: "Personalizada"
+ },
+ shift: {
+ name: "Deslocamento de paleta",
+ description: "Se as cores de algumas etiquetas n\xE3o se ajustarem, voc\xEA pode deslocar a paleta."
+ },
+ custom: {
+ name: "Paleta personalizada",
+ placeholder: "Colar paleta",
+ description: "O formato \xE9 XXXXXX-XXXXXX-XXXXXX para cada cor RGB.",
+ community: {
+ heading: "Paletas da comunidade",
+ description: "Abaixo est\xE3o paletas coletadas da discuss\xE3o no GitHub. Clique em qualquer cart\xE3o para aplic\xE1-la instantaneamente ou {{communityLinkStart}}compartilhe a sua{{communityLinkEnd}}.",
+ loading: "Carregando paletas\u2026",
+ error: "N\xE3o foi poss\xEDvel carregar as paletas agora. Sinta-se \xE0 vontade para abrir a discuss\xE3o para naveg\xE1-las manualmente.",
+ empty: "Nenhuma paleta compartilhada ainda. Seja o primeiro a postar uma!",
+ applyHint: "Clique para aplicar esta paleta"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} Acessibilidade",
+ description: "Mostrar op\xE7\xF5es de acessibilidade",
+ highTextContrast: {
+ name: "Alto contraste de texto",
+ description: "Usar apenas cores branca e preta para textos"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} Experimental",
+ description: "A\xE7\xF5es perigosas ou op\xE7\xF5es inst\xE1veis que podem ser alteradas ou removidas a qualquer momento",
+ mixColors: {
+ name: "Misturar cores",
+ description: "Ajuda a tornar o texto leg\xEDvel"
+ },
+ gradientTransition: {
+ name: "Transi\xE7\xE3o de gradiente"
+ },
+ tagColors: {
+ name: "Atribui\xE7\xF5es de etiquetas",
+ description: "Escolha uma cor da paleta atual. Se voc\xEA mudar a paleta, a etiqueta se adapta \xE0 cor mais pr\xF3xima.",
+ placeholder: "Comece a digitar uma etiqueta",
+ clear: "Redefinir cor",
+ empty: "Nenhuma substitui\xE7\xE3o de etiqueta ainda",
+ applyHint: "Aplicar cor"
+ },
+ reset: {
+ name: "Redefinir configura\xE7\xE3o",
+ description: "\u{1F6A8} Todas as cores de todas as etiquetas ser\xE3o recalculadas como se fosse o primeiro lan\xE7amento do plugin. Requer reinicializa\xE7\xE3o do Obsidian.",
+ button: "Redefinir"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: uma nova vers\xE3o est\xE1 dispon\xEDvel",
+ resetDone: "\u2705 Redefini\xE7\xE3o conclu\xEDda\nPor favor, reinicie o Obsidian",
+ communityPaletteApplied: "\u{1F3A8} Paleta da comunidade aplicada por {{author}}"
+ }
+};
+
+// src/i18n/ru.json
+var ru_default = {
+ settings: {
+ palette: {
+ heading: "\u041F\u0430\u043B\u0438\u0442\u0440\u0430",
+ description: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043F\u0430\u043B\u0438\u0442\u0440\u0443",
+ options: {
+ adaptiveSoft: "\u{1F338} \u0410\u0434\u0430\u043F\u0442\u0438\u0432\u043D\u0430\u044F \u043C\u044F\u0433\u043A\u0430\u044F",
+ adaptiveBright: "\u{1F33A} \u0410\u0434\u0430\u043F\u0442\u0438\u0432\u043D\u0430\u044F \u044F\u0440\u043A\u0430\u044F",
+ custom: "\u0421\u0432\u043E\u044F"
+ },
+ shift: {
+ name: "\u0421\u0434\u0432\u0438\u0433 \u043F\u0430\u043B\u0438\u0442\u0440\u044B",
+ description: "\u0415\u0441\u043B\u0438 \u0446\u0432\u0435\u0442\u0430 \u043A\u0430\u043A\u0438\u0445-\u0442\u043E \u0442\u0435\u0433\u043E\u0432 \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u044F\u0442, \u043C\u043E\u0436\u043D\u043E \u0441\u0434\u0432\u0438\u043D\u0443\u0442\u044C \u043F\u0430\u043B\u0438\u0442\u0440\u0443."
+ },
+ custom: {
+ name: "\u0421\u0432\u043E\u044F \u043F\u0430\u043B\u0438\u0442\u0440\u0430",
+ placeholder: "\u0412\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u043F\u0430\u043B\u0438\u0442\u0440\u0443",
+ description: "\u0424\u043E\u0440\u043C\u0430\u0442 XXXXXX-XXXXXX-XXXXXX \u0434\u043B\u044F \u043A\u0430\u0436\u0434\u043E\u0433\u043E RGB-\u0446\u0432\u0435\u0442\u0430.",
+ community: {
+ heading: "\u041F\u0430\u043B\u0438\u0442\u0440\u044B \u0441\u043E\u043E\u0431\u0449\u0435\u0441\u0442\u0432\u0430",
+ description: "\u041D\u0438\u0436\u0435 \u0441\u043E\u0431\u0440\u0430\u043D\u044B \u043F\u0430\u043B\u0438\u0442\u0440\u044B \u0438\u0437 \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u044F \u043D\u0430 GitHub. \u041A\u043B\u0438\u043A\u043D\u0438\u0442\u0435 \u043F\u043E \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u0435\u0451 \u0438\u043B\u0438 {{communityLinkStart}}\u043F\u043E\u0434\u0435\u043B\u0438\u0442\u0435\u0441\u044C \u0441\u0432\u043E\u0435\u0439{{communityLinkEnd}}.",
+ loading: "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043C \u043F\u0430\u043B\u0438\u0442\u0440\u044B\u2026",
+ error: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0430\u043B\u0438\u0442\u0440\u044B. \u041E\u0442\u043A\u0440\u043E\u0439\u0442\u0435 \u043E\u0431\u0441\u0443\u0436\u0434\u0435\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C \u0438\u0445 \u0432\u0440\u0443\u0447\u043D\u0443\u044E.",
+ empty: "\u041F\u043E\u043A\u0430 \u043D\u0438\u043A\u0442\u043E \u043D\u0435 \u043F\u043E\u0434\u0435\u043B\u0438\u043B\u0441\u044F \u043F\u0430\u043B\u0438\u0442\u0440\u0430\u043C\u0438. \u0411\u0443\u0434\u044C\u0442\u0435 \u043F\u0435\u0440\u0432\u044B\u043C\u0438!",
+ applyHint: "\u041D\u0430\u0436\u043C\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u044D\u0442\u0443 \u043F\u0430\u043B\u0438\u0442\u0440\u0443"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} \u0414\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C",
+ description: "\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u0438",
+ highTextContrast: {
+ name: "\u0412\u044B\u0441\u043E\u043A\u0438\u0439 \u043A\u043E\u043D\u0442\u0440\u0430\u0441\u0442 \u0442\u0435\u043A\u0441\u0442\u0430",
+ description: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0431\u0435\u043B\u044B\u0439 \u0438 \u0447\u0435\u0440\u043D\u044B\u0439 \u0446\u0432\u0435\u0442\u0430 \u0442\u0435\u043A\u0441\u0442\u0430"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} \u042D\u043A\u0441\u043F\u0435\u0440\u0438\u043C\u0435\u043D\u0442\u0430\u043B\u044C\u043D\u044B\u0435",
+ description: "\u041E\u043F\u0430\u0441\u043D\u044B\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0438\u043B\u0438 \u043D\u0435\u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u044B\u0435 \u043E\u043F\u0446\u0438\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043C\u043E\u0433\u0443\u0442 \u0431\u044B\u0442\u044C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u044B \u0438\u043B\u0438 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u0432 \u043B\u044E\u0431\u043E\u0439 \u043C\u043E\u043C\u0435\u043D\u0442",
+ mixColors: {
+ name: "\u0421\u043C\u0435\u0448\u0438\u0432\u0430\u0442\u044C \u0446\u0432\u0435\u0442\u0430",
+ description: "\u041F\u043E\u043C\u043E\u0433\u0430\u0435\u0442 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0442\u0435\u043A\u0441\u0442 \u0447\u0438\u0442\u0430\u0435\u043C\u044B\u043C"
+ },
+ gradientTransition: {
+ name: "\u041F\u043B\u0430\u0432\u043D\u044B\u0439 \u0433\u0440\u0430\u0434\u0438\u0435\u043D\u0442"
+ },
+ tagColors: {
+ name: "\u041D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0446\u0432\u0435\u0442\u043E\u0432",
+ description: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0446\u0432\u0435\u0442 \u0438\u0437 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u043F\u0430\u043B\u0438\u0442\u0440\u044B. \u041F\u0440\u0438 \u0441\u043C\u0435\u043D\u0435 \u043F\u0430\u043B\u0438\u0442\u0440\u044B \u0442\u0435\u0433 \u0430\u0434\u0430\u043F\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044F \u043A \u0431\u043B\u0438\u0436\u0430\u0439\u0448\u0435\u043C\u0443 \u0446\u0432\u0435\u0442\u0443.",
+ placeholder: "\u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0432\u0432\u043E\u0434\u0438\u0442\u044C \u0442\u0435\u0433",
+ clear: "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0446\u0432\u0435\u0442",
+ empty: "\u041F\u043E\u043A\u0430 \u043D\u0435\u0442 \u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u043D\u044B\u0445 \u0446\u0432\u0435\u0442\u043E\u0432",
+ applyHint: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0446\u0432\u0435\u0442"
+ },
+ reset: {
+ name: "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u043A\u043E\u043D\u0444\u0438\u0433",
+ description: "\u{1F6A8} \u0412\u0441\u0435 \u0446\u0432\u0435\u0442\u0430 \u0432\u0441\u0435\u0445 \u0442\u0435\u0433\u043E\u0432 \u0431\u0443\u0434\u0443\u0442 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u043D\u044B \u0442\u0430\u043A, \u0431\u0443\u0434\u0442\u043E \u043F\u043B\u0430\u0433\u0438\u043D \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u0432\u043F\u0435\u0440\u0432\u044B\u0435. \u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A Obsidian.",
+ button: "\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}: \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F",
+ resetDone: "\u2705 \u0421\u0431\u0440\u043E\u0441 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\n\u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 Obsidian",
+ communityPaletteApplied: "\u{1F3A8} \u041F\u0430\u043B\u0438\u0442\u0440\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u0441\u0442\u0432\u0430 \u043E\u0442 {{author}} \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u0440\u0438\u043C\u0435\u043D\u0435\u043D\u0430"
+ }
+};
+
+// src/i18n/zh.json
+var zh_default = {
+ settings: {
+ palette: {
+ heading: "\u8C03\u8272\u677F",
+ description: "\u9009\u62E9\u8C03\u8272\u677F",
+ options: {
+ adaptiveSoft: "\u{1F338} \u81EA\u9002\u5E94\u67D4\u548C",
+ adaptiveBright: "\u{1F33A} \u81EA\u9002\u5E94\u9C9C\u8273",
+ custom: "\u81EA\u5B9A\u4E49"
+ },
+ shift: {
+ name: "\u8C03\u8272\u677F\u504F\u79FB",
+ description: "\u5982\u679C\u67D0\u4E9B\u6807\u7B7E\u7684\u989C\u8272\u4E0D\u5408\u9002\uFF0C\u53EF\u4EE5\u504F\u79FB\u8C03\u8272\u677F\u3002"
+ },
+ custom: {
+ name: "\u81EA\u5B9A\u4E49\u8C03\u8272\u677F",
+ placeholder: "\u7C98\u8D34\u8C03\u8272\u677F",
+ description: "\u6BCF\u79CD RGB \u989C\u8272\u7684\u683C\u5F0F\u4E3A XXXXXX-XXXXXX-XXXXXX\u3002",
+ community: {
+ heading: "\u793E\u533A\u8C03\u8272\u677F",
+ description: "\u4E0B\u65B9\u5C55\u793A\u6765\u81EA GitHub \u8BA8\u8BBA\u533A\u7684\u8C03\u8272\u677F\u3002\u70B9\u51FB\u4EFB\u610F\u5361\u7247\u5373\u53EF\u7ACB\u5373\u5E94\u7528\uFF0C\u6216{{communityLinkStart}}\u5206\u4EAB\u4F60\u7684\u914D\u8272{{communityLinkEnd}}\u3002",
+ loading: "\u6B63\u5728\u52A0\u8F7D\u8C03\u8272\u677F\u2026",
+ error: "\u6682\u65F6\u65E0\u6CD5\u52A0\u8F7D\u8C03\u8272\u677F\uFF0C\u8BF7\u6253\u5F00\u8BA8\u8BBA\u9875\u9762\u624B\u52A8\u67E5\u770B\u3002",
+ empty: "\u8FD8\u6CA1\u6709\u4EBA\u5206\u4EAB\u8C03\u8272\u677F\uFF0C\u6B22\u8FCE\u7387\u5148\u8D21\u732E\uFF01",
+ applyHint: "\u70B9\u51FB\u5373\u53EF\u5E94\u7528\u6B64\u8C03\u8272\u677F"
+ }
+ }
+ },
+ accessibility: {
+ heading: "\u{1F9BE} \u65E0\u969C\u788D",
+ description: "\u663E\u793A\u65E0\u969C\u788D\u9009\u9879",
+ highTextContrast: {
+ name: "\u9AD8\u5BF9\u6BD4\u6587\u672C",
+ description: "\u4EC5\u5BF9\u6587\u672C\u4F7F\u7528\u9ED1\u8272\u548C\u767D\u8272"
+ }
+ },
+ experimental: {
+ heading: "\u{1F9EA} \u5B9E\u9A8C\u529F\u80FD",
+ description: "\u5371\u9669\u64CD\u4F5C\u6216\u6781\u5176\u4E0D\u7A33\u5B9A\u7684\u9009\u9879\uFF0C\u968F\u65F6\u53EF\u80FD\u4FEE\u6539\u6216\u79FB\u9664",
+ mixColors: {
+ name: "\u6DF7\u5408\u989C\u8272",
+ description: "\u6709\u52A9\u4E8E\u4FDD\u6301\u6587\u672C\u53EF\u8BFB"
+ },
+ gradientTransition: {
+ name: "\u6E10\u53D8\u8FC7\u6E21"
+ },
+ tagColors: {
+ name: "\u6807\u7B7E\u989C\u8272\u5206\u914D",
+ description: "\u8BF7\u4ECE\u5F53\u524D\u8C03\u8272\u677F\u4E2D\u9009\u62E9\u989C\u8272\u3002\u66F4\u6362\u8C03\u8272\u677F\u65F6\uFF0C\u6807\u7B7E\u4F1A\u81EA\u52A8\u9002\u914D\u6700\u63A5\u8FD1\u7684\u989C\u8272\u3002",
+ placeholder: "\u5F00\u59CB\u8F93\u5165\u6807\u7B7E",
+ clear: "\u91CD\u7F6E\u989C\u8272",
+ empty: "\u8FD8\u6CA1\u6709\u5206\u914D\u6807\u7B7E\u989C\u8272",
+ applyHint: "\u5E94\u7528\u989C\u8272"
+ },
+ reset: {
+ name: "\u91CD\u7F6E\u914D\u7F6E",
+ description: "\u{1F6A8} \u6240\u6709\u6807\u7B7E\u7684\u989C\u8272\u90FD\u4F1A\u91CD\u65B0\u8BA1\u7B97\uFF0C\u5C31\u50CF\u7B2C\u4E00\u6B21\u542F\u52A8\u63D2\u4EF6\u4E00\u6837\u3002\u9700\u8981\u91CD\u65B0\u542F\u52A8 Obsidian\u3002",
+ button: "\u91CD\u7F6E"
+ }
+ }
+ },
+ notices: {
+ updateAvailable: "\u2B06\uFE0F {{pluginName}}\uFF1A\u6709\u53EF\u7528\u7684\u65B0\u7248\u672C",
+ resetDone: "\u2705 \u91CD\u7F6E\u5B8C\u6210\n\u8BF7\u91CD\u65B0\u542F\u52A8 Obsidian",
+ communityPaletteApplied: "\u{1F3A8} \u5DF2\u5E94\u7528\u6765\u81EA {{author}} \u7684\u793E\u533A\u8C03\u8272\u677F"
+ }
+};
+
+// src/logger.ts
+var logger = {
+ warn: (message) => {
+ console.warn(message);
+ },
+ error: (message) => {
+ console.error(message);
+ },
+ log: (message) => {
+ console.log(message);
+ }
+};
+
+// src/i18n/index.ts
+var locales = {
+ en: en_default,
+ ru: ru_default,
+ de: de_default,
+ zh: zh_default,
+ es: es_default,
+ fr: fr_default,
+ it: it_default,
+ pt: pt_default,
+ ja: ja_default
+};
+var I18n = class {
+ static t(key, params) {
+ const locale = window.localStorage.getItem("language") || "en";
+ const keys = key.split(".");
+ let translations = locales[locale] || locales["en"];
+ for (const k of keys) {
+ if (typeof translations !== "object" || translations === null) {
+ logger.warn(`Translation missing: ${key}`);
+ translations = locales["en"];
+ return this.extractEnglishValue(keys, key);
+ }
+ if (translations[k] === void 0) {
+ logger.warn(`Translation missing: ${key}`);
+ translations = locales["en"];
+ return this.extractEnglishValue(keys, key);
+ }
+ translations = translations[k];
+ }
+ if (typeof translations !== "string") {
+ return key;
+ }
+ let result = translations;
+ if (params) {
+ Object.entries(params).forEach(([key2, value]) => {
+ const placeholder = new RegExp(`{{${key2}}}`, "g");
+ result = result.replace(placeholder, value);
+ });
+ }
+ return result;
+ }
+ static extractEnglishValue(keys, originalKey) {
+ let engValue = locales["en"];
+ for (const ek of keys) {
+ if (typeof engValue !== "object" || engValue === null) {
+ engValue = void 0;
+ break;
+ }
+ if (engValue[ek] === void 0) {
+ engValue = void 0;
+ break;
+ }
+ engValue = engValue[ek];
+ }
+ if (typeof engValue === "string") {
+ return engValue;
+ }
+ return originalKey;
+ }
+};
+
+// src/tagUtils.ts
+function normalizeTagName(tagName) {
+ return tagName.replace(/#/g, "").trim().replace(/\s+/g, "").replace(/\/+$/, "").toLowerCase();
+}
+function normalizePaletteIndex(index, length) {
+ if (length <= 0) {
+ return 0;
+ }
+ const normalized = index % length;
+ return normalized < 0 ? normalized + length : normalized;
+}
+
+// src/CommunityPalettesService.ts
+var import_obsidian = require("obsidian");
+var DISCUSSION_COMMENTS_URL = "https://api.github.com/repos/pfrankov/obsidian-colored-tags/discussions/18/comments";
+var PER_PAGE = 100;
+var POSITIVE_REACTIONS = [
+ "+1",
+ "heart",
+ "hooray",
+ "rocket",
+ "eyes",
+ "laugh"
+];
+var NEGATIVE_REACTIONS = ["-1", "confused"];
+var CommunityPalettesService = class {
+ static async getCommunityPalettes() {
+ if (this.cache) {
+ return this.cache;
+ }
+ if (this.pendingRequest) {
+ return this.pendingRequest;
+ }
+ this.pendingRequest = this.fetchCommunityPalettes().then((palettes) => {
+ this.cache = palettes;
+ return palettes;
+ }).finally(() => {
+ this.pendingRequest = void 0;
+ });
+ return this.pendingRequest;
+ }
+ static async fetchCommunityPalettes() {
+ const palettes = await this.collectPalettes(
+ 1,
+ [],
+ /* @__PURE__ */ new Set(),
+ /* @__PURE__ */ new Set()
+ );
+ return palettes.sort((a2, b2) => {
+ if (a2.score === b2.score) {
+ return a2.order - b2.order;
+ }
+ return b2.score - a2.score;
+ }).map(({ order: _order, ...palette }) => palette);
+ }
+ static async collectPalettes(page, palettes, uniqueValues, authorsWithPalette) {
+ var _a;
+ const response = await this.requestPage(page);
+ if (!response) {
+ return palettes;
+ }
+ const comments = (_a = response.json) != null ? _a : [];
+ if (!comments.length) {
+ return palettes;
+ }
+ this.appendPalettesFromComments(
+ comments,
+ palettes,
+ uniqueValues,
+ authorsWithPalette
+ );
+ if (comments.length < PER_PAGE) {
+ return palettes;
+ }
+ return this.collectPalettes(
+ page + 1,
+ palettes,
+ uniqueValues,
+ authorsWithPalette
+ );
+ }
+ static async requestPage(page) {
+ try {
+ const response = await (0, import_obsidian.requestUrl)({
+ url: `${DISCUSSION_COMMENTS_URL}?per_page=${PER_PAGE}&page=${page}`,
+ headers: {
+ Accept: "application/vnd.github+json"
+ },
+ throw: false
+ });
+ if (response.status !== 200) {
+ console.error(
+ `GitHub API responded with ${response.status} on page ${page}`
+ );
+ return null;
+ }
+ return response;
+ } catch (error) {
+ console.error("Failed to fetch community palettes", error);
+ return null;
+ }
+ }
+ static appendPalettesFromComments(comments, palettes, uniqueValues, authorsWithPalette) {
+ var _a, _b;
+ const scoredComments = comments.map((comment) => ({
+ comment,
+ score: this.getReactionScore(comment.reactions)
+ })).filter(({ score }) => score >= 0);
+ for (const { comment, score } of scoredComments) {
+ const author = ((_a = comment.user) == null ? void 0 : _a.login) || "unknown";
+ if (authorsWithPalette.has(author)) {
+ continue;
+ }
+ const palettesFromComment = this.extractPalettesFromBody(
+ comment.body || ""
+ );
+ const [value] = palettesFromComment;
+ if (!value || uniqueValues.has(value)) {
+ continue;
+ }
+ uniqueValues.add(value);
+ authorsWithPalette.add(author);
+ palettes.push({
+ id: `${comment.id}-0`,
+ value,
+ colors: value.split("-").filter(Boolean).map((hex) => `#${hex}`),
+ author,
+ authorUrl: (_b = comment.user) == null ? void 0 : _b.html_url,
+ commentUrl: comment.html_url,
+ score,
+ order: palettes.length
+ });
+ }
+ }
+ static extractPalettesFromBody(body) {
+ const regex = /\b([0-9a-fA-F]{6}(?:-[0-9a-fA-F]{6})+)\b/g;
+ const results = /* @__PURE__ */ new Set();
+ let match;
+ while ((match = regex.exec(body)) !== null) {
+ results.add(match[1].toLowerCase());
+ }
+ return Array.from(results);
+ }
+ static getReactionScore(reactions) {
+ if (!reactions) {
+ return 0;
+ }
+ const positive = POSITIVE_REACTIONS.reduce((total, key) => {
+ var _a;
+ return total + ((_a = reactions[key]) != null ? _a : 0);
+ }, 0);
+ const negative = NEGATIVE_REACTIONS.reduce((total, key) => {
+ var _a;
+ return total + ((_a = reactions[key]) != null ? _a : 0);
+ }, 0);
+ return positive - negative;
+ }
+};
+CommunityPalettesService.cache = null;
+
+// src/ColoredTagsPluginSettingTab.ts
+var SELECTED_CLASS = "is-selected";
+var ColoredTagsPluginSettingTab = class extends import_obsidian2.PluginSettingTab {
+ constructor(app, plugin) {
+ super(app, plugin);
+ this.showExperimental = false;
+ this.showAccessibility = false;
+ this.communityPaletteDescriptionCounter = 0;
+ this.communityPaletteCards = /* @__PURE__ */ new Map();
+ this.paletteChangeSubscribers = [];
+ this.plugin = plugin;
+ }
+ renderPalette(paletteEl, animate = false) {
+ paletteEl.empty();
+ const palette = this.getActivePalette();
+ palette.forEach((paletteColor, index) => {
+ const firstElementStyles = "border-radius: var(--radius-m) 0 0 var(--radius-m)";
+ const lastElementStyles = "border-radius: 0 var(--radius-m) var(--radius-m) 0";
+ const styles = [
+ index === 0 && firstElementStyles,
+ "flex: 1",
+ "height: 2em",
+ `background-color: ${paletteColor}`,
+ index === palette.length - 1 && lastElementStyles
+ ];
+ paletteEl.createEl("div", {
+ attr: { style: styles.filter(Boolean).join(";") }
+ });
+ });
+ if (animate && paletteEl.animate) {
+ paletteEl.animate([{ opacity: 0.2 }, { opacity: 1 }], {
+ duration: 220,
+ easing: "ease-out"
+ });
+ }
+ }
+ renderTags(containerEl) {
+ const tagsObject = this.app.metadataCache.getTags();
+ const tagsArray = Object.keys(tagsObject);
+ if (!tagsArray.length) {
+ return;
+ }
+ const tagEl = containerEl.createEl("div", {
+ cls: "tagsExample"
+ });
+ tagsArray.sort((a2, b2) => {
+ return tagsObject[b2] - tagsObject[a2];
+ });
+ const mostPopularTags = tagsArray.splice(0, 2);
+ const mostPopularNestedTags = Object.values(
+ tagsArray.reduce((acc, tag) => {
+ const nestingLevel = tag.split("/").length;
+ if (!acc[nestingLevel]) {
+ acc[nestingLevel] = tag;
+ }
+ return acc;
+ }, {})
+ );
+ [...mostPopularTags, ...mostPopularNestedTags].forEach(
+ (tag) => {
+ const link = tagEl.createEl("a", { attr: { href: tag } });
+ link.classList.add("tag");
+ link.innerText = tag;
+ }
+ );
+ }
+ display() {
+ const { containerEl } = this;
+ containerEl.empty();
+ containerEl.classList.add("colored-tags-settings");
+ this.paletteChangeSubscribers = [];
+ this.renderTags(containerEl);
+ const paletteEl = containerEl.createEl("div", {
+ cls: "palette"
+ });
+ this.renderPalette(paletteEl);
+ this.renderPaletteSettings(containerEl, paletteEl);
+ this.renderAccessibilitySettings(containerEl);
+ this.renderExperimentalSettings(containerEl);
+ }
+ renderPaletteSettings(containerEl, paletteEl) {
+ new import_obsidian2.Setting(containerEl).setHeading().setName(I18n.t("settings.palette.heading")).setDesc(I18n.t("settings.palette.description")).addDropdown(
+ (dropdown) => dropdown.addOptions({
+ ["adaptive-soft" /* ADAPTIVE_SOFT */]: I18n.t(
+ "settings.palette.options.adaptiveSoft"
+ ),
+ ["adaptive-bright" /* ADAPTIVE_BRIGHT */]: I18n.t(
+ "settings.palette.options.adaptiveBright"
+ ),
+ ["custom" /* CUSTOM */]: I18n.t(
+ "settings.palette.options.custom"
+ )
+ }).setValue(String(this.plugin.settings.palette.selected)).onChange(async (value) => {
+ this.plugin.settings.palette.selected = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ if (this.plugin.settings.palette.selected === "custom") {
+ this.renderCustomPaletteField(containerEl, paletteEl);
+ }
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.palette.shift.name")).setDesc(I18n.t("settings.palette.shift.description")).addSlider(
+ (slider) => slider.setLimits(0, this.plugin.palettes.light.length - 1, 1).setValue(this.plugin.settings.palette.seed).onChange(async (value) => {
+ slider.showTooltip();
+ this.plugin.settings.palette.seed = value;
+ await this.plugin.saveSettings();
+ this.renderPalette(paletteEl);
+ this.notifyPaletteChange();
+ })
+ );
+ }
+ renderCustomPaletteField(containerEl, paletteEl) {
+ let customPaletteInput = null;
+ const customPaletteField = new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.palette.custom.name")).setDesc("").addText((text) => {
+ customPaletteInput = text;
+ text.inputEl.style.minWidth = "100%";
+ text.setValue(
+ this.plugin.settings.palette.custom
+ ).setPlaceholder(I18n.t("settings.palette.custom.placeholder"));
+ text.onChange(async (value) => {
+ if (/^([A-Fa-f0-9]{6}(-|$))+$/i.test(value)) {
+ this.plugin.settings.palette.custom = value;
+ await this.plugin.saveSettings();
+ this.renderPalette(paletteEl);
+ this.notifyPaletteChange();
+ this.updateCommunityPaletteSelection();
+ }
+ });
+ });
+ customPaletteField.descEl.innerHTML = I18n.t(
+ "settings.palette.custom.description"
+ );
+ if (customPaletteInput) {
+ this.renderCommunityPalettesSection(
+ containerEl,
+ paletteEl,
+ customPaletteInput
+ );
+ }
+ }
+ renderCommunityPalettesSection(containerEl, paletteEl, inputComponent) {
+ this.communityPaletteDescriptionCounter = 0;
+ this.communityPaletteCards = /* @__PURE__ */ new Map();
+ const sectionSetting = new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.palette.custom.community.heading")).setDesc("");
+ sectionSetting.settingEl.classList.add("community-palettes");
+ const descEl = sectionSetting.descEl;
+ descEl.empty();
+ const communityLinkStart = '';
+ const communityLinkEnd = "";
+ const descriptionEl = descEl.createDiv({
+ cls: "community-palettes__description"
+ });
+ descriptionEl.innerHTML = I18n.t(
+ "settings.palette.custom.community.description",
+ { communityLinkStart, communityLinkEnd }
+ );
+ const scrollContainer = descEl.createDiv({
+ cls: "community-palettes__scroll"
+ });
+ const gridEl = scrollContainer.createDiv({
+ cls: "community-palettes__grid"
+ });
+ const statusEl = scrollContainer.createDiv({
+ cls: "community-palettes__status",
+ text: I18n.t("settings.palette.custom.community.loading")
+ });
+ CommunityPalettesService.getCommunityPalettes().then((palettes) => {
+ if (!palettes.length) {
+ statusEl.textContent = I18n.t(
+ "settings.palette.custom.community.empty"
+ );
+ return;
+ }
+ statusEl.remove();
+ palettes.forEach((palette) => {
+ const card = this.renderCommunityPaletteCard(
+ gridEl,
+ palette,
+ paletteEl,
+ inputComponent
+ );
+ this.communityPaletteCards.set(
+ this.normalizePaletteValue(palette.value),
+ card
+ );
+ });
+ this.updateCommunityPaletteSelection();
+ }).catch((error) => {
+ console.error(error);
+ statusEl.textContent = I18n.t(
+ "settings.palette.custom.community.error"
+ );
+ });
+ }
+ renderCommunityPaletteCard(gridEl, palette, paletteEl, inputComponent) {
+ const descriptionId = `community-palette-desc-${this.communityPaletteDescriptionCounter++}`;
+ const card = gridEl.createDiv({
+ cls: "community-palette-card",
+ attr: {
+ role: "button",
+ tabindex: "0",
+ "aria-describedby": descriptionId,
+ "aria-pressed": "false"
+ }
+ });
+ card.createSpan({
+ cls: "visually-hidden",
+ attr: { id: descriptionId },
+ text: I18n.t("settings.palette.custom.community.applyHint")
+ });
+ const paletteRow = card.createDiv({
+ cls: "community-palette-card__preview"
+ });
+ palette.colors.forEach((color) => {
+ paletteRow.createDiv({
+ attr: {
+ style: `flex: 1; background-color: ${color};`
+ }
+ });
+ });
+ const metaEl = card.createDiv({
+ cls: "community-palette-card__meta"
+ });
+ metaEl.createSpan({
+ cls: "community-palette-card__author",
+ text: palette.author
+ });
+ metaEl.createSpan({
+ cls: "community-palette-card__upvotes",
+ text: `\u2764\uFE0F ${palette.score}`
+ });
+ const applyPalette = () => {
+ void this.applyCommunityPalette(palette, inputComponent, paletteEl);
+ };
+ card.addEventListener("click", applyPalette);
+ card.addEventListener("keydown", (event) => {
+ if (event.key === "Enter" || event.key === " ") {
+ event.preventDefault();
+ applyPalette();
+ }
+ });
+ return card;
+ }
+ async applyCommunityPalette(palette, inputComponent, paletteEl) {
+ this.plugin.settings.palette.custom = palette.value;
+ inputComponent.setValue(palette.value);
+ await this.plugin.saveSettings();
+ this.renderPalette(paletteEl, true);
+ this.notifyPaletteChange();
+ this.updateCommunityPaletteSelection();
+ new import_obsidian2.Notice(
+ I18n.t("notices.communityPaletteApplied", {
+ author: palette.author
+ }),
+ 4e3
+ );
+ }
+ normalizePaletteValue(value) {
+ return (value || "").replace(/#/g, "").replace(/\s+/g, "").trim().toLowerCase();
+ }
+ updateCommunityPaletteSelection() {
+ if (!this.communityPaletteCards.size) {
+ return;
+ }
+ const isCustomSelected = this.plugin.settings.palette.selected === "custom" /* CUSTOM */;
+ const normalizedCustom = isCustomSelected ? this.normalizePaletteValue(this.plugin.settings.palette.custom) : "";
+ const hasMatch = Boolean(normalizedCustom);
+ this.communityPaletteCards.forEach((card, value) => {
+ const isSelected = hasMatch && value === normalizedCustom;
+ if (isSelected) {
+ card.classList.add(SELECTED_CLASS);
+ } else {
+ card.classList.remove(SELECTED_CLASS);
+ }
+ card.setAttribute("aria-pressed", isSelected ? "true" : "false");
+ });
+ }
+ renderAccessibilitySettings(containerEl) {
+ new import_obsidian2.Setting(containerEl).setHeading().setName(I18n.t("settings.accessibility.heading")).setDesc(I18n.t("settings.accessibility.description")).addToggle(
+ (toggle) => toggle.setValue(this.showAccessibility).onChange(async (value) => {
+ this.showAccessibility = value;
+ this.display();
+ })
+ );
+ if (this.showAccessibility) {
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.accessibility.highTextContrast.name")).setDesc(
+ I18n.t(
+ "settings.accessibility.highTextContrast.description"
+ )
+ ).addToggle(
+ (toggle) => toggle.setValue(
+ this.plugin.settings.accessibility.highTextContrast
+ ).onChange(async (value) => {
+ this.plugin.settings.accessibility.highTextContrast = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ }
+ }
+ renderExperimentalSettings(containerEl) {
+ new import_obsidian2.Setting(containerEl).setHeading().setName(I18n.t("settings.experimental.heading")).setDesc(I18n.t("settings.experimental.description")).addToggle(
+ (toggle) => toggle.setValue(this.showExperimental).onChange(async (value) => {
+ this.showExperimental = value;
+ this.display();
+ })
+ );
+ if (!this.showExperimental) {
+ return;
+ }
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.mixColors.name")).setDesc(I18n.t("settings.experimental.mixColors.description")).addToggle(
+ (toggle) => toggle.setValue(this.plugin.settings.mixColors).onChange(async (value) => {
+ this.plugin.settings.mixColors = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.gradientTransition.name")).addToggle(
+ (toggle) => toggle.setValue(this.plugin.settings.transition).onChange(async (value) => {
+ this.plugin.settings.transition = value;
+ await this.plugin.saveSettings();
+ this.display();
+ })
+ );
+ this.renderTagPaletteOverrides(containerEl);
+ new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.reset.name")).setDesc(I18n.t("settings.experimental.reset.description")).addButton(
+ (button) => button.setButtonText(I18n.t("settings.experimental.reset.button")).setClass("mod-warning").onClick(async () => {
+ new import_obsidian2.Notice(I18n.t("notices.resetDone"), 1e4);
+ button.setDisabled(true);
+ button.buttonEl.setAttribute("disabled", "true");
+ button.buttonEl.classList.remove("mod-warning");
+ this.plugin.settings = Object.assign(
+ {},
+ DEFAULT_SETTINGS
+ );
+ await this.plugin.saveSettings();
+ this.updateCommunityPaletteSelection();
+ })
+ );
+ }
+ renderTagPaletteOverrides(containerEl) {
+ const tagPaletteSetting = new import_obsidian2.Setting(containerEl).setName(I18n.t("settings.experimental.tagColors.name")).setDesc(I18n.t("settings.experimental.tagColors.description"));
+ tagPaletteSetting.settingEl.classList.add("tag-color-setting-item");
+ tagPaletteSetting.controlEl.empty();
+ const wrapper = tagPaletteSetting.controlEl.createDiv({
+ cls: "tag-color-setting"
+ });
+ const controlsRow = wrapper.createDiv({
+ cls: "tag-color-setting__row"
+ });
+ const inputContainer = controlsRow.createDiv({
+ cls: "tag-color-setting__input"
+ });
+ const datalistId = `tag-color-list-${Date.now()}`;
+ const datalist = inputContainer.createEl("datalist", {
+ attr: { id: datalistId }
+ });
+ const refreshTagOptions = () => this.populateTagOptions(datalist);
+ refreshTagOptions();
+ let currentTag = "";
+ const tagInput = new import_obsidian2.TextComponent(inputContainer);
+ tagInput.setPlaceholder(
+ I18n.t("settings.experimental.tagColors.placeholder")
+ ).setValue("");
+ tagInput.inputEl.setAttr("list", datalistId);
+ tagInput.onChange((value) => {
+ currentTag = normalizeTagName(value);
+ updateSelectedSwatch();
+ });
+ const paletteEl = controlsRow.createDiv({
+ cls: "tag-color-setting__palette"
+ });
+ const listContainer = wrapper.createDiv({
+ cls: "tag-color-setting__chips"
+ });
+ const handleAssignmentsChange = () => {
+ updateSelectionState();
+ refreshTagOptions();
+ };
+ const updateSelectedSwatch = () => {
+ var _a;
+ const palette = this.getActivePalette();
+ const assignedIndex = (_a = this.plugin.settings.tagColors && this.plugin.settings.tagColors[currentTag]) != null ? _a : null;
+ Array.from(paletteEl.children).forEach((child, index) => {
+ child.classList.toggle(
+ SELECTED_CLASS,
+ assignedIndex !== null && assignedIndex !== void 0 && normalizePaletteIndex(assignedIndex, palette.length) === index
+ );
+ child.disabled = !currentTag;
+ });
+ };
+ const updateSelectionState = () => {
+ updateSelectedSwatch();
+ };
+ const applySelection = async (index) => {
+ if (!currentTag) {
+ return;
+ }
+ this.plugin.settings.tagColors[currentTag] = index;
+ await this.plugin.saveSettings();
+ this.plugin.colorizeTag(currentTag);
+ this.renderTagColorAssignments(
+ listContainer,
+ handleAssignmentsChange
+ );
+ };
+ this.renderPaletteSwatches(paletteEl, applySelection);
+ this.renderTagColorAssignments(listContainer, handleAssignmentsChange);
+ this.subscribeToPaletteChange(() => {
+ this.renderPaletteSwatches(paletteEl, applySelection);
+ this.renderTagColorAssignments(
+ listContainer,
+ handleAssignmentsChange
+ );
+ updateSelectionState();
+ });
+ updateSelectionState();
+ }
+ renderPaletteSwatches(paletteEl, onSelect) {
+ paletteEl.empty();
+ const palette = this.getActivePalette();
+ palette.forEach((color, index) => {
+ const swatch = paletteEl.createEl("button", {
+ cls: "tag-color-setting__swatch",
+ attr: {
+ type: "button",
+ style: `background-color: ${color}`,
+ "aria-label": `${I18n.t(
+ "settings.experimental.tagColors.applyHint"
+ )} ${index + 1}`
+ }
+ });
+ swatch.addEventListener("click", () => onSelect(index));
+ });
+ }
+ renderTagColorAssignments(listEl, onChange) {
+ listEl.empty();
+ const entries = Object.entries(this.plugin.settings.tagColors || {});
+ if (!entries.length) {
+ listEl.createDiv({
+ cls: "tag-color-setting__empty",
+ text: I18n.t("settings.experimental.tagColors.empty")
+ });
+ onChange == null ? void 0 : onChange();
+ return;
+ }
+ entries.sort(([tagA], [tagB]) => tagA.localeCompare(tagB)).forEach(([tag]) => {
+ const chipWrapper = listEl.createDiv({
+ cls: "tag-color-setting__chip"
+ });
+ const chip = chipWrapper.createEl("a", {
+ cls: "tag",
+ text: `#${tag}`,
+ attr: {
+ href: `#${tag}`
+ }
+ });
+ chip.addEventListener("click", (event) => {
+ var _a;
+ event.preventDefault();
+ const tagInputEl = (_a = listEl.closest(".tag-color-setting")) == null ? void 0 : _a.querySelector(
+ ".tag-color-setting__input input"
+ );
+ if (tagInputEl) {
+ tagInputEl.value = `#${tag}`;
+ tagInputEl.dispatchEvent(new Event("input"));
+ }
+ });
+ const removeButton = chipWrapper.createEl("button", {
+ cls: "tag-color-setting__chip-remove",
+ attr: {
+ type: "button",
+ "aria-label": I18n.t(
+ "settings.experimental.tagColors.clear"
+ ),
+ title: I18n.t("settings.experimental.tagColors.clear")
+ },
+ text: "\u2715"
+ });
+ removeButton.addEventListener("click", async (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ delete this.plugin.settings.tagColors[tag];
+ await this.plugin.saveSettings();
+ this.renderTagColorAssignments(listEl, onChange);
+ });
+ });
+ onChange == null ? void 0 : onChange();
+ }
+ populateTagOptions(datalist) {
+ var _a, _b;
+ datalist.empty();
+ const knownTags = new Set(
+ Object.keys(this.plugin.settings.knownTags || {})
+ );
+ const assignedTags = new Set(
+ Object.keys(this.plugin.settings.tagColors || {})
+ );
+ const metadataTags = Object.keys(
+ ((_b = (_a = this.app.metadataCache) == null ? void 0 : _a.getTags) == null ? void 0 : _b.call(_a)) || {}
+ ).map((tag) => normalizeTagName(tag)).filter((tag) => tag.length > 0);
+ metadataTags.forEach((tag) => knownTags.add(tag));
+ Array.from(knownTags).filter((tag) => !assignedTags.has(tag)).sort((a2, b2) => a2.localeCompare(b2)).forEach((tag) => {
+ datalist.createEl("option", {
+ attr: { value: `#${tag}` }
+ });
+ });
+ }
+ getActivePalette() {
+ let palette = this.plugin.palettes.light;
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+ palette = this.plugin.palettes.dark;
+ }
+ return palette.length ? palette : this.plugin.palettes.light;
+ }
+ subscribeToPaletteChange(callback) {
+ this.paletteChangeSubscribers.push(callback);
+ }
+ notifyPaletteChange() {
+ this.paletteChangeSubscribers.forEach((cb) => cb());
+ }
+};
+
+// node_modules/colorjs.io/dist/color.js
+function multiplyMatrices(A, B) {
+ let m3 = A.length;
+ if (!Array.isArray(A[0])) {
+ A = [A];
+ }
+ if (!Array.isArray(B[0])) {
+ B = B.map((x) => [x]);
+ }
+ let p2 = B[0].length;
+ let B_cols = B[0].map((_, i) => B.map((x) => x[i]));
+ let product = A.map((row) => B_cols.map((col) => {
+ let ret = 0;
+ if (!Array.isArray(row)) {
+ for (let c4 of col) {
+ ret += row * c4;
+ }
+ return ret;
+ }
+ for (let i = 0; i < row.length; i++) {
+ ret += row[i] * (col[i] || 0);
+ }
+ return ret;
+ }));
+ if (m3 === 1) {
+ product = product[0];
+ }
+ if (p2 === 1) {
+ return product.map((x) => x[0]);
+ }
+ return product;
+}
+function isString(str) {
+ return type(str) === "string";
+}
+function type(o) {
+ let str = Object.prototype.toString.call(o);
+ return (str.match(/^\[object\s+(.*?)\]$/)[1] || "").toLowerCase();
+}
+function toPrecision(n2, precision) {
+ n2 = +n2;
+ precision = +precision;
+ let integerLength = (Math.floor(n2) + "").length;
+ if (precision > integerLength) {
+ return +n2.toFixed(precision - integerLength);
+ } else {
+ let p10 = 10 ** (integerLength - precision);
+ return Math.round(n2 / p10) * p10;
+ }
+}
+function parseFunction(str) {
+ if (!str) {
+ return;
+ }
+ str = str.trim();
+ const isFunctionRegex = /^([a-z]+)\((.+?)\)$/i;
+ const isNumberRegex = /^-?[\d.]+$/;
+ let parts = str.match(isFunctionRegex);
+ if (parts) {
+ let args = [];
+ parts[2].replace(/\/?\s*([-\w.]+(?:%|deg)?)/g, ($0, arg) => {
+ if (/%$/.test(arg)) {
+ arg = new Number(arg.slice(0, -1) / 100);
+ arg.type = "";
+ } else if (/deg$/.test(arg)) {
+ arg = new Number(+arg.slice(0, -3));
+ arg.type = "";
+ arg.unit = "deg";
+ } else if (isNumberRegex.test(arg)) {
+ arg = new Number(arg);
+ arg.type = "";
+ }
+ if ($0.startsWith("/")) {
+ arg = arg instanceof Number ? arg : new Number(arg);
+ arg.alpha = true;
+ }
+ args.push(arg);
+ });
+ return {
+ name: parts[1].toLowerCase(),
+ rawName: parts[1],
+ rawArgs: parts[2],
+ // An argument could be (as of css-color-4):
+ // a number, percentage, degrees (hue), ident (in color())
+ args
+ };
+ }
+}
+function last(arr) {
+ return arr[arr.length - 1];
+}
+function interpolate(start, end, p2) {
+ if (isNaN(start)) {
+ return end;
+ }
+ if (isNaN(end)) {
+ return start;
+ }
+ return start + (end - start) * p2;
+}
+function interpolateInv(start, end, value) {
+ return (value - start) / (end - start);
+}
+function mapRange(from, to2, value) {
+ return interpolate(to2[0], to2[1], interpolateInv(from[0], from[1], value));
+}
+function parseCoordGrammar(coordGrammars) {
+ return coordGrammars.map((coordGrammar2) => {
+ return coordGrammar2.split("|").map((type2) => {
+ type2 = type2.trim();
+ let range2 = type2.match(/^(<[a-z]+>)\[(-?[.\d]+),\s*(-?[.\d]+)\]?$/);
+ if (range2) {
+ let ret = new String(range2[1]);
+ ret.range = [+range2[2], +range2[3]];
+ return ret;
+ }
+ return type2;
+ });
+ });
+}
+var util = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ interpolate,
+ interpolateInv,
+ isString,
+ last,
+ mapRange,
+ multiplyMatrices,
+ parseCoordGrammar,
+ parseFunction,
+ toPrecision,
+ type
+});
+var Hooks = class {
+ add(name, callback, first) {
+ if (typeof arguments[0] != "string") {
+ for (var name in arguments[0]) {
+ this.add(name, arguments[0][name], arguments[1]);
+ }
+ return;
+ }
+ (Array.isArray(name) ? name : [name]).forEach(function(name2) {
+ this[name2] = this[name2] || [];
+ if (callback) {
+ this[name2][first ? "unshift" : "push"](callback);
+ }
+ }, this);
+ }
+ run(name, env) {
+ this[name] = this[name] || [];
+ this[name].forEach(function(callback) {
+ callback.call(env && env.context ? env.context : env, env);
+ });
+ }
+};
+var hooks = new Hooks();
+var defaults = {
+ gamut_mapping: "lch.c",
+ precision: 5,
+ deltaE: "76"
+ // Default deltaE method
+};
+var WHITES = {
+ // for compatibility, the four-digit chromaticity-derived ones everyone else uses
+ D50: [0.3457 / 0.3585, 1, (1 - 0.3457 - 0.3585) / 0.3585],
+ D65: [0.3127 / 0.329, 1, (1 - 0.3127 - 0.329) / 0.329]
+};
+function getWhite(name) {
+ if (Array.isArray(name)) {
+ return name;
+ }
+ return WHITES[name];
+}
+function adapt$1(W1, W2, XYZ, options = {}) {
+ W1 = getWhite(W1);
+ W2 = getWhite(W2);
+ if (!W1 || !W2) {
+ throw new TypeError(`Missing white point to convert ${!W1 ? "from" : ""}${!W1 && !W2 ? "/" : ""}${!W2 ? "to" : ""}`);
+ }
+ if (W1 === W2) {
+ return XYZ;
+ }
+ let env = { W1, W2, XYZ, options };
+ hooks.run("chromatic-adaptation-start", env);
+ if (!env.M) {
+ if (env.W1 === WHITES.D65 && env.W2 === WHITES.D50) {
+ env.M = [
+ [1.0479298208405488, 0.022946793341019088, -0.05019222954313557],
+ [0.029627815688159344, 0.990434484573249, -0.01707382502938514],
+ [-0.009243058152591178, 0.015055144896577895, 0.7518742899580008]
+ ];
+ } else if (env.W1 === WHITES.D50 && env.W2 === WHITES.D65) {
+ env.M = [
+ [0.9554734527042182, -0.023098536874261423, 0.0632593086610217],
+ [-0.028369706963208136, 1.0099954580058226, 0.021041398966943008],
+ [0.012314001688319899, -0.020507696433477912, 1.3303659366080753]
+ ];
+ }
+ }
+ hooks.run("chromatic-adaptation-end", env);
+ if (env.M) {
+ return multiplyMatrices(env.M, env.XYZ);
+ } else {
+ throw new TypeError("Only Bradford CAT with white points D50 and D65 supported for now.");
+ }
+}
+var \u03B5$4 = 75e-6;
+var _ColorSpace = class {
+ constructor(options) {
+ var _a, _b, _c, _d, _e, _f, _g;
+ this.id = options.id;
+ this.name = options.name;
+ this.base = options.base ? _ColorSpace.get(options.base) : null;
+ this.aliases = options.aliases;
+ if (this.base) {
+ this.fromBase = options.fromBase;
+ this.toBase = options.toBase;
+ }
+ let coords = (_a = options.coords) != null ? _a : this.base.coords;
+ for (let name in coords) {
+ if (!("name" in coords[name])) {
+ coords[name].name = name;
+ }
+ }
+ this.coords = coords;
+ let white2 = (_c = (_b = options.white) != null ? _b : this.base.white) != null ? _c : "D65";
+ this.white = getWhite(white2);
+ this.formats = (_d = options.formats) != null ? _d : {};
+ for (let name in this.formats) {
+ let format = this.formats[name];
+ format.type || (format.type = "function");
+ format.name || (format.name = name);
+ }
+ if (options.cssId && !((_e = this.formats.functions) == null ? void 0 : _e.color)) {
+ this.formats.color = { id: options.cssId };
+ Object.defineProperty(this, "cssId", { value: options.cssId });
+ } else if (((_f = this.formats) == null ? void 0 : _f.color) && !((_g = this.formats) == null ? void 0 : _g.color.id)) {
+ this.formats.color.id = this.id;
+ }
+ this.referred = options.referred;
+ Object.defineProperty(this, "path", {
+ value: getPath(this).reverse(),
+ writable: false,
+ enumerable: true,
+ configurable: true
+ });
+ hooks.run("colorspace-init-end", this);
+ }
+ inGamut(coords, { epsilon = \u03B5$4 } = {}) {
+ if (this.isPolar) {
+ coords = this.toBase(coords);
+ return this.base.inGamut(coords, { epsilon });
+ }
+ let coordMeta = Object.values(this.coords);
+ return coords.every((c4, i) => {
+ let meta = coordMeta[i];
+ if (meta.type !== "angle" && meta.range) {
+ if (Number.isNaN(c4)) {
+ return true;
+ }
+ let [min, max2] = meta.range;
+ return (min === void 0 || c4 >= min - epsilon) && (max2 === void 0 || c4 <= max2 + epsilon);
+ }
+ return true;
+ });
+ }
+ get cssId() {
+ var _a, _b;
+ return ((_b = (_a = this.formats.functions) == null ? void 0 : _a.color) == null ? void 0 : _b.id) || this.id;
+ }
+ get isPolar() {
+ for (let id in this.coords) {
+ if (this.coords[id].type === "angle") {
+ return true;
+ }
+ }
+ return false;
+ }
+ getFormat(format) {
+ if (typeof format === "object") {
+ format = processFormat(format, this);
+ return format;
+ }
+ let ret;
+ if (format === "default") {
+ ret = Object.values(this.formats)[0];
+ } else {
+ ret = this.formats[format];
+ }
+ if (ret) {
+ ret = processFormat(ret, this);
+ return ret;
+ }
+ return null;
+ }
+ // We cannot rely on simple === because then ColorSpace objects cannot be proxied
+ equals(space) {
+ if (!space) {
+ return false;
+ }
+ return this === space || this.id === space.id;
+ }
+ to(space, coords) {
+ if (arguments.length === 1) {
+ [space, coords] = [space.space, space.coords];
+ }
+ space = _ColorSpace.get(space);
+ if (this.equals(space)) {
+ return coords;
+ }
+ coords = coords.map((c4) => Number.isNaN(c4) ? 0 : c4);
+ let myPath = this.path;
+ let otherPath = space.path;
+ let connectionSpace, connectionSpaceIndex;
+ for (let i = 0; i < myPath.length; i++) {
+ if (myPath[i].equals(otherPath[i])) {
+ connectionSpace = myPath[i];
+ connectionSpaceIndex = i;
+ } else {
+ break;
+ }
+ }
+ if (!connectionSpace) {
+ throw new Error(`Cannot convert between color spaces ${this} and ${space}: no connection space was found`);
+ }
+ for (let i = myPath.length - 1; i > connectionSpaceIndex; i--) {
+ coords = myPath[i].toBase(coords);
+ }
+ for (let i = connectionSpaceIndex + 1; i < otherPath.length; i++) {
+ coords = otherPath[i].fromBase(coords);
+ }
+ return coords;
+ }
+ from(space, coords) {
+ if (arguments.length === 1) {
+ [space, coords] = [space.space, space.coords];
+ }
+ space = _ColorSpace.get(space);
+ return space.to(this, coords);
+ }
+ toString() {
+ return `${this.name} (${this.id})`;
+ }
+ getMinCoords() {
+ var _a;
+ let ret = [];
+ for (let id in this.coords) {
+ let meta = this.coords[id];
+ let range2 = meta.range || meta.refRange;
+ ret.push((_a = range2 == null ? void 0 : range2.min) != null ? _a : 0);
+ }
+ return ret;
+ }
+ // Returns array of unique color spaces
+ static get all() {
+ return [...new Set(Object.values(_ColorSpace.registry))];
+ }
+ static register(id, space) {
+ if (arguments.length === 1) {
+ space = arguments[0];
+ id = space.id;
+ }
+ space = this.get(space);
+ if (this.registry[id] && this.registry[id] !== space) {
+ throw new Error(`Duplicate color space registration: '${id}'`);
+ }
+ this.registry[id] = space;
+ if (arguments.length === 1 && space.aliases) {
+ for (let alias of space.aliases) {
+ this.register(alias, space);
+ }
+ }
+ return space;
+ }
+ /**
+ * Lookup ColorSpace object by name
+ * @param {ColorSpace | string} name
+ */
+ static get(space, ...alternatives) {
+ if (!space || space instanceof _ColorSpace) {
+ return space;
+ }
+ let argType = type(space);
+ if (argType === "string") {
+ let ret = _ColorSpace.registry[space.toLowerCase()];
+ if (!ret) {
+ throw new TypeError(`No color space found with id = "${space}"`);
+ }
+ return ret;
+ }
+ if (alternatives.length) {
+ return _ColorSpace.get(...alternatives);
+ }
+ throw new TypeError(`${space} is not a valid color space`);
+ }
+ /**
+ * Get metadata about a coordinate of a color space
+ *
+ * @static
+ * @param {Array | string} ref
+ * @param {ColorSpace | string} [workingSpace]
+ * @return {Object}
+ */
+ static resolveCoord(ref, workingSpace) {
+ var _a;
+ let coordType = type(ref);
+ let space, coord;
+ if (coordType === "string") {
+ if (ref.includes(".")) {
+ [space, coord] = ref.split(".");
+ } else {
+ [space, coord] = [, ref];
+ }
+ } else if (Array.isArray(ref)) {
+ [space, coord] = ref;
+ } else {
+ space = ref.space;
+ coord = ref.coordId;
+ }
+ space = _ColorSpace.get(space);
+ if (!space) {
+ space = workingSpace;
+ }
+ if (!space) {
+ throw new TypeError(`Cannot resolve coordinate reference ${ref}: No color space specified and relative references are not allowed here`);
+ }
+ coordType = type(coord);
+ if (coordType === "number" || coordType === "string" && coord >= 0) {
+ let meta = Object.entries(space.coords)[coord];
+ if (meta) {
+ return { space, id: meta[0], index: coord, ...meta[1] };
+ }
+ }
+ space = _ColorSpace.get(space);
+ let normalizedCoord = coord.toLowerCase();
+ let i = 0;
+ for (let id in space.coords) {
+ let meta = space.coords[id];
+ if (id.toLowerCase() === normalizedCoord || ((_a = meta.name) == null ? void 0 : _a.toLowerCase()) === normalizedCoord) {
+ return { space, id, index: i, ...meta };
+ }
+ i++;
+ }
+ throw new TypeError(`No "${coord}" coordinate found in ${space.name}. Its coordinates are: ${Object.keys(space.coords).join(", ")}`);
+ }
+};
+var ColorSpace = _ColorSpace;
+__publicField(ColorSpace, "registry", {});
+__publicField(ColorSpace, "DEFAULT_FORMAT", {
+ type: "functions",
+ name: "color"
+});
+function getPath(space) {
+ let ret = [space];
+ for (let s = space; s = s.base; ) {
+ ret.push(s);
+ }
+ return ret;
+}
+function processFormat(format, { coords } = {}) {
+ if (format.coords && !format.coordGrammar) {
+ format.type || (format.type = "function");
+ format.name || (format.name = "color");
+ format.coordGrammar = parseCoordGrammar(format.coords);
+ let coordFormats = Object.entries(coords).map(([id, coordMeta], i) => {
+ let outputType = format.coordGrammar[i][0];
+ let fromRange = coordMeta.range || coordMeta.refRange;
+ let toRange = outputType.range, suffix = "";
+ if (outputType == "") {
+ toRange = [0, 100];
+ suffix = "%";
+ } else if (outputType == "") {
+ suffix = "deg";
+ }
+ return { fromRange, toRange, suffix };
+ });
+ format.serializeCoords = (coords2, precision) => {
+ return coords2.map((c4, i) => {
+ let { fromRange, toRange, suffix } = coordFormats[i];
+ if (fromRange && toRange) {
+ c4 = mapRange(fromRange, toRange, c4);
+ }
+ c4 = toPrecision(c4, precision);
+ if (suffix) {
+ c4 += suffix;
+ }
+ return c4;
+ });
+ };
+ }
+ return format;
+}
+var XYZ_D65 = new ColorSpace({
+ id: "xyz-d65",
+ name: "XYZ D65",
+ coords: {
+ x: { name: "X" },
+ y: { name: "Y" },
+ z: { name: "Z" }
+ },
+ white: "D65",
+ formats: {
+ color: {
+ ids: ["xyz-d65", "xyz"]
+ }
+ },
+ aliases: ["xyz"]
+});
+var RGBColorSpace = class extends ColorSpace {
+ /**
+ * Creates a new RGB ColorSpace.
+ * If coords are not specified, they will use the default RGB coords.
+ * Instead of `fromBase()` and `toBase()` functions,
+ * you can specify to/from XYZ matrices and have `toBase()` and `fromBase()` automatically generated.
+ * @param {*} options - Same options as {@link ColorSpace} plus:
+ * @param {number[][]} options.toXYZ_M - Matrix to convert to XYZ
+ * @param {number[][]} options.fromXYZ_M - Matrix to convert from XYZ
+ */
+ constructor(options) {
+ var _a, _b, _c;
+ if (!options.coords) {
+ options.coords = {
+ r: {
+ range: [0, 1],
+ name: "Red"
+ },
+ g: {
+ range: [0, 1],
+ name: "Green"
+ },
+ b: {
+ range: [0, 1],
+ name: "Blue"
+ }
+ };
+ }
+ if (!options.base) {
+ options.base = XYZ_D65;
+ }
+ if (options.toXYZ_M && options.fromXYZ_M) {
+ (_a = options.toBase) != null ? _a : options.toBase = (rgb) => {
+ let xyz = multiplyMatrices(options.toXYZ_M, rgb);
+ if (this.white !== this.base.white) {
+ xyz = adapt$1(this.white, this.base.white, xyz);
+ }
+ return xyz;
+ };
+ (_b = options.fromBase) != null ? _b : options.fromBase = (xyz) => {
+ xyz = adapt$1(this.base.white, this.white, xyz);
+ return multiplyMatrices(options.fromXYZ_M, xyz);
+ };
+ }
+ (_c = options.referred) != null ? _c : options.referred = "display";
+ super(options);
+ }
+};
+function parse(str, { meta } = {}) {
+ var _a, _b, _c, _d, _e, _f;
+ let env = { "str": (_a = String(str)) == null ? void 0 : _a.trim() };
+ hooks.run("parse-start", env);
+ if (env.color) {
+ return env.color;
+ }
+ env.parsed = parseFunction(env.str);
+ if (env.parsed) {
+ let name = env.parsed.name;
+ if (name === "color") {
+ let id = env.parsed.args.shift();
+ let alpha = env.parsed.rawArgs.indexOf("/") > 0 ? env.parsed.args.pop() : 1;
+ for (let space of ColorSpace.all) {
+ let colorSpec = space.getFormat("color");
+ if (colorSpec) {
+ if (id === colorSpec.id || ((_b = colorSpec.ids) == null ? void 0 : _b.includes(id))) {
+ const coords = Object.keys(space.coords).map((_, i) => env.parsed.args[i] || 0);
+ if (meta) {
+ meta.formatId = "color";
+ }
+ return { spaceId: space.id, coords, alpha };
+ }
+ }
+ }
+ let didYouMean = "";
+ if (id in ColorSpace.registry) {
+ let cssId = (_e = (_d = (_c = ColorSpace.registry[id].formats) == null ? void 0 : _c.functions) == null ? void 0 : _d.color) == null ? void 0 : _e.id;
+ if (cssId) {
+ didYouMean = `Did you mean color(${cssId})?`;
+ }
+ }
+ throw new TypeError(`Cannot parse color(${id}). ` + (didYouMean || "Missing a plugin?"));
+ } else {
+ for (let space of ColorSpace.all) {
+ let format = space.getFormat(name);
+ if (format && format.type === "function") {
+ let alpha = 1;
+ if (format.lastAlpha || last(env.parsed.args).alpha) {
+ alpha = env.parsed.args.pop();
+ }
+ let coords = env.parsed.args;
+ let types;
+ if (format.coordGrammar) {
+ types = Object.entries(space.coords).map(([id, coordMeta], i) => {
+ var _a2;
+ let coordGrammar2 = format.coordGrammar[i];
+ let providedType = (_a2 = coords[i]) == null ? void 0 : _a2.type;
+ let type2 = coordGrammar2.find((c4) => c4 == providedType);
+ if (!type2) {
+ let coordName = coordMeta.name || id;
+ throw new TypeError(`${providedType} not allowed for ${coordName} in ${name}()`);
+ }
+ let fromRange = type2.range;
+ if (providedType === "") {
+ fromRange || (fromRange = [0, 1]);
+ }
+ let toRange = coordMeta.range || coordMeta.refRange;
+ if (fromRange && toRange) {
+ coords[i] = mapRange(fromRange, toRange, coords[i]);
+ }
+ return type2;
+ });
+ }
+ if (meta) {
+ Object.assign(meta, { formatId: format.name, types });
+ }
+ return {
+ spaceId: space.id,
+ coords,
+ alpha
+ };
+ }
+ }
+ }
+ } else {
+ for (let space of ColorSpace.all) {
+ for (let formatId in space.formats) {
+ let format = space.formats[formatId];
+ if (format.type !== "custom") {
+ continue;
+ }
+ if (format.test && !format.test(env.str)) {
+ continue;
+ }
+ let color = format.parse(env.str);
+ if (color) {
+ (_f = color.alpha) != null ? _f : color.alpha = 1;
+ if (meta) {
+ meta.formatId = formatId;
+ }
+ return color;
+ }
+ }
+ }
+ }
+ throw new TypeError(`Could not parse ${str} as a color. Missing a plugin?`);
+}
+function getColor(color) {
+ if (!color) {
+ throw new TypeError("Empty color reference");
+ }
+ if (isString(color)) {
+ color = parse(color);
+ }
+ let space = color.space || color.spaceId;
+ if (!(space instanceof ColorSpace)) {
+ color.space = ColorSpace.get(space);
+ }
+ if (color.alpha === void 0) {
+ color.alpha = 1;
+ }
+ return color;
+}
+function getAll(color, space) {
+ space = ColorSpace.get(space);
+ return space.from(color);
+}
+function get(color, prop) {
+ let { space, index } = ColorSpace.resolveCoord(prop, color.space);
+ let coords = getAll(color, space);
+ return coords[index];
+}
+function setAll(color, space, coords) {
+ space = ColorSpace.get(space);
+ color.coords = space.to(color.space, coords);
+ return color;
+}
+function set(color, prop, value) {
+ color = getColor(color);
+ if (arguments.length === 2 && type(arguments[1]) === "object") {
+ let object = arguments[1];
+ for (let p2 in object) {
+ set(color, p2, object[p2]);
+ }
+ } else {
+ if (typeof value === "function") {
+ value = value(get(color, prop));
+ }
+ let { space, index } = ColorSpace.resolveCoord(prop, color.space);
+ let coords = getAll(color, space);
+ coords[index] = value;
+ setAll(color, space, coords);
+ }
+ return color;
+}
+var XYZ_D50 = new ColorSpace({
+ id: "xyz-d50",
+ name: "XYZ D50",
+ white: "D50",
+ base: XYZ_D65,
+ fromBase: (coords) => adapt$1(XYZ_D65.white, "D50", coords),
+ toBase: (coords) => adapt$1("D50", XYZ_D65.white, coords),
+ formats: {
+ color: {}
+ }
+});
+var \u03B5$3 = 216 / 24389;
+var \u03B53$1 = 24 / 116;
+var \u03BA$1 = 24389 / 27;
+var white$1 = WHITES.D50;
+var lab = new ColorSpace({
+ id: "lab",
+ name: "Lab",
+ coords: {
+ l: {
+ refRange: [0, 100],
+ name: "L"
+ },
+ a: {
+ refRange: [-125, 125]
+ },
+ b: {
+ refRange: [-125, 125]
+ }
+ },
+ // Assuming XYZ is relative to D50, convert to CIE Lab
+ // from CIE standard, which now defines these as a rational fraction
+ white: white$1,
+ base: XYZ_D50,
+ // Convert D50-adapted XYX to Lab
+ // CIE 15.3:2004 section 8.2.1.1
+ fromBase(XYZ) {
+ let xyz = XYZ.map((value, i) => value / white$1[i]);
+ let f = xyz.map((value) => value > \u03B5$3 ? Math.cbrt(value) : (\u03BA$1 * value + 16) / 116);
+ return [
+ 116 * f[1] - 16,
+ // L
+ 500 * (f[0] - f[1]),
+ // a
+ 200 * (f[1] - f[2])
+ // b
+ ];
+ },
+ // Convert Lab to D50-adapted XYZ
+ // Same result as CIE 15.3:2004 Appendix D although the derivation is different
+ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+ toBase(Lab) {
+ let f = [];
+ f[1] = (Lab[0] + 16) / 116;
+ f[0] = Lab[1] / 500 + f[1];
+ f[2] = f[1] - Lab[2] / 200;
+ let xyz = [
+ f[0] > \u03B53$1 ? Math.pow(f[0], 3) : (116 * f[0] - 16) / \u03BA$1,
+ Lab[0] > 8 ? Math.pow((Lab[0] + 16) / 116, 3) : Lab[0] / \u03BA$1,
+ f[2] > \u03B53$1 ? Math.pow(f[2], 3) : (116 * f[2] - 16) / \u03BA$1
+ ];
+ return xyz.map((value, i) => value * white$1[i]);
+ },
+ formats: {
+ "lab": {
+ coords: [" | ", " | [-1,1]", " | [-1,1]"]
+ }
+ }
+});
+function constrain(angle) {
+ return (angle % 360 + 360) % 360;
+}
+function adjust(arc, angles) {
+ if (arc === "raw") {
+ return angles;
+ }
+ let [a1, a2] = angles.map(constrain);
+ let angleDiff = a2 - a1;
+ if (arc === "increasing") {
+ if (angleDiff < 0) {
+ a2 += 360;
+ }
+ } else if (arc === "decreasing") {
+ if (angleDiff > 0) {
+ a1 += 360;
+ }
+ } else if (arc === "longer") {
+ if (-180 < angleDiff && angleDiff < 180) {
+ if (angleDiff > 0) {
+ a1 += 360;
+ } else {
+ a2 += 360;
+ }
+ }
+ } else if (arc === "shorter") {
+ if (angleDiff > 180) {
+ a1 += 360;
+ } else if (angleDiff < -180) {
+ a2 += 360;
+ }
+ }
+ return [a1, a2];
+}
+var lch = new ColorSpace({
+ id: "lch",
+ name: "LCH",
+ coords: {
+ l: {
+ refRange: [0, 100],
+ name: "Lightness"
+ },
+ c: {
+ refRange: [0, 150],
+ name: "Chroma"
+ },
+ h: {
+ refRange: [0, 360],
+ type: "angle",
+ name: "Hue"
+ }
+ },
+ base: lab,
+ fromBase(Lab) {
+ let [L, a2, b2] = Lab;
+ let hue;
+ const \u03B52 = 0.02;
+ if (Math.abs(a2) < \u03B52 && Math.abs(b2) < \u03B52) {
+ hue = NaN;
+ } else {
+ hue = Math.atan2(b2, a2) * 180 / Math.PI;
+ }
+ return [
+ L,
+ // L is still L
+ Math.sqrt(a2 ** 2 + b2 ** 2),
+ // Chroma
+ constrain(hue)
+ // Hue, in degrees [0 to 360)
+ ];
+ },
+ toBase(LCH) {
+ let [Lightness, Chroma, Hue] = LCH;
+ if (Chroma < 0) {
+ Chroma = 0;
+ }
+ if (isNaN(Hue)) {
+ Hue = 0;
+ }
+ return [
+ Lightness,
+ // L is still L
+ Chroma * Math.cos(Hue * Math.PI / 180),
+ // a
+ Chroma * Math.sin(Hue * Math.PI / 180)
+ // b
+ ];
+ },
+ formats: {
+ "lch": {
+ coords: [" | ", " | ", " | "]
+ }
+ }
+});
+var Gfactor = 25 ** 7;
+var \u03C0$1 = Math.PI;
+var r2d = 180 / \u03C0$1;
+var d2r$1 = \u03C0$1 / 180;
+function deltaE2000(color, sample, { kL = 1, kC = 1, kH = 1 } = {}) {
+ let [L1, a1, b1] = lab.from(color);
+ let C1 = lch.from(lab, [L1, a1, b1])[1];
+ let [L2, a2, b2] = lab.from(sample);
+ let C2 = lch.from(lab, [L2, a2, b2])[1];
+ if (C1 < 0) {
+ C1 = 0;
+ }
+ if (C2 < 0) {
+ C2 = 0;
+ }
+ let Cbar = (C1 + C2) / 2;
+ let C7 = Cbar ** 7;
+ let G = 0.5 * (1 - Math.sqrt(C7 / (C7 + Gfactor)));
+ let adash1 = (1 + G) * a1;
+ let adash2 = (1 + G) * a2;
+ let Cdash1 = Math.sqrt(adash1 ** 2 + b1 ** 2);
+ let Cdash2 = Math.sqrt(adash2 ** 2 + b2 ** 2);
+ let h1 = adash1 === 0 && b1 === 0 ? 0 : Math.atan2(b1, adash1);
+ let h2 = adash2 === 0 && b2 === 0 ? 0 : Math.atan2(b2, adash2);
+ if (h1 < 0) {
+ h1 += 2 * \u03C0$1;
+ }
+ if (h2 < 0) {
+ h2 += 2 * \u03C0$1;
+ }
+ h1 *= r2d;
+ h2 *= r2d;
+ let \u0394L = L2 - L1;
+ let \u0394C = Cdash2 - Cdash1;
+ let hdiff = h2 - h1;
+ let hsum = h1 + h2;
+ let habs = Math.abs(hdiff);
+ let \u0394h;
+ if (Cdash1 * Cdash2 === 0) {
+ \u0394h = 0;
+ } else if (habs <= 180) {
+ \u0394h = hdiff;
+ } else if (hdiff > 180) {
+ \u0394h = hdiff - 360;
+ } else if (hdiff < -180) {
+ \u0394h = hdiff + 360;
+ } else {
+ console.log("the unthinkable has happened");
+ }
+ let \u0394H = 2 * Math.sqrt(Cdash2 * Cdash1) * Math.sin(\u0394h * d2r$1 / 2);
+ let Ldash = (L1 + L2) / 2;
+ let Cdash = (Cdash1 + Cdash2) / 2;
+ let Cdash7 = Math.pow(Cdash, 7);
+ let hdash;
+ if (Cdash1 * Cdash2 === 0) {
+ hdash = hsum;
+ } else if (habs <= 180) {
+ hdash = hsum / 2;
+ } else if (hsum < 360) {
+ hdash = (hsum + 360) / 2;
+ } else {
+ hdash = (hsum - 360) / 2;
+ }
+ let lsq = (Ldash - 50) ** 2;
+ let SL = 1 + 0.015 * lsq / Math.sqrt(20 + lsq);
+ let SC = 1 + 0.045 * Cdash;
+ let T = 1;
+ T -= 0.17 * Math.cos((hdash - 30) * d2r$1);
+ T += 0.24 * Math.cos(2 * hdash * d2r$1);
+ T += 0.32 * Math.cos((3 * hdash + 6) * d2r$1);
+ T -= 0.2 * Math.cos((4 * hdash - 63) * d2r$1);
+ let SH = 1 + 0.015 * Cdash * T;
+ let \u0394\u03B8 = 30 * Math.exp(-1 * ((hdash - 275) / 25) ** 2);
+ let RC = 2 * Math.sqrt(Cdash7 / (Cdash7 + Gfactor));
+ let RT = -1 * Math.sin(2 * \u0394\u03B8 * d2r$1) * RC;
+ let dE = (\u0394L / (kL * SL)) ** 2;
+ dE += (\u0394C / (kC * SC)) ** 2;
+ dE += (\u0394H / (kH * SH)) ** 2;
+ dE += RT * (\u0394C / (kC * SC)) * (\u0394H / (kH * SH));
+ return Math.sqrt(dE);
+}
+var \u03B5$2 = 75e-6;
+function inGamut(color, space = color.space, { epsilon = \u03B5$2 } = {}) {
+ color = getColor(color);
+ space = ColorSpace.get(space);
+ let coords = color.coords;
+ if (space !== color.space) {
+ coords = space.from(color);
+ }
+ return space.inGamut(coords, { epsilon });
+}
+function clone(color) {
+ return {
+ space: color.space,
+ coords: color.coords.slice(),
+ alpha: color.alpha
+ };
+}
+function toGamut(color, { method = defaults.gamut_mapping, space = color.space } = {}) {
+ if (isString(arguments[1])) {
+ space = arguments[1];
+ }
+ space = ColorSpace.get(space);
+ if (inGamut(color, space, { epsilon: 0 })) {
+ return getColor(color);
+ }
+ let spaceColor = to(color, space);
+ if (method !== "clip" && !inGamut(color, space)) {
+ let clipped = toGamut(clone(spaceColor), { method: "clip", space });
+ if (deltaE2000(color, clipped) > 2) {
+ let coordMeta = ColorSpace.resolveCoord(method);
+ let mapSpace = coordMeta.space;
+ let coordId = coordMeta.id;
+ let mappedColor = to(spaceColor, mapSpace);
+ let bounds = coordMeta.range || coordMeta.refRange;
+ let min = bounds[0];
+ let \u03B52 = 0.01;
+ let low = min;
+ let high = get(mappedColor, coordId);
+ while (high - low > \u03B52) {
+ let clipped2 = clone(mappedColor);
+ clipped2 = toGamut(clipped2, { space, method: "clip" });
+ let deltaE2 = deltaE2000(mappedColor, clipped2);
+ if (deltaE2 - 2 < \u03B52) {
+ low = get(mappedColor, coordId);
+ } else {
+ high = get(mappedColor, coordId);
+ }
+ set(mappedColor, coordId, (low + high) / 2);
+ }
+ spaceColor = to(mappedColor, space);
+ } else {
+ spaceColor = clipped;
+ }
+ }
+ if (method === "clip" || !inGamut(spaceColor, space, { epsilon: 0 })) {
+ let bounds = Object.values(space.coords).map((c4) => c4.range || []);
+ spaceColor.coords = spaceColor.coords.map((c4, i) => {
+ let [min, max2] = bounds[i];
+ if (min !== void 0) {
+ c4 = Math.max(min, c4);
+ }
+ if (max2 !== void 0) {
+ c4 = Math.min(c4, max2);
+ }
+ return c4;
+ });
+ }
+ if (space !== color.space) {
+ spaceColor = to(spaceColor, color.space);
+ }
+ color.coords = spaceColor.coords;
+ return color;
+}
+toGamut.returns = "color";
+function to(color, space, { inGamut: inGamut2 } = {}) {
+ color = getColor(color);
+ space = ColorSpace.get(space);
+ let coords = space.from(color);
+ let ret = { space, coords, alpha: color.alpha };
+ if (inGamut2) {
+ ret = toGamut(ret);
+ }
+ return ret;
+}
+to.returns = "color";
+function serialize(color, {
+ precision = defaults.precision,
+ format = "default",
+ inGamut: inGamut$1 = true,
+ ...customOptions
+} = {}) {
+ var _a, _b, _c;
+ let ret;
+ color = getColor(color);
+ let formatId = format;
+ format = (_b = (_a = color.space.getFormat(format)) != null ? _a : color.space.getFormat("default")) != null ? _b : ColorSpace.DEFAULT_FORMAT;
+ inGamut$1 || (inGamut$1 = format.toGamut);
+ let coords = color.coords;
+ coords = coords.map((c4) => c4 ? c4 : 0);
+ if (inGamut$1 && !inGamut(color)) {
+ coords = toGamut(clone(color), inGamut$1 === true ? void 0 : inGamut$1).coords;
+ }
+ if (format.type === "custom") {
+ customOptions.precision = precision;
+ if (format.serialize) {
+ ret = format.serialize(coords, color.alpha, customOptions);
+ } else {
+ throw new TypeError(`format ${formatId} can only be used to parse colors, not for serialization`);
+ }
+ } else {
+ let name = format.name || "color";
+ if (format.serializeCoords) {
+ coords = format.serializeCoords(coords, precision);
+ } else {
+ if (precision !== null) {
+ coords = coords.map((c4) => toPrecision(c4, precision));
+ }
+ }
+ let args = [...coords];
+ if (name === "color") {
+ let cssId = format.id || ((_c = format.ids) == null ? void 0 : _c[0]) || color.space.id;
+ args.unshift(cssId);
+ }
+ let alpha = color.alpha;
+ if (precision !== null) {
+ alpha = toPrecision(alpha, precision);
+ }
+ let strAlpha = color.alpha < 1 && !format.noAlpha ? `${format.commas ? "," : " /"} ${alpha}` : "";
+ ret = `${name}(${args.join(format.commas ? ", " : " ")}${strAlpha})`;
+ }
+ return ret;
+}
+var toXYZ_M$5 = [
+ [0.6369580483012914, 0.14461690358620832, 0.1688809751641721],
+ [0.2627002120112671, 0.6779980715188708, 0.05930171646986196],
+ [0, 0.028072693049087428, 1.060985057710791]
+];
+var fromXYZ_M$5 = [
+ [1.716651187971268, -0.355670783776392, -0.25336628137366],
+ [-0.666684351832489, 1.616481236634939, 0.0157685458139111],
+ [0.017639857445311, -0.042770613257809, 0.942103121235474]
+];
+var REC2020Linear = new RGBColorSpace({
+ id: "rec2020-linear",
+ name: "Linear REC.2020",
+ white: "D65",
+ toXYZ_M: toXYZ_M$5,
+ fromXYZ_M: fromXYZ_M$5,
+ formats: {
+ color: {}
+ }
+});
+var \u03B1 = 1.09929682680944;
+var \u03B2 = 0.018053968510807;
+var REC2020 = new RGBColorSpace({
+ id: "rec2020",
+ name: "REC.2020",
+ base: REC2020Linear,
+ // Non-linear transfer function from Rec. ITU-R BT.2020-2 table 4
+ toBase(RGB) {
+ return RGB.map(function(val) {
+ if (val < \u03B2 * 4.5) {
+ return val / 4.5;
+ }
+ return Math.pow((val + \u03B1 - 1) / \u03B1, 1 / 0.45);
+ });
+ },
+ fromBase(RGB) {
+ return RGB.map(function(val) {
+ if (val >= \u03B2) {
+ return \u03B1 * Math.pow(val, 0.45) - (\u03B1 - 1);
+ }
+ return 4.5 * val;
+ });
+ },
+ formats: {
+ color: {}
+ }
+});
+var toXYZ_M$4 = [
+ [0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
+ [0.2289745640697488, 0.6917385218365064, 0.079286914093745],
+ [0, 0.04511338185890264, 1.043944368900976]
+];
+var fromXYZ_M$4 = [
+ [2.493496911941425, -0.9313836179191239, -0.40271078445071684],
+ [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
+ [0.03584583024378447, -0.07617238926804182, 0.9568845240076872]
+];
+var P3Linear = new RGBColorSpace({
+ id: "p3-linear",
+ name: "Linear P3",
+ white: "D65",
+ toXYZ_M: toXYZ_M$4,
+ fromXYZ_M: fromXYZ_M$4
+});
+var toXYZ_M$3 = [
+ [0.41239079926595934, 0.357584339383878, 0.1804807884018343],
+ [0.21263900587151027, 0.715168678767756, 0.07219231536073371],
+ [0.01933081871559182, 0.11919477979462598, 0.9505321522496607]
+];
+var fromXYZ_M$3 = [
+ [3.2409699419045226, -1.537383177570094, -0.4986107602930034],
+ [-0.9692436362808796, 1.8759675015077202, 0.04155505740717559],
+ [0.05563007969699366, -0.20397695888897652, 1.0569715142428786]
+];
+var sRGBLinear = new RGBColorSpace({
+ id: "srgb-linear",
+ name: "Linear sRGB",
+ white: "D65",
+ toXYZ_M: toXYZ_M$3,
+ fromXYZ_M: fromXYZ_M$3,
+ formats: {
+ color: {}
+ }
+});
+var KEYWORDS = {
+ "aliceblue": [240 / 255, 248 / 255, 1],
+ "antiquewhite": [250 / 255, 235 / 255, 215 / 255],
+ "aqua": [0, 1, 1],
+ "aquamarine": [127 / 255, 1, 212 / 255],
+ "azure": [240 / 255, 1, 1],
+ "beige": [245 / 255, 245 / 255, 220 / 255],
+ "bisque": [1, 228 / 255, 196 / 255],
+ "black": [0, 0, 0],
+ "blanchedalmond": [1, 235 / 255, 205 / 255],
+ "blue": [0, 0, 1],
+ "blueviolet": [138 / 255, 43 / 255, 226 / 255],
+ "brown": [165 / 255, 42 / 255, 42 / 255],
+ "burlywood": [222 / 255, 184 / 255, 135 / 255],
+ "cadetblue": [95 / 255, 158 / 255, 160 / 255],
+ "chartreuse": [127 / 255, 1, 0],
+ "chocolate": [210 / 255, 105 / 255, 30 / 255],
+ "coral": [1, 127 / 255, 80 / 255],
+ "cornflowerblue": [100 / 255, 149 / 255, 237 / 255],
+ "cornsilk": [1, 248 / 255, 220 / 255],
+ "crimson": [220 / 255, 20 / 255, 60 / 255],
+ "cyan": [0, 1, 1],
+ "darkblue": [0, 0, 139 / 255],
+ "darkcyan": [0, 139 / 255, 139 / 255],
+ "darkgoldenrod": [184 / 255, 134 / 255, 11 / 255],
+ "darkgray": [169 / 255, 169 / 255, 169 / 255],
+ "darkgreen": [0, 100 / 255, 0],
+ "darkgrey": [169 / 255, 169 / 255, 169 / 255],
+ "darkkhaki": [189 / 255, 183 / 255, 107 / 255],
+ "darkmagenta": [139 / 255, 0, 139 / 255],
+ "darkolivegreen": [85 / 255, 107 / 255, 47 / 255],
+ "darkorange": [1, 140 / 255, 0],
+ "darkorchid": [153 / 255, 50 / 255, 204 / 255],
+ "darkred": [139 / 255, 0, 0],
+ "darksalmon": [233 / 255, 150 / 255, 122 / 255],
+ "darkseagreen": [143 / 255, 188 / 255, 143 / 255],
+ "darkslateblue": [72 / 255, 61 / 255, 139 / 255],
+ "darkslategray": [47 / 255, 79 / 255, 79 / 255],
+ "darkslategrey": [47 / 255, 79 / 255, 79 / 255],
+ "darkturquoise": [0, 206 / 255, 209 / 255],
+ "darkviolet": [148 / 255, 0, 211 / 255],
+ "deeppink": [1, 20 / 255, 147 / 255],
+ "deepskyblue": [0, 191 / 255, 1],
+ "dimgray": [105 / 255, 105 / 255, 105 / 255],
+ "dimgrey": [105 / 255, 105 / 255, 105 / 255],
+ "dodgerblue": [30 / 255, 144 / 255, 1],
+ "firebrick": [178 / 255, 34 / 255, 34 / 255],
+ "floralwhite": [1, 250 / 255, 240 / 255],
+ "forestgreen": [34 / 255, 139 / 255, 34 / 255],
+ "fuchsia": [1, 0, 1],
+ "gainsboro": [220 / 255, 220 / 255, 220 / 255],
+ "ghostwhite": [248 / 255, 248 / 255, 1],
+ "gold": [1, 215 / 255, 0],
+ "goldenrod": [218 / 255, 165 / 255, 32 / 255],
+ "gray": [128 / 255, 128 / 255, 128 / 255],
+ "green": [0, 128 / 255, 0],
+ "greenyellow": [173 / 255, 1, 47 / 255],
+ "grey": [128 / 255, 128 / 255, 128 / 255],
+ "honeydew": [240 / 255, 1, 240 / 255],
+ "hotpink": [1, 105 / 255, 180 / 255],
+ "indianred": [205 / 255, 92 / 255, 92 / 255],
+ "indigo": [75 / 255, 0, 130 / 255],
+ "ivory": [1, 1, 240 / 255],
+ "khaki": [240 / 255, 230 / 255, 140 / 255],
+ "lavender": [230 / 255, 230 / 255, 250 / 255],
+ "lavenderblush": [1, 240 / 255, 245 / 255],
+ "lawngreen": [124 / 255, 252 / 255, 0],
+ "lemonchiffon": [1, 250 / 255, 205 / 255],
+ "lightblue": [173 / 255, 216 / 255, 230 / 255],
+ "lightcoral": [240 / 255, 128 / 255, 128 / 255],
+ "lightcyan": [224 / 255, 1, 1],
+ "lightgoldenrodyellow": [250 / 255, 250 / 255, 210 / 255],
+ "lightgray": [211 / 255, 211 / 255, 211 / 255],
+ "lightgreen": [144 / 255, 238 / 255, 144 / 255],
+ "lightgrey": [211 / 255, 211 / 255, 211 / 255],
+ "lightpink": [1, 182 / 255, 193 / 255],
+ "lightsalmon": [1, 160 / 255, 122 / 255],
+ "lightseagreen": [32 / 255, 178 / 255, 170 / 255],
+ "lightskyblue": [135 / 255, 206 / 255, 250 / 255],
+ "lightslategray": [119 / 255, 136 / 255, 153 / 255],
+ "lightslategrey": [119 / 255, 136 / 255, 153 / 255],
+ "lightsteelblue": [176 / 255, 196 / 255, 222 / 255],
+ "lightyellow": [1, 1, 224 / 255],
+ "lime": [0, 1, 0],
+ "limegreen": [50 / 255, 205 / 255, 50 / 255],
+ "linen": [250 / 255, 240 / 255, 230 / 255],
+ "magenta": [1, 0, 1],
+ "maroon": [128 / 255, 0, 0],
+ "mediumaquamarine": [102 / 255, 205 / 255, 170 / 255],
+ "mediumblue": [0, 0, 205 / 255],
+ "mediumorchid": [186 / 255, 85 / 255, 211 / 255],
+ "mediumpurple": [147 / 255, 112 / 255, 219 / 255],
+ "mediumseagreen": [60 / 255, 179 / 255, 113 / 255],
+ "mediumslateblue": [123 / 255, 104 / 255, 238 / 255],
+ "mediumspringgreen": [0, 250 / 255, 154 / 255],
+ "mediumturquoise": [72 / 255, 209 / 255, 204 / 255],
+ "mediumvioletred": [199 / 255, 21 / 255, 133 / 255],
+ "midnightblue": [25 / 255, 25 / 255, 112 / 255],
+ "mintcream": [245 / 255, 1, 250 / 255],
+ "mistyrose": [1, 228 / 255, 225 / 255],
+ "moccasin": [1, 228 / 255, 181 / 255],
+ "navajowhite": [1, 222 / 255, 173 / 255],
+ "navy": [0, 0, 128 / 255],
+ "oldlace": [253 / 255, 245 / 255, 230 / 255],
+ "olive": [128 / 255, 128 / 255, 0],
+ "olivedrab": [107 / 255, 142 / 255, 35 / 255],
+ "orange": [1, 165 / 255, 0],
+ "orangered": [1, 69 / 255, 0],
+ "orchid": [218 / 255, 112 / 255, 214 / 255],
+ "palegoldenrod": [238 / 255, 232 / 255, 170 / 255],
+ "palegreen": [152 / 255, 251 / 255, 152 / 255],
+ "paleturquoise": [175 / 255, 238 / 255, 238 / 255],
+ "palevioletred": [219 / 255, 112 / 255, 147 / 255],
+ "papayawhip": [1, 239 / 255, 213 / 255],
+ "peachpuff": [1, 218 / 255, 185 / 255],
+ "peru": [205 / 255, 133 / 255, 63 / 255],
+ "pink": [1, 192 / 255, 203 / 255],
+ "plum": [221 / 255, 160 / 255, 221 / 255],
+ "powderblue": [176 / 255, 224 / 255, 230 / 255],
+ "purple": [128 / 255, 0, 128 / 255],
+ "rebeccapurple": [102 / 255, 51 / 255, 153 / 255],
+ "red": [1, 0, 0],
+ "rosybrown": [188 / 255, 143 / 255, 143 / 255],
+ "royalblue": [65 / 255, 105 / 255, 225 / 255],
+ "saddlebrown": [139 / 255, 69 / 255, 19 / 255],
+ "salmon": [250 / 255, 128 / 255, 114 / 255],
+ "sandybrown": [244 / 255, 164 / 255, 96 / 255],
+ "seagreen": [46 / 255, 139 / 255, 87 / 255],
+ "seashell": [1, 245 / 255, 238 / 255],
+ "sienna": [160 / 255, 82 / 255, 45 / 255],
+ "silver": [192 / 255, 192 / 255, 192 / 255],
+ "skyblue": [135 / 255, 206 / 255, 235 / 255],
+ "slateblue": [106 / 255, 90 / 255, 205 / 255],
+ "slategray": [112 / 255, 128 / 255, 144 / 255],
+ "slategrey": [112 / 255, 128 / 255, 144 / 255],
+ "snow": [1, 250 / 255, 250 / 255],
+ "springgreen": [0, 1, 127 / 255],
+ "steelblue": [70 / 255, 130 / 255, 180 / 255],
+ "tan": [210 / 255, 180 / 255, 140 / 255],
+ "teal": [0, 128 / 255, 128 / 255],
+ "thistle": [216 / 255, 191 / 255, 216 / 255],
+ "tomato": [1, 99 / 255, 71 / 255],
+ "turquoise": [64 / 255, 224 / 255, 208 / 255],
+ "violet": [238 / 255, 130 / 255, 238 / 255],
+ "wheat": [245 / 255, 222 / 255, 179 / 255],
+ "white": [1, 1, 1],
+ "whitesmoke": [245 / 255, 245 / 255, 245 / 255],
+ "yellow": [1, 1, 0],
+ "yellowgreen": [154 / 255, 205 / 255, 50 / 255]
+};
+var coordGrammar = Array(3).fill(" | [0, 255]");
+var coordGrammarNumber = Array(3).fill("[0, 255]");
+var sRGB = new RGBColorSpace({
+ id: "srgb",
+ name: "sRGB",
+ base: sRGBLinear,
+ fromBase: (rgb) => {
+ return rgb.map((val) => {
+ let sign = val < 0 ? -1 : 1;
+ let abs = val * sign;
+ if (abs > 31308e-7) {
+ return sign * (1.055 * abs ** (1 / 2.4) - 0.055);
+ }
+ return 12.92 * val;
+ });
+ },
+ toBase: (rgb) => {
+ return rgb.map((val) => {
+ let sign = val < 0 ? -1 : 1;
+ let abs = val * sign;
+ if (abs < 0.04045) {
+ return val / 12.92;
+ }
+ return sign * ((abs + 0.055) / 1.055) ** 2.4;
+ });
+ },
+ formats: {
+ "rgb": {
+ coords: coordGrammar
+ },
+ "rgb_number": {
+ name: "rgb",
+ commas: true,
+ coords: coordGrammarNumber,
+ noAlpha: true
+ },
+ "color": {
+ /* use defaults */
+ },
+ "rgba": {
+ coords: coordGrammar,
+ commas: true,
+ lastAlpha: true
+ },
+ "rgba_number": {
+ name: "rgba",
+ commas: true,
+ coords: coordGrammarNumber
+ },
+ "hex": {
+ type: "custom",
+ toGamut: true,
+ test: (str) => /^#([a-f0-9]{3,4}){1,2}$/i.test(str),
+ parse(str) {
+ if (str.length <= 5) {
+ str = str.replace(/[a-f0-9]/gi, "$&$&");
+ }
+ let rgba = [];
+ str.replace(/[a-f0-9]{2}/gi, (component) => {
+ rgba.push(parseInt(component, 16) / 255);
+ });
+ return {
+ spaceId: "srgb",
+ coords: rgba.slice(0, 3),
+ alpha: rgba.slice(3)[0]
+ };
+ },
+ serialize: (coords, alpha, {
+ collapse = true
+ // collapse to 3-4 digit hex when possible?
+ } = {}) => {
+ if (alpha < 1) {
+ coords.push(alpha);
+ }
+ coords = coords.map((c4) => Math.round(c4 * 255));
+ let collapsible = collapse && coords.every((c4) => c4 % 17 === 0);
+ let hex = coords.map((c4) => {
+ if (collapsible) {
+ return (c4 / 17).toString(16);
+ }
+ return c4.toString(16).padStart(2, "0");
+ }).join("");
+ return "#" + hex;
+ }
+ },
+ "keyword": {
+ type: "custom",
+ test: (str) => /^[a-z]+$/i.test(str),
+ parse(str) {
+ str = str.toLowerCase();
+ let ret = { spaceId: "srgb", coords: null, alpha: 1 };
+ if (str === "transparent") {
+ ret.coords = KEYWORDS.black;
+ ret.alpha = 0;
+ } else {
+ ret.coords = KEYWORDS[str];
+ }
+ if (ret.coords) {
+ return ret;
+ }
+ }
+ }
+ }
+});
+var P3 = new RGBColorSpace({
+ id: "p3",
+ name: "P3",
+ base: P3Linear,
+ // Gamma encoding/decoding is the same as sRGB
+ fromBase: sRGB.fromBase,
+ toBase: sRGB.toBase,
+ formats: {
+ color: {
+ id: "display-p3"
+ }
+ }
+});
+defaults.display_space = sRGB;
+if (typeof CSS !== "undefined" && CSS.supports) {
+ for (let space of [lab, REC2020, P3]) {
+ let coords = space.getMinCoords();
+ let color = { space, coords, alpha: 1 };
+ let str = serialize(color);
+ if (CSS.supports("color", str)) {
+ defaults.display_space = space;
+ break;
+ }
+ }
+}
+function display(color, { space = defaults.display_space, ...options } = {}) {
+ let ret = serialize(color, options);
+ if (typeof CSS === "undefined" || CSS.supports("color", ret) || !defaults.display_space) {
+ ret = new String(ret);
+ ret.color = color;
+ } else {
+ let fallbackColor = to(color, space);
+ ret = new String(serialize(fallbackColor, options));
+ ret.color = fallbackColor;
+ }
+ return ret;
+}
+function distance(color1, color2, space = "lab") {
+ space = ColorSpace.get(space);
+ let coords1 = space.from(color1);
+ let coords2 = space.from(color2);
+ return Math.sqrt(coords1.reduce((acc, c12, i) => {
+ let c22 = coords2[i];
+ if (isNaN(c12) || isNaN(c22)) {
+ return acc;
+ }
+ return acc + (c22 - c12) ** 2;
+ }, 0));
+}
+function equals(color1, color2) {
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ return color1.space === color2.space && color1.alpha === color2.alpha && color1.coords.every((c4, i) => c4 === color2.coords[i]);
+}
+function getLuminance(color) {
+ return get(color, [XYZ_D65, "y"]);
+}
+function setLuminance(color, value) {
+ set(color, [XYZ_D65, "y"], value);
+}
+function register$2(Color2) {
+ Object.defineProperty(Color2.prototype, "luminance", {
+ get() {
+ return getLuminance(this);
+ },
+ set(value) {
+ setLuminance(this, value);
+ }
+ });
+}
+var luminance = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ getLuminance,
+ register: register$2,
+ setLuminance
+});
+function contrastWCAG21(color1, color2) {
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ let Y1 = Math.max(getLuminance(color1), 0);
+ let Y2 = Math.max(getLuminance(color2), 0);
+ if (Y2 > Y1) {
+ [Y1, Y2] = [Y2, Y1];
+ }
+ return (Y1 + 0.05) / (Y2 + 0.05);
+}
+var normBG = 0.56;
+var normTXT = 0.57;
+var revTXT = 0.62;
+var revBG = 0.65;
+var blkThrs = 0.022;
+var blkClmp = 1.414;
+var loClip = 0.1;
+var deltaYmin = 5e-4;
+var scaleBoW = 1.14;
+var loBoWoffset = 0.027;
+var scaleWoB = 1.14;
+function fclamp(Y) {
+ if (Y >= blkThrs) {
+ return Y;
+ }
+ return Y + (blkThrs - Y) ** blkClmp;
+}
+function linearize(val) {
+ let sign = val < 0 ? -1 : 1;
+ let abs = Math.abs(val);
+ return sign * Math.pow(abs, 2.4);
+}
+function contrastAPCA(background, foreground) {
+ foreground = getColor(foreground);
+ background = getColor(background);
+ let S;
+ let C;
+ let Sapc;
+ let R, G, B;
+ foreground = to(foreground, "srgb");
+ [R, G, B] = foreground.coords;
+ let lumTxt = linearize(R) * 0.2126729 + linearize(G) * 0.7151522 + linearize(B) * 0.072175;
+ background = to(background, "srgb");
+ [R, G, B] = background.coords;
+ let lumBg = linearize(R) * 0.2126729 + linearize(G) * 0.7151522 + linearize(B) * 0.072175;
+ let Ytxt = fclamp(lumTxt);
+ let Ybg = fclamp(lumBg);
+ let BoW = Ybg > Ytxt;
+ if (Math.abs(Ybg - Ytxt) < deltaYmin) {
+ C = 0;
+ } else {
+ if (BoW) {
+ S = Ybg ** normBG - Ytxt ** normTXT;
+ C = S * scaleBoW;
+ } else {
+ S = Ybg ** revBG - Ytxt ** revTXT;
+ C = S * scaleWoB;
+ }
+ }
+ if (Math.abs(C) < loClip) {
+ Sapc = 0;
+ } else if (C > 0) {
+ Sapc = C - loBoWoffset;
+ } else {
+ Sapc = C + loBoWoffset;
+ }
+ return Sapc * 100;
+}
+function contrastMichelson(color1, color2) {
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ let Y1 = Math.max(getLuminance(color1), 0);
+ let Y2 = Math.max(getLuminance(color2), 0);
+ if (Y2 > Y1) {
+ [Y1, Y2] = [Y2, Y1];
+ }
+ let denom = Y1 + Y2;
+ return denom === 0 ? 0 : (Y1 - Y2) / denom;
+}
+var max = 5e4;
+function contrastWeber(color1, color2) {
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ let Y1 = Math.max(getLuminance(color1), 0);
+ let Y2 = Math.max(getLuminance(color2), 0);
+ if (Y2 > Y1) {
+ [Y1, Y2] = [Y2, Y1];
+ }
+ return Y2 === 0 ? max : (Y1 - Y2) / Y2;
+}
+function contrastLstar(color1, color2) {
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ let L1 = get(color1, [lab, "l"]);
+ let L2 = get(color2, [lab, "l"]);
+ return Math.abs(L1 - L2);
+}
+var \u03B5$1 = 216 / 24389;
+var \u03B53 = 24 / 116;
+var \u03BA = 24389 / 27;
+var white = WHITES.D65;
+var lab_d65 = new ColorSpace({
+ id: "lab-d65",
+ name: "Lab D65",
+ coords: {
+ l: {
+ refRange: [0, 100],
+ name: "L"
+ },
+ a: {
+ refRange: [-125, 125]
+ },
+ b: {
+ refRange: [-125, 125]
+ }
+ },
+ // Assuming XYZ is relative to D65, convert to CIE Lab
+ // from CIE standard, which now defines these as a rational fraction
+ white,
+ base: XYZ_D65,
+ // Convert D65-adapted XYZ to Lab
+ // CIE 15.3:2004 section 8.2.1.1
+ fromBase(XYZ) {
+ let xyz = XYZ.map((value, i) => value / white[i]);
+ let f = xyz.map((value) => value > \u03B5$1 ? Math.cbrt(value) : (\u03BA * value + 16) / 116);
+ return [
+ 116 * f[1] - 16,
+ // L
+ 500 * (f[0] - f[1]),
+ // a
+ 200 * (f[1] - f[2])
+ // b
+ ];
+ },
+ // Convert Lab to D65-adapted XYZ
+ // Same result as CIE 15.3:2004 Appendix D although the derivation is different
+ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+ toBase(Lab) {
+ let f = [];
+ f[1] = (Lab[0] + 16) / 116;
+ f[0] = Lab[1] / 500 + f[1];
+ f[2] = f[1] - Lab[2] / 200;
+ let xyz = [
+ f[0] > \u03B53 ? Math.pow(f[0], 3) : (116 * f[0] - 16) / \u03BA,
+ Lab[0] > 8 ? Math.pow((Lab[0] + 16) / 116, 3) : Lab[0] / \u03BA,
+ f[2] > \u03B53 ? Math.pow(f[2], 3) : (116 * f[2] - 16) / \u03BA
+ ];
+ return xyz.map((value, i) => value * white[i]);
+ },
+ formats: {
+ "lab-d65": {
+ coords: [" | ", " | [-1,1]", " | [-1,1]"]
+ }
+ }
+});
+var phi = Math.pow(5, 0.5) * 0.5 + 0.5;
+function contrastDeltaPhi(color1, color2) {
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ let Lstr1 = get(color1, [lab_d65, "l"]);
+ let Lstr2 = get(color2, [lab_d65, "l"]);
+ let deltaPhiStar = Math.abs(Math.pow(Lstr1, phi) - Math.pow(Lstr2, phi));
+ let contrast2 = Math.pow(deltaPhiStar, 1 / phi) * Math.SQRT2 - 40;
+ return contrast2 < 7.5 ? 0 : contrast2;
+}
+var contrastMethods = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ contrastAPCA,
+ contrastDeltaPhi,
+ contrastLstar,
+ contrastMichelson,
+ contrastWCAG21,
+ contrastWeber
+});
+function contrast(background, foreground, o = {}) {
+ if (isString(o)) {
+ o = { algorithm: o };
+ }
+ let { algorithm, ...rest } = o;
+ if (!algorithm) {
+ let algorithms = Object.keys(contrastMethods).map((a2) => a2.replace(/^contrast/, "")).join(", ");
+ throw new TypeError(`contrast() function needs a contrast algorithm. Please specify one of: ${algorithms}`);
+ }
+ background = getColor(background);
+ foreground = getColor(foreground);
+ for (let a2 in contrastMethods) {
+ if ("contrast" + algorithm.toLowerCase() === a2.toLowerCase()) {
+ return contrastMethods[a2](background, foreground, rest);
+ }
+ }
+ throw new TypeError(`Unknown contrast algorithm: ${algorithm}`);
+}
+function uv(color) {
+ let [X, Y, Z] = getAll(color, XYZ_D65);
+ let denom = X + 15 * Y + 3 * Z;
+ return [4 * X / denom, 9 * Y / denom];
+}
+function xy(color) {
+ let [X, Y, Z] = getAll(color, XYZ_D65);
+ let sum = X + Y + Z;
+ return [X / sum, Y / sum];
+}
+function register$1(Color2) {
+ Object.defineProperty(Color2.prototype, "uv", {
+ get() {
+ return uv(this);
+ }
+ });
+ Object.defineProperty(Color2.prototype, "xy", {
+ get() {
+ return xy(this);
+ }
+ });
+}
+var chromaticity = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ register: register$1,
+ uv,
+ xy
+});
+function deltaE76(color, sample) {
+ return distance(color, sample, "lab");
+}
+var \u03C0 = Math.PI;
+var d2r = \u03C0 / 180;
+function deltaECMC(color, sample, { l = 2, c: c4 = 1 } = {}) {
+ let [L1, a1, b1] = lab.from(color);
+ let [, C1, H1] = lch.from(lab, [L1, a1, b1]);
+ let [L2, a2, b2] = lab.from(sample);
+ let C2 = lch.from(lab, [L2, a2, b2])[1];
+ if (C1 < 0) {
+ C1 = 0;
+ }
+ if (C2 < 0) {
+ C2 = 0;
+ }
+ let \u0394L = L1 - L2;
+ let \u0394C = C1 - C2;
+ let \u0394a = a1 - a2;
+ let \u0394b = b1 - b2;
+ let H2 = \u0394a ** 2 + \u0394b ** 2 - \u0394C ** 2;
+ let SL = 0.511;
+ if (L1 >= 16) {
+ SL = 0.040975 * L1 / (1 + 0.01765 * L1);
+ }
+ let SC = 0.0638 * C1 / (1 + 0.0131 * C1) + 0.638;
+ let T;
+ if (Number.isNaN(H1)) {
+ H1 = 0;
+ }
+ if (H1 >= 164 && H1 <= 345) {
+ T = 0.56 + Math.abs(0.2 * Math.cos((H1 + 168) * d2r));
+ } else {
+ T = 0.36 + Math.abs(0.4 * Math.cos((H1 + 35) * d2r));
+ }
+ let C4 = Math.pow(C1, 4);
+ let F = Math.sqrt(C4 / (C4 + 1900));
+ let SH = SC * (F * T + 1 - F);
+ let dE = (\u0394L / (l * SL)) ** 2;
+ dE += (\u0394C / (c4 * SC)) ** 2;
+ dE += H2 / SH ** 2;
+ return Math.sqrt(dE);
+}
+var Yw$1 = 203;
+var XYZ_Abs_D65 = new ColorSpace({
+ // Absolute CIE XYZ, with a D65 whitepoint,
+ // as used in most HDR colorspaces as a starting point.
+ // SDR spaces are converted per BT.2048
+ // so that diffuse, media white is 203 cd/m²
+ id: "xyz-abs-d65",
+ name: "Absolute XYZ D65",
+ coords: {
+ x: {
+ refRange: [0, 9504.7],
+ name: "Xa"
+ },
+ y: {
+ refRange: [0, 1e4],
+ name: "Ya"
+ },
+ z: {
+ refRange: [0, 10888.3],
+ name: "Za"
+ }
+ },
+ base: XYZ_D65,
+ fromBase(XYZ) {
+ return XYZ.map((v) => Math.max(v * Yw$1, 0));
+ },
+ toBase(AbsXYZ) {
+ return AbsXYZ.map((v) => Math.max(v / Yw$1, 0));
+ }
+});
+var b$1 = 1.15;
+var g = 0.66;
+var n$1 = 2610 / 2 ** 14;
+var ninv$1 = 2 ** 14 / 2610;
+var c1$2 = 3424 / 2 ** 12;
+var c2$2 = 2413 / 2 ** 7;
+var c3$2 = 2392 / 2 ** 7;
+var p = 1.7 * 2523 / 2 ** 5;
+var pinv = 2 ** 5 / (1.7 * 2523);
+var d = -0.56;
+var d0 = 16295499532821565e-27;
+var XYZtoCone_M = [
+ [0.41478972, 0.579999, 0.014648],
+ [-0.20151, 1.120649, 0.0531008],
+ [-0.0166008, 0.2648, 0.6684799]
+];
+var ConetoXYZ_M = [
+ [1.9242264357876067, -1.0047923125953657, 0.037651404030618],
+ [0.35031676209499907, 0.7264811939316552, -0.06538442294808501],
+ [-0.09098281098284752, -0.3127282905230739, 1.5227665613052603]
+];
+var ConetoIab_M = [
+ [0.5, 0.5, 0],
+ [3.524, -4.066708, 0.542708],
+ [0.199076, 1.096799, -1.295875]
+];
+var IabtoCone_M = [
+ [1, 0.1386050432715393, 0.05804731615611886],
+ [0.9999999999999999, -0.1386050432715393, -0.05804731615611886],
+ [0.9999999999999998, -0.09601924202631895, -0.8118918960560388]
+];
+var Jzazbz = new ColorSpace({
+ id: "jzazbz",
+ name: "Jzazbz",
+ coords: {
+ jz: {
+ refRange: [0, 1],
+ name: "Jz"
+ },
+ az: {
+ refRange: [-0.5, 0.5]
+ },
+ bz: {
+ refRange: [-0.5, 0.5]
+ }
+ },
+ base: XYZ_Abs_D65,
+ fromBase(XYZ) {
+ let [Xa, Ya, Za] = XYZ;
+ let Xm = b$1 * Xa - (b$1 - 1) * Za;
+ let Ym = g * Ya - (g - 1) * Xa;
+ let LMS = multiplyMatrices(XYZtoCone_M, [Xm, Ym, Za]);
+ let PQLMS = LMS.map(function(val) {
+ let num = c1$2 + c2$2 * (val / 1e4) ** n$1;
+ let denom = 1 + c3$2 * (val / 1e4) ** n$1;
+ return (num / denom) ** p;
+ });
+ let [Iz, az, bz] = multiplyMatrices(ConetoIab_M, PQLMS);
+ let Jz = (1 + d) * Iz / (1 + d * Iz) - d0;
+ return [Jz, az, bz];
+ },
+ toBase(Jzazbz2) {
+ let [Jz, az, bz] = Jzazbz2;
+ let Iz = (Jz + d0) / (1 + d - d * (Jz + d0));
+ let PQLMS = multiplyMatrices(IabtoCone_M, [Iz, az, bz]);
+ let LMS = PQLMS.map(function(val) {
+ let num = c1$2 - val ** pinv;
+ let denom = c3$2 * val ** pinv - c2$2;
+ let x = 1e4 * (num / denom) ** ninv$1;
+ return x;
+ });
+ let [Xm, Ym, Za] = multiplyMatrices(ConetoXYZ_M, LMS);
+ let Xa = (Xm + (b$1 - 1) * Za) / b$1;
+ let Ya = (Ym + (g - 1) * Xa) / g;
+ return [Xa, Ya, Za];
+ },
+ formats: {
+ // https://drafts.csswg.org/css-color-hdr/#Jzazbz
+ "color": {}
+ }
+});
+var jzczhz = new ColorSpace({
+ id: "jzczhz",
+ name: "JzCzHz",
+ coords: {
+ jz: {
+ refRange: [0, 1],
+ name: "Jz"
+ },
+ cz: {
+ refRange: [0, 1],
+ name: "Chroma"
+ },
+ hz: {
+ refRange: [0, 360],
+ type: "angle",
+ name: "Hue"
+ }
+ },
+ base: Jzazbz,
+ fromBase(jzazbz) {
+ let [Jz, az, bz] = jzazbz;
+ let hue;
+ const \u03B52 = 2e-4;
+ if (Math.abs(az) < \u03B52 && Math.abs(bz) < \u03B52) {
+ hue = NaN;
+ } else {
+ hue = Math.atan2(bz, az) * 180 / Math.PI;
+ }
+ return [
+ Jz,
+ // Jz is still Jz
+ Math.sqrt(az ** 2 + bz ** 2),
+ // Chroma
+ constrain(hue)
+ // Hue, in degrees [0 to 360)
+ ];
+ },
+ toBase(jzczhz2) {
+ return [
+ jzczhz2[0],
+ // Jz is still Jz
+ jzczhz2[1] * Math.cos(jzczhz2[2] * Math.PI / 180),
+ // az
+ jzczhz2[1] * Math.sin(jzczhz2[2] * Math.PI / 180)
+ // bz
+ ];
+ },
+ formats: {
+ color: {}
+ }
+});
+function deltaEJz(color, sample) {
+ let [Jz1, Cz1, Hz1] = jzczhz.from(color);
+ let [Jz2, Cz2, Hz2] = jzczhz.from(sample);
+ let \u0394J = Jz1 - Jz2;
+ let \u0394C = Cz1 - Cz2;
+ if (Number.isNaN(Hz1) && Number.isNaN(Hz2)) {
+ Hz1 = 0;
+ Hz2 = 0;
+ } else if (Number.isNaN(Hz1)) {
+ Hz1 = Hz2;
+ } else if (Number.isNaN(Hz2)) {
+ Hz2 = Hz1;
+ }
+ let \u0394h = Hz1 - Hz2;
+ let \u0394H = 2 * Math.sqrt(Cz1 * Cz2) * Math.sin(\u0394h / 2 * (Math.PI / 180));
+ return Math.sqrt(\u0394J ** 2 + \u0394C ** 2 + \u0394H ** 2);
+}
+var c1$1 = 3424 / 4096;
+var c2$1 = 2413 / 128;
+var c3$1 = 2392 / 128;
+var m1 = 2610 / 16384;
+var m2 = 2523 / 32;
+var im1 = 16384 / 2610;
+var im2 = 32 / 2523;
+var XYZtoLMS_M$1 = [
+ [0.3592, 0.6976, -0.0358],
+ [-0.1922, 1.1004, 0.0755],
+ [7e-3, 0.0749, 0.8434]
+];
+var LMStoIPT_M = [
+ [2048 / 4096, 2048 / 4096, 0],
+ [6610 / 4096, -13613 / 4096, 7003 / 4096],
+ [17933 / 4096, -17390 / 4096, -543 / 4096]
+];
+var IPTtoLMS_M = [
+ [0.9999888965628402, 0.008605050147287059, 0.11103437159861648],
+ [1.00001110343716, -0.008605050147287059, -0.11103437159861648],
+ [1.0000320633910054, 0.56004913547279, -0.3206339100541203]
+];
+var LMStoXYZ_M$1 = [
+ [2.0701800566956137, -1.326456876103021, 0.20661600684785517],
+ [0.3649882500326575, 0.6804673628522352, -0.04542175307585323],
+ [-0.04959554223893211, -0.04942116118675749, 1.1879959417328034]
+];
+var ictcp = new ColorSpace({
+ id: "ictcp",
+ name: "ICTCP",
+ // From BT.2100-2 page 7:
+ // During production, signal values are expected to exceed the
+ // range E′ = [0.0 : 1.0]. This provides processing headroom and avoids
+ // signal degradation during cascaded processing. Such values of E′,
+ // below 0.0 or exceeding 1.0, should not be clipped during production
+ // and exchange.
+ // Values below 0.0 should not be clipped in reference displays (even
+ // though they represent “negative” light) to allow the black level of
+ // the signal (LB) to be properly set using test signals known as “PLUGE”
+ coords: {
+ i: {
+ refRange: [0, 1],
+ // Constant luminance,
+ name: "I"
+ },
+ ct: {
+ refRange: [-0.5, 0.5],
+ // Full BT.2020 gamut in range [-0.5, 0.5]
+ name: "CT"
+ },
+ cp: {
+ refRange: [-0.5, 0.5],
+ name: "CP"
+ }
+ },
+ base: XYZ_Abs_D65,
+ fromBase(XYZ) {
+ let LMS = multiplyMatrices(XYZtoLMS_M$1, XYZ);
+ return LMStoICtCp(LMS);
+ },
+ toBase(ICtCp) {
+ let LMS = ICtCptoLMS(ICtCp);
+ return multiplyMatrices(LMStoXYZ_M$1, LMS);
+ },
+ formats: {
+ color: {}
+ }
+});
+function LMStoICtCp(LMS) {
+ let PQLMS = LMS.map(function(val) {
+ let num = c1$1 + c2$1 * (val / 1e4) ** m1;
+ let denom = 1 + c3$1 * (val / 1e4) ** m1;
+ return (num / denom) ** m2;
+ });
+ return multiplyMatrices(LMStoIPT_M, PQLMS);
+}
+function ICtCptoLMS(ICtCp) {
+ let PQLMS = multiplyMatrices(IPTtoLMS_M, ICtCp);
+ let LMS = PQLMS.map(function(val) {
+ let num = Math.max(val ** im2 - c1$1, 0);
+ let denom = c2$1 - c3$1 * val ** im2;
+ return 1e4 * (num / denom) ** im1;
+ });
+ return LMS;
+}
+function deltaEITP(color, sample) {
+ let [I1, T1, P1] = ictcp.from(color);
+ let [I2, T2, P2] = ictcp.from(sample);
+ return 720 * Math.sqrt((I1 - I2) ** 2 + 0.25 * (T1 - T2) ** 2 + (P1 - P2) ** 2);
+}
+var XYZtoLMS_M = [
+ [0.8190224432164319, 0.3619062562801221, -0.12887378261216414],
+ [0.0329836671980271, 0.9292868468965546, 0.03614466816999844],
+ [0.048177199566046255, 0.26423952494422764, 0.6335478258136937]
+];
+var LMStoXYZ_M = [
+ [1.2268798733741557, -0.5578149965554813, 0.28139105017721583],
+ [-0.04057576262431372, 1.1122868293970594, -0.07171106666151701],
+ [-0.07637294974672142, -0.4214933239627914, 1.5869240244272418]
+];
+var LMStoLab_M = [
+ [0.2104542553, 0.793617785, -0.0040720468],
+ [1.9779984951, -2.428592205, 0.4505937099],
+ [0.0259040371, 0.7827717662, -0.808675766]
+];
+var LabtoLMS_M = [
+ [0.9999999984505198, 0.39633779217376786, 0.2158037580607588],
+ [1.0000000088817609, -0.10556134232365635, -0.06385417477170591],
+ [1.0000000546724108, -0.08948418209496575, -1.2914855378640917]
+];
+var OKLab = new ColorSpace({
+ id: "oklab",
+ name: "Oklab",
+ coords: {
+ l: {
+ refRange: [0, 1],
+ name: "L"
+ },
+ a: {
+ refRange: [-0.4, 0.4]
+ },
+ b: {
+ refRange: [-0.4, 0.4]
+ }
+ },
+ // Note that XYZ is relative to D65
+ white: "D65",
+ base: XYZ_D65,
+ fromBase(XYZ) {
+ let LMS = multiplyMatrices(XYZtoLMS_M, XYZ);
+ let LMSg = LMS.map((val) => Math.cbrt(val));
+ return multiplyMatrices(LMStoLab_M, LMSg);
+ },
+ toBase(OKLab2) {
+ let LMSg = multiplyMatrices(LabtoLMS_M, OKLab2);
+ let LMS = LMSg.map((val) => val ** 3);
+ return multiplyMatrices(LMStoXYZ_M, LMS);
+ },
+ formats: {
+ "oklab": {
+ coords: [" | ", " | [-1,1]", " | [-1,1]"]
+ }
+ }
+});
+function deltaEOK(color, sample) {
+ let [L1, a1, b1] = OKLab.from(color);
+ let [L2, a2, b2] = OKLab.from(sample);
+ let \u0394L = L1 - L2;
+ let \u0394a = a1 - a2;
+ let \u0394b = b1 - b2;
+ return Math.sqrt(\u0394L ** 2 + \u0394a ** 2 + \u0394b ** 2);
+}
+var deltaEMethods = {
+ deltaE76,
+ deltaECMC,
+ deltaE2000,
+ deltaEJz,
+ deltaEITP,
+ deltaEOK
+};
+function deltaE(c12, c22, o = {}) {
+ if (isString(o)) {
+ o = { method: o };
+ }
+ let { method = defaults.deltaE, ...rest } = o;
+ c12 = getColor(c12);
+ c22 = getColor(c22);
+ for (let m3 in deltaEMethods) {
+ if ("deltae" + method.toLowerCase() === m3.toLowerCase()) {
+ return deltaEMethods[m3](c12, c22, rest);
+ }
+ }
+ throw new TypeError(`Unknown deltaE method: ${method}`);
+}
+function lighten(color, amount = 0.25) {
+ let space = ColorSpace.get("oklch", "lch");
+ let lightness = [space, "l"];
+ return set(color, lightness, (l) => l * (1 + amount));
+}
+function darken(color, amount = 0.25) {
+ let space = ColorSpace.get("oklch", "lch");
+ let lightness = [space, "l"];
+ return set(color, lightness, (l) => l * (1 - amount));
+}
+var variations = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ darken,
+ lighten
+});
+function mix(c12, c22, p2 = 0.5, o = {}) {
+ [c12, c22] = [getColor(c12), getColor(c22)];
+ if (type(p2) === "object") {
+ [p2, o] = [0.5, p2];
+ }
+ let { space, outputSpace, premultiplied } = o;
+ let r = range(c12, c22, { space, outputSpace, premultiplied });
+ return r(p2);
+}
+function steps(c12, c22, options = {}) {
+ let colorRange;
+ if (isRange(c12)) {
+ [colorRange, options] = [c12, c22];
+ [c12, c22] = colorRange.rangeArgs.colors;
+ }
+ let {
+ maxDeltaE,
+ deltaEMethod,
+ steps: steps2 = 2,
+ maxSteps = 1e3,
+ ...rangeOptions
+ } = options;
+ if (!colorRange) {
+ [c12, c22] = [getColor(c12), getColor(c22)];
+ colorRange = range(c12, c22, rangeOptions);
+ }
+ let totalDelta = deltaE(c12, c22);
+ let actualSteps = maxDeltaE > 0 ? Math.max(steps2, Math.ceil(totalDelta / maxDeltaE) + 1) : steps2;
+ let ret = [];
+ if (maxSteps !== void 0) {
+ actualSteps = Math.min(actualSteps, maxSteps);
+ }
+ if (actualSteps === 1) {
+ ret = [{ p: 0.5, color: colorRange(0.5) }];
+ } else {
+ let step = 1 / (actualSteps - 1);
+ ret = Array.from({ length: actualSteps }, (_, i) => {
+ let p2 = i * step;
+ return { p: p2, color: colorRange(p2) };
+ });
+ }
+ if (maxDeltaE > 0) {
+ let maxDelta = ret.reduce((acc, cur, i) => {
+ if (i === 0) {
+ return 0;
+ }
+ let \u0394\u0395 = deltaE(cur.color, ret[i - 1].color, deltaEMethod);
+ return Math.max(acc, \u0394\u0395);
+ }, 0);
+ while (maxDelta > maxDeltaE) {
+ maxDelta = 0;
+ for (let i = 1; i < ret.length && ret.length < maxSteps; i++) {
+ let prev = ret[i - 1];
+ let cur = ret[i];
+ let p2 = (cur.p + prev.p) / 2;
+ let color = colorRange(p2);
+ maxDelta = Math.max(maxDelta, deltaE(color, prev.color), deltaE(color, cur.color));
+ ret.splice(i, 0, { p: p2, color: colorRange(p2) });
+ i++;
+ }
+ }
+ }
+ ret = ret.map((a2) => a2.color);
+ return ret;
+}
+function range(color1, color2, options = {}) {
+ if (isRange(color1)) {
+ let [r, options2] = [color1, color2];
+ return range(...r.rangeArgs.colors, { ...r.rangeArgs.options, ...options2 });
+ }
+ let { space, outputSpace, progression, premultiplied } = options;
+ color1 = getColor(color1);
+ color2 = getColor(color2);
+ color1 = clone(color1);
+ color2 = clone(color2);
+ let rangeArgs = { colors: [color1, color2], options };
+ if (space) {
+ space = ColorSpace.get(space);
+ } else {
+ space = ColorSpace.registry[defaults.interpolationSpace] || color1.space;
+ }
+ outputSpace = outputSpace ? ColorSpace.get(outputSpace) : space;
+ color1 = to(color1, space);
+ color2 = to(color2, space);
+ color1 = toGamut(color1);
+ color2 = toGamut(color2);
+ if (space.coords.h && space.coords.h.type === "angle") {
+ let arc = options.hue = options.hue || "shorter";
+ let hue = [space, "h"];
+ let [\u03B81, \u03B82] = [get(color1, hue), get(color2, hue)];
+ [\u03B81, \u03B82] = adjust(arc, [\u03B81, \u03B82]);
+ set(color1, hue, \u03B81);
+ set(color2, hue, \u03B82);
+ }
+ if (premultiplied) {
+ color1.coords = color1.coords.map((c4) => c4 * color1.alpha);
+ color2.coords = color2.coords.map((c4) => c4 * color2.alpha);
+ }
+ return Object.assign((p2) => {
+ p2 = progression ? progression(p2) : p2;
+ let coords = color1.coords.map((start, i) => {
+ let end = color2.coords[i];
+ return interpolate(start, end, p2);
+ });
+ let alpha = interpolate(color1.alpha, color2.alpha, p2);
+ let ret = { space, coords, alpha };
+ if (premultiplied) {
+ ret.coords = ret.coords.map((c4) => c4 / alpha);
+ }
+ if (outputSpace !== space) {
+ ret = to(ret, outputSpace);
+ }
+ return ret;
+ }, {
+ rangeArgs
+ });
+}
+function isRange(val) {
+ return type(val) === "function" && !!val.rangeArgs;
+}
+defaults.interpolationSpace = "lab";
+function register(Color2) {
+ Color2.defineFunction("mix", mix, { returns: "color" });
+ Color2.defineFunction("range", range, { returns: "function" });
+ Color2.defineFunction("steps", steps, { returns: "array" });
+}
+var interpolation = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ isRange,
+ mix,
+ range,
+ register,
+ steps
+});
+var HSL = new ColorSpace({
+ id: "hsl",
+ name: "HSL",
+ coords: {
+ h: {
+ refRange: [0, 360],
+ type: "angle",
+ name: "Hue"
+ },
+ s: {
+ range: [0, 100],
+ name: "Saturation"
+ },
+ l: {
+ range: [0, 100],
+ name: "Lightness"
+ }
+ },
+ base: sRGB,
+ // Adapted from https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
+ fromBase: (rgb) => {
+ let max2 = Math.max(...rgb);
+ let min = Math.min(...rgb);
+ let [r, g2, b2] = rgb;
+ let [h, s, l] = [NaN, 0, (min + max2) / 2];
+ let d2 = max2 - min;
+ if (d2 !== 0) {
+ s = l === 0 || l === 1 ? 0 : (max2 - l) / Math.min(l, 1 - l);
+ switch (max2) {
+ case r:
+ h = (g2 - b2) / d2 + (g2 < b2 ? 6 : 0);
+ break;
+ case g2:
+ h = (b2 - r) / d2 + 2;
+ break;
+ case b2:
+ h = (r - g2) / d2 + 4;
+ }
+ h = h * 60;
+ }
+ return [h, s * 100, l * 100];
+ },
+ // Adapted from https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative
+ toBase: (hsl) => {
+ let [h, s, l] = hsl;
+ h = h % 360;
+ if (h < 0) {
+ h += 360;
+ }
+ s /= 100;
+ l /= 100;
+ function f(n2) {
+ let k = (n2 + h / 30) % 12;
+ let a2 = s * Math.min(l, 1 - l);
+ return l - a2 * Math.max(-1, Math.min(k - 3, 9 - k, 1));
+ }
+ return [f(0), f(8), f(4)];
+ },
+ formats: {
+ "hsl": {
+ toGamut: true,
+ coords: [" | ", "", ""]
+ },
+ "hsla": {
+ coords: [" | ", "", ""],
+ commas: true,
+ lastAlpha: true
+ }
+ }
+});
+var HSV = new ColorSpace({
+ id: "hsv",
+ name: "HSV",
+ coords: {
+ h: {
+ refRange: [0, 360],
+ type: "angle",
+ name: "Hue"
+ },
+ s: {
+ range: [0, 100],
+ name: "Saturation"
+ },
+ v: {
+ range: [0, 100],
+ name: "Value"
+ }
+ },
+ base: HSL,
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#Interconversion
+ fromBase(hsl) {
+ let [h, s, l] = hsl;
+ s /= 100;
+ l /= 100;
+ let v = l + s * Math.min(l, 1 - l);
+ return [
+ h,
+ // h is the same
+ v === 0 ? 0 : 200 * (1 - l / v),
+ // s
+ 100 * v
+ ];
+ },
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#Interconversion
+ toBase(hsv) {
+ let [h, s, v] = hsv;
+ s /= 100;
+ v /= 100;
+ let l = v * (1 - s / 2);
+ return [
+ h,
+ // h is the same
+ l === 0 || l === 1 ? 0 : (v - l) / Math.min(l, 1 - l) * 100,
+ l * 100
+ ];
+ },
+ formats: {
+ color: {
+ toGamut: true
+ }
+ }
+});
+var hwb = new ColorSpace({
+ id: "hwb",
+ name: "HWB",
+ coords: {
+ h: {
+ refRange: [0, 360],
+ type: "angle",
+ name: "Hue"
+ },
+ w: {
+ range: [0, 100],
+ name: "Whiteness"
+ },
+ b: {
+ range: [0, 100],
+ name: "Blackness"
+ }
+ },
+ base: HSV,
+ fromBase(hsv) {
+ let [h, s, v] = hsv;
+ return [h, v * (100 - s) / 100, 100 - v];
+ },
+ toBase(hwb2) {
+ let [h, w, b2] = hwb2;
+ w /= 100;
+ b2 /= 100;
+ let sum = w + b2;
+ if (sum >= 1) {
+ let gray = w / sum;
+ return [h, 0, gray * 100];
+ }
+ let v = 1 - b2;
+ let s = v === 0 ? 0 : 1 - w / v;
+ return [h, s * 100, v * 100];
+ },
+ formats: {
+ "hwb": {
+ toGamut: true,
+ coords: [" | ", "", ""]
+ }
+ }
+});
+var toXYZ_M$2 = [
+ [0.5766690429101305, 0.1855582379065463, 0.1882286462349947],
+ [0.29734497525053605, 0.6273635662554661, 0.07529145849399788],
+ [0.02703136138641234, 0.07068885253582723, 0.9913375368376388]
+];
+var fromXYZ_M$2 = [
+ [2.0415879038107465, -0.5650069742788596, -0.34473135077832956],
+ [-0.9692436362808795, 1.8759675015077202, 0.04155505740717557],
+ [0.013444280632031142, -0.11836239223101838, 1.0151749943912054]
+];
+var A98Linear = new RGBColorSpace({
+ id: "a98rgb-linear",
+ name: "Linear Adobe\xAE 98 RGB compatible",
+ white: "D65",
+ toXYZ_M: toXYZ_M$2,
+ fromXYZ_M: fromXYZ_M$2
+});
+var a98rgb = new RGBColorSpace({
+ id: "a98rgb",
+ name: "Adobe\xAE 98 RGB compatible",
+ base: A98Linear,
+ toBase: (RGB) => RGB.map((val) => Math.pow(Math.abs(val), 563 / 256) * Math.sign(val)),
+ fromBase: (RGB) => RGB.map((val) => Math.pow(Math.abs(val), 256 / 563) * Math.sign(val)),
+ formats: {
+ color: {
+ id: "a98-rgb"
+ }
+ }
+});
+var toXYZ_M$1 = [
+ [0.7977604896723027, 0.13518583717574031, 0.0313493495815248],
+ [0.2880711282292934, 0.7118432178101014, 8565396060525902e-20],
+ [0, 0, 0.8251046025104601]
+];
+var fromXYZ_M$1 = [
+ [1.3457989731028281, -0.25558010007997534, -0.05110628506753401],
+ [-0.5446224939028347, 1.5082327413132781, 0.02053603239147973],
+ [0, 0, 1.2119675456389454]
+];
+var ProPhotoLinear = new RGBColorSpace({
+ id: "prophoto-linear",
+ name: "Linear ProPhoto",
+ white: "D50",
+ base: XYZ_D50,
+ toXYZ_M: toXYZ_M$1,
+ fromXYZ_M: fromXYZ_M$1
+});
+var Et = 1 / 512;
+var Et2 = 16 / 512;
+var prophoto = new RGBColorSpace({
+ id: "prophoto",
+ name: "ProPhoto",
+ base: ProPhotoLinear,
+ toBase(RGB) {
+ return RGB.map((v) => v < Et2 ? v / 16 : v ** 1.8);
+ },
+ fromBase(RGB) {
+ return RGB.map((v) => v >= Et ? v ** (1 / 1.8) : 16 * v);
+ },
+ formats: {
+ color: {
+ id: "prophoto-rgb"
+ }
+ }
+});
+var oklch = new ColorSpace({
+ id: "oklch",
+ name: "Oklch",
+ coords: {
+ l: {
+ refRange: [0, 1],
+ name: "Lightness"
+ },
+ c: {
+ refRange: [0, 0.4],
+ name: "Chroma"
+ },
+ h: {
+ refRange: [0, 360],
+ type: "angle",
+ name: "Hue"
+ }
+ },
+ white: "D65",
+ base: OKLab,
+ fromBase(oklab) {
+ let [L, a2, b2] = oklab;
+ let h;
+ const \u03B52 = 2e-4;
+ if (Math.abs(a2) < \u03B52 && Math.abs(b2) < \u03B52) {
+ h = NaN;
+ } else {
+ h = Math.atan2(b2, a2) * 180 / Math.PI;
+ }
+ return [
+ L,
+ // OKLab L is still L
+ Math.sqrt(a2 ** 2 + b2 ** 2),
+ // Chroma
+ constrain(h)
+ // Hue, in degrees [0 to 360)
+ ];
+ },
+ // Convert from polar form
+ toBase(oklch2) {
+ let [L, C, h] = oklch2;
+ let a2, b2;
+ if (isNaN(h)) {
+ a2 = 0;
+ b2 = 0;
+ } else {
+ a2 = C * Math.cos(h * Math.PI / 180);
+ b2 = C * Math.sin(h * Math.PI / 180);
+ }
+ return [L, a2, b2];
+ },
+ formats: {
+ "oklch": {
+ coords: [" | ", " | [0,1]", " | "]
+ }
+ }
+});
+var Yw = 203;
+var n = 2610 / 2 ** 14;
+var ninv = 2 ** 14 / 2610;
+var m = 2523 / 2 ** 5;
+var minv = 2 ** 5 / 2523;
+var c1 = 3424 / 2 ** 12;
+var c2 = 2413 / 2 ** 7;
+var c3 = 2392 / 2 ** 7;
+var rec2100Pq = new RGBColorSpace({
+ id: "rec2100pq",
+ name: "REC.2100-PQ",
+ base: REC2020Linear,
+ toBase(RGB) {
+ return RGB.map(function(val) {
+ let x = (Math.max(val ** minv - c1, 0) / (c2 - c3 * val ** minv)) ** ninv;
+ return x * 1e4 / Yw;
+ });
+ },
+ fromBase(RGB) {
+ return RGB.map(function(val) {
+ let x = Math.max(val * Yw / 1e4, 0);
+ let num = c1 + c2 * x ** n;
+ let denom = 1 + c3 * x ** n;
+ return (num / denom) ** m;
+ });
+ },
+ formats: {
+ color: {
+ id: "rec2100-pq"
+ }
+ }
+});
+var a = 0.17883277;
+var b = 0.28466892;
+var c = 0.55991073;
+var scale = 3.7743;
+var rec2100Hlg = new RGBColorSpace({
+ id: "rec2100hlg",
+ cssid: "rec2100-hlg",
+ name: "REC.2100-HLG",
+ referred: "scene",
+ base: REC2020Linear,
+ toBase(RGB) {
+ return RGB.map(function(val) {
+ if (val <= 0.5) {
+ return val ** 2 / 3 * scale;
+ }
+ return (Math.exp((val - c) / a) + b) / 12 * scale;
+ });
+ },
+ fromBase(RGB) {
+ return RGB.map(function(val) {
+ val /= scale;
+ if (val <= 1 / 12) {
+ return Math.sqrt(3 * val);
+ }
+ return a * Math.log(12 * val - b) + c;
+ });
+ },
+ formats: {
+ color: {
+ id: "rec2100-hlg"
+ }
+ }
+});
+var CATs = {};
+hooks.add("chromatic-adaptation-start", (env) => {
+ if (env.options.method) {
+ env.M = adapt(env.W1, env.W2, env.options.method);
+ }
+});
+hooks.add("chromatic-adaptation-end", (env) => {
+ if (!env.M) {
+ env.M = adapt(env.W1, env.W2, env.options.method);
+ }
+});
+function defineCAT({ id, toCone_M, fromCone_M }) {
+ CATs[id] = arguments[0];
+}
+function adapt(W1, W2, id = "Bradford") {
+ let method = CATs[id];
+ let [\u03C1s, \u03B3s, \u03B2s] = multiplyMatrices(method.toCone_M, W1);
+ let [\u03C1d, \u03B3d, \u03B2d] = multiplyMatrices(method.toCone_M, W2);
+ let scale2 = [
+ [\u03C1d / \u03C1s, 0, 0],
+ [0, \u03B3d / \u03B3s, 0],
+ [0, 0, \u03B2d / \u03B2s]
+ ];
+ let scaled_cone_M = multiplyMatrices(scale2, method.toCone_M);
+ let adapt_M = multiplyMatrices(method.fromCone_M, scaled_cone_M);
+ return adapt_M;
+}
+defineCAT({
+ id: "von Kries",
+ toCone_M: [
+ [0.40024, 0.7076, -0.08081],
+ [-0.2263, 1.16532, 0.0457],
+ [0, 0, 0.91822]
+ ],
+ fromCone_M: [
+ [1.8599364, -1.1293816, 0.2198974],
+ [0.3611914, 0.6388125, -64e-7],
+ [0, 0, 1.0890636]
+ ]
+});
+defineCAT({
+ id: "Bradford",
+ // Convert an array of XYZ values in the range 0.0 - 1.0
+ // to cone fundamentals
+ toCone_M: [
+ [0.8951, 0.2664, -0.1614],
+ [-0.7502, 1.7135, 0.0367],
+ [0.0389, -0.0685, 1.0296]
+ ],
+ // and back
+ fromCone_M: [
+ [0.9869929, -0.1470543, 0.1599627],
+ [0.4323053, 0.5183603, 0.0492912],
+ [-85287e-7, 0.0400428, 0.9684867]
+ ]
+});
+defineCAT({
+ id: "CAT02",
+ // with complete chromatic adaptation to W2, so D = 1.0
+ toCone_M: [
+ [0.7328, 0.4296, -0.1624],
+ [-0.7036, 1.6975, 61e-4],
+ [3e-3, 0.0136, 0.9834]
+ ],
+ fromCone_M: [
+ [1.0961238, -0.278869, 0.1827452],
+ [0.454369, 0.4735332, 0.0720978],
+ [-96276e-7, -5698e-6, 1.0153256]
+ ]
+});
+defineCAT({
+ id: "CAT16",
+ toCone_M: [
+ [0.401288, 0.650173, -0.051461],
+ [-0.250268, 1.204414, 0.045854],
+ [-2079e-6, 0.048952, 0.953127]
+ ],
+ // the extra precision is needed to avoid roundtripping errors
+ fromCone_M: [
+ [1.862067855087233, -1.011254630531685, 0.1491867754444518],
+ [0.3875265432361372, 0.6214474419314753, -0.008973985167612518],
+ [-0.01584149884933386, -0.03412293802851557, 1.04996443687785]
+ ]
+});
+Object.assign(WHITES, {
+ // whitepoint values from ASTM E308-01 with 10nm spacing, 1931 2 degree observer
+ // all normalized to Y (luminance) = 1.00000
+ // Illuminant A is a tungsten electric light, giving a very warm, orange light.
+ A: [1.0985, 1, 0.35585],
+ // Illuminant C was an early approximation to daylight: illuminant A with a blue filter.
+ C: [0.98074, 1, 1.18232],
+ // The daylight series of illuminants simulate natural daylight.
+ // The color temperature (in degrees Kelvin/100) ranges from
+ // cool, overcast daylight (D50) to bright, direct sunlight (D65).
+ D55: [0.95682, 1, 0.92149],
+ D75: [0.94972, 1, 1.22638],
+ // Equal-energy illuminant, used in two-stage CAT16
+ E: [1, 1, 1],
+ // The F series of illuminants represent fluorescent lights
+ F2: [0.99186, 1, 0.67393],
+ F7: [0.95041, 1, 1.08747],
+ F11: [1.00962, 1, 0.6435]
+});
+WHITES.ACES = [0.32168 / 0.33767, 1, (1 - 0.32168 - 0.33767) / 0.33767];
+var toXYZ_M = [
+ [0.6624541811085053, 0.13400420645643313, 0.1561876870049078],
+ [0.27222871678091454, 0.6740817658111484, 0.05368951740793705],
+ [-0.005574649490394108, 0.004060733528982826, 1.0103391003129971]
+];
+var fromXYZ_M = [
+ [1.6410233796943257, -0.32480329418479, -0.23642469523761225],
+ [-0.6636628587229829, 1.6153315916573379, 0.016756347685530137],
+ [0.011721894328375376, -0.008284441996237409, 0.9883948585390215]
+];
+var ACEScg = new RGBColorSpace({
+ id: "acescg",
+ name: "ACEScg",
+ // ACEScg – A scene-referred, linear-light encoding of ACES Data
+ // https://docs.acescentral.com/specifications/acescg/
+ // uses the AP1 primaries, see section 4.3.1 Color primaries
+ coords: {
+ r: {
+ range: [0, 65504],
+ name: "Red"
+ },
+ g: {
+ range: [0, 65504],
+ name: "Green"
+ },
+ b: {
+ range: [0, 65504],
+ name: "Blue"
+ }
+ },
+ referred: "scene",
+ white: WHITES.ACES,
+ toXYZ_M,
+ fromXYZ_M,
+ formats: {
+ color: {}
+ }
+});
+var \u03B5 = 2 ** -16;
+var ACES_min_nonzero = -0.35828683;
+var ACES_cc_max = (Math.log2(65504) + 9.72) / 17.52;
+var acescc = new RGBColorSpace({
+ id: "acescc",
+ name: "ACEScc",
+ // see S-2014-003 ACEScc – A Logarithmic Encoding of ACES Data
+ // https://docs.acescentral.com/specifications/acescc/
+ // uses the AP1 primaries, see section 4.3.1 Color primaries
+ // Appendix A: "Very small ACES scene referred values below 7 1/4 stops
+ // below 18% middle gray are encoded as negative ACEScc values.
+ // These values should be preserved per the encoding in Section 4.4
+ // so that all positive ACES values are maintained."
+ coords: {
+ r: {
+ range: [ACES_min_nonzero, ACES_cc_max],
+ name: "Red"
+ },
+ g: {
+ range: [ACES_min_nonzero, ACES_cc_max],
+ name: "Green"
+ },
+ b: {
+ range: [ACES_min_nonzero, ACES_cc_max],
+ name: "Blue"
+ }
+ },
+ referred: "scene",
+ base: ACEScg,
+ // from section 4.4.2 Decoding Function
+ toBase(RGB) {
+ const low = (9.72 - 15) / 17.52;
+ return RGB.map(function(val) {
+ if (val <= low) {
+ return (2 ** (val * 17.52 - 9.72) - \u03B5) * 2;
+ } else if (val < ACES_cc_max) {
+ return 2 ** (val * 17.52 - 9.72);
+ } else {
+ return 65504;
+ }
+ });
+ },
+ // Non-linear encoding function from S-2014-003, section 4.4.1 Encoding Function
+ fromBase(RGB) {
+ return RGB.map(function(val) {
+ if (val <= 0) {
+ return (Math.log2(\u03B5) + 9.72) / 17.52;
+ } else if (val < \u03B5) {
+ return (Math.log2(\u03B5 + val * 0.5) + 9.72) / 17.52;
+ } else {
+ return (Math.log2(val) + 9.72) / 17.52;
+ }
+ });
+ },
+ // encoded media white (rgb 1,1,1) => linear [ 222.861, 222.861, 222.861 ]
+ // encoded media black (rgb 0,0,0) => linear [ 0.0011857, 0.0011857, 0.0011857]
+ formats: {
+ color: {}
+ }
+});
+var spaces = /* @__PURE__ */ Object.freeze({
+ __proto__: null,
+ A98RGB: a98rgb,
+ A98RGB_Linear: A98Linear,
+ ACEScc: acescc,
+ ACEScg,
+ HSL,
+ HSV,
+ HWB: hwb,
+ ICTCP: ictcp,
+ JzCzHz: jzczhz,
+ Jzazbz,
+ LCH: lch,
+ Lab: lab,
+ Lab_D65: lab_d65,
+ OKLCH: oklch,
+ OKLab,
+ P3,
+ P3_Linear: P3Linear,
+ ProPhoto: prophoto,
+ ProPhoto_Linear: ProPhotoLinear,
+ REC_2020: REC2020,
+ REC_2020_Linear: REC2020Linear,
+ REC_2100_HLG: rec2100Hlg,
+ REC_2100_PQ: rec2100Pq,
+ XYZ_ABS_D65: XYZ_Abs_D65,
+ XYZ_D50,
+ XYZ_D65,
+ sRGB,
+ sRGB_Linear: sRGBLinear
+});
+var Color = class {
+ /**
+ * Creates an instance of Color.
+ * Signatures:
+ * - `new Color(stringToParse)`
+ * - `new Color(otherColor)`
+ * - `new Color({space, coords, alpha})`
+ * - `new Color(space, coords, alpha)`
+ * - `new Color(spaceId, coords, alpha)`
+ */
+ constructor(...args) {
+ let color;
+ if (args.length === 1) {
+ color = getColor(args[0]);
+ }
+ let space, coords, alpha;
+ if (color) {
+ space = color.space || color.spaceId;
+ coords = color.coords;
+ alpha = color.alpha;
+ } else {
+ [space, coords, alpha] = args;
+ }
+ Object.defineProperty(this, "space", {
+ value: ColorSpace.get(space),
+ writable: false,
+ enumerable: true,
+ configurable: true
+ // see note in https://262.ecma-international.org/8.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
+ });
+ this.coords = coords ? coords.slice() : [0, 0, 0];
+ this.alpha = alpha < 1 ? alpha : 1;
+ for (let i = 0; i < this.coords.length; i++) {
+ if (this.coords[i] === "NaN") {
+ this.coords[i] = NaN;
+ }
+ }
+ for (let id in this.space.coords) {
+ Object.defineProperty(this, id, {
+ get: () => this.get(id),
+ set: (value) => this.set(id, value)
+ });
+ }
+ }
+ get spaceId() {
+ return this.space.id;
+ }
+ clone() {
+ return new Color(this.space, this.coords, this.alpha);
+ }
+ toJSON() {
+ return {
+ spaceId: this.spaceId,
+ coords: this.coords,
+ alpha: this.alpha
+ };
+ }
+ display(...args) {
+ let ret = display(this, ...args);
+ ret.color = new Color(ret.color);
+ return ret;
+ }
+ /**
+ * Get a color from the argument passed
+ * Basically gets us the same result as new Color(color) but doesn't clone an existing color object
+ */
+ static get(color, ...args) {
+ if (color instanceof Color) {
+ return color;
+ }
+ return new Color(color, ...args);
+ }
+ static defineFunction(name, code, o = code) {
+ let { instance = true, returns } = o;
+ let func = function(...args) {
+ let ret = code(...args);
+ if (returns === "color") {
+ ret = Color.get(ret);
+ } else if (returns === "function") {
+ let f = ret;
+ ret = function(...args2) {
+ let ret2 = f(...args2);
+ return Color.get(ret2);
+ };
+ Object.assign(ret, f);
+ } else if (returns === "array") {
+ ret = ret.map((c4) => Color.get(c4));
+ }
+ return ret;
+ };
+ if (!(name in Color)) {
+ Color[name] = func;
+ }
+ if (instance) {
+ Color.prototype[name] = function(...args) {
+ return func(this, ...args);
+ };
+ }
+ }
+ static defineFunctions(o) {
+ for (let name in o) {
+ Color.defineFunction(name, o[name], o[name]);
+ }
+ }
+ static extend(exports) {
+ if (exports.register) {
+ exports.register(Color);
+ } else {
+ for (let name in exports) {
+ Color.defineFunction(name, exports[name]);
+ }
+ }
+ }
+};
+Color.defineFunctions({
+ get,
+ getAll,
+ set,
+ setAll,
+ to,
+ equals,
+ inGamut,
+ toGamut,
+ distance,
+ toString: serialize
+});
+Object.assign(Color, {
+ util,
+ hooks,
+ WHITES,
+ Space: ColorSpace,
+ spaces: ColorSpace.registry,
+ parse,
+ // Global defaults one may want to configure
+ defaults
+});
+for (let key of Object.keys(spaces)) {
+ ColorSpace.register(spaces[key]);
+}
+for (let id in ColorSpace.registry) {
+ addSpaceAccessors(id, ColorSpace.registry[id]);
+}
+hooks.add("colorspace-init-end", (space) => {
+ var _a;
+ addSpaceAccessors(space.id, space);
+ (_a = space.aliases) == null ? void 0 : _a.forEach((alias) => {
+ addSpaceAccessors(alias, space);
+ });
+});
+function addSpaceAccessors(id, space) {
+ Object.keys(space.coords);
+ Object.values(space.coords).map((c4) => c4.name);
+ let propId = id.replace(/-/g, "_");
+ Object.defineProperty(Color.prototype, propId, {
+ // Convert coords to coords in another colorspace and return them
+ // Source colorspace: this.spaceId
+ // Target colorspace: id
+ get() {
+ let ret = this.getAll(id);
+ if (typeof Proxy === "undefined") {
+ return ret;
+ }
+ return new Proxy(ret, {
+ has: (obj, property) => {
+ try {
+ ColorSpace.resolveCoord([space, property]);
+ return true;
+ } catch (e) {
+ }
+ return Reflect.has(obj, property);
+ },
+ get: (obj, property, receiver) => {
+ if (property && typeof property !== "symbol" && !(property in obj)) {
+ let { index } = ColorSpace.resolveCoord([space, property]);
+ if (index >= 0) {
+ return obj[index];
+ }
+ }
+ return Reflect.get(obj, property, receiver);
+ },
+ set: (obj, property, value, receiver) => {
+ if (property && typeof property !== "symbol" && !(property in obj) || property >= 0) {
+ let { index } = ColorSpace.resolveCoord([space, property]);
+ if (index >= 0) {
+ obj[index] = value;
+ this.setAll(id, obj);
+ return true;
+ }
+ }
+ return Reflect.set(obj, property, value, receiver);
+ }
+ });
+ },
+ // Convert coords in another colorspace to internal coords and set them
+ // Target colorspace: this.spaceId
+ // Source colorspace: id
+ set(coords) {
+ this.setAll(id, coords);
+ },
+ configurable: true,
+ enumerable: true
+ });
+}
+Color.extend(deltaEMethods);
+Color.extend({ deltaE });
+Object.assign(Color, { deltaEMethods });
+Color.extend(variations);
+Color.extend({ contrast });
+Color.extend(chromaticity);
+Color.extend(luminance);
+Color.extend(interpolation);
+Color.extend(contrastMethods);
+
+// src/ColorService.ts
+var ColorService = class {
+ constructor() {
+ this.darkenMemoization = /* @__PURE__ */ new Map();
+ }
+ generatePalettes(paletteConfig) {
+ if (paletteConfig.selected === "custom" /* CUSTOM */) {
+ const palette = paletteConfig.custom.split("-").filter(Boolean).map((str) => `#${str}`);
+ if (palette.length > 0) {
+ return {
+ light: this.processColorPalette({
+ isDarkTheme: false,
+ palette,
+ seed: paletteConfig.seed
+ }),
+ dark: this.processColorPalette({
+ isDarkTheme: true,
+ palette,
+ seed: paletteConfig.seed
+ })
+ };
+ }
+ }
+ const isBright = paletteConfig.selected === "adaptive-bright" /* ADAPTIVE_BRIGHT */;
+ const baseChroma = isBright ? 85 : 16;
+ const baseLightness = isBright ? 75 : 87;
+ const offset = 35;
+ const commonConfig = {
+ paletteSize: 8,
+ seed: paletteConfig.seed,
+ isShuffling: true,
+ baseChroma,
+ baseLightness,
+ constantOffset: offset
+ };
+ return {
+ light: this.generateAdaptiveColorPalette({
+ isDarkTheme: false,
+ ...commonConfig
+ }),
+ dark: this.generateAdaptiveColorPalette({
+ isDarkTheme: true,
+ ...commonConfig
+ })
+ };
+ }
+ getColors(tagName, palette, tagsMap, options, tagColorOverrides) {
+ const chunks = tagName.split("/");
+ const gradientStops = this.calculateGradientStops(
+ chunks,
+ palette,
+ tagsMap,
+ options.isMixing,
+ options.isTransition,
+ tagColorOverrides
+ );
+ const backgroundColor = new Color(gradientStops[0]);
+ const background = backgroundColor.toString({ format: "lch" });
+ const linearGradient = this.buildLinearGradient(
+ gradientStops,
+ options.isTransition
+ );
+ const color = options.highTextContrast ? this.calculateHighContrastColor(backgroundColor) : this.calculateDarkenedColor(backgroundColor);
+ return { background, color, linearGradient };
+ }
+ calculateGradientStops(chunks, palette, tagsMap, isMixing, isTransition, tagColorOverrides) {
+ const gradientStops = [];
+ let backgroundColor = null;
+ let lastColor = "";
+ let currentPath = "";
+ for (const chunk of chunks) {
+ const key = this.composeTagPath(currentPath, chunk);
+ const order = tagsMap.get(key) || 1;
+ const overrideIndex = tagColorOverrides == null ? void 0 : tagColorOverrides.get(key);
+ const paletteForChunk = this.getPaletteForChunk(
+ palette,
+ lastColor,
+ overrideIndex
+ );
+ const colorFromPalette = this.pickColorFromPalette(
+ paletteForChunk,
+ order,
+ overrideIndex
+ );
+ lastColor = colorFromPalette;
+ const newColor = this.buildColorForChunk(
+ backgroundColor,
+ colorFromPalette,
+ isMixing,
+ isTransition
+ );
+ if (!backgroundColor) {
+ backgroundColor = newColor;
+ }
+ gradientStops.push(newColor.toString({ format: "lch" }));
+ currentPath = key;
+ }
+ return gradientStops;
+ }
+ composeTagPath(parentPath, chunk) {
+ return parentPath ? `${parentPath}/${chunk}` : chunk;
+ }
+ getPaletteForChunk(palette, lastColor, overrideIndex) {
+ if (overrideIndex !== void 0 || palette.length <= 1) {
+ return palette;
+ }
+ const filtered = palette.filter((color) => color !== lastColor);
+ return filtered.length > 0 ? filtered : palette;
+ }
+ pickColorFromPalette(palette, order, overrideIndex) {
+ const indexSource = overrideIndex !== void 0 ? overrideIndex : order - 1;
+ const index = normalizePaletteIndex(indexSource, palette.length);
+ return palette[index];
+ }
+ buildColorForChunk(backgroundColor, colorFromPalette, isMixing, isTransition) {
+ if (backgroundColor && isMixing) {
+ return this.mixColors(
+ backgroundColor,
+ colorFromPalette,
+ isTransition
+ );
+ }
+ return new Color(colorFromPalette).to("lch");
+ }
+ mixColors(baseColor, newColorStr, isTransition) {
+ const mixingLevel = isTransition ? 0.5 : 0.4;
+ let mixed = baseColor.mix(newColorStr, mixingLevel, { space: "lch" });
+ if (mixed.deltaE2000(baseColor) < 10) {
+ mixed = baseColor.mix(newColorStr, mixingLevel + 0.1, {
+ space: "lch"
+ });
+ }
+ return mixed;
+ }
+ buildLinearGradient(gradientStops, isTransition) {
+ const defaultGap = isTransition ? 50 : 0;
+ const gap = defaultGap / gradientStops.length * 2;
+ const sumOfGaps = gap * (gradientStops.length - 1);
+ const elementSize = (100 - sumOfGaps) / gradientStops.length;
+ return gradientStops.map((color, index) => {
+ const start = index * (elementSize + gap);
+ const end = start + elementSize;
+ return `${color} ${start}% max(2em, ${end}%)`;
+ });
+ }
+ calculateHighContrastColor(backgroundColor) {
+ const whiteColor = new Color("white");
+ const blackColor = new Color("black");
+ const onWhite = Math.abs(backgroundColor.contrast(whiteColor, "APCA"));
+ const onBlack = Math.abs(backgroundColor.contrast(blackColor, "APCA"));
+ return (onWhite > onBlack ? whiteColor : blackColor).toString();
+ }
+ calculateDarkenedColor(baseColor) {
+ const CONTRAST = 4.5;
+ const memoKey = baseColor.toString();
+ const cached = this.darkenMemoization.get(memoKey);
+ if (cached) {
+ return cached;
+ }
+ const colorLight = new Color(baseColor).to("lch");
+ const colorDark = new Color(baseColor).to("lch");
+ colorLight.c = Math.min(colorLight.c + 3, 100);
+ colorDark.c = Math.min(colorDark.c + 20, 100);
+ let result = "#fff";
+ for (let i = 0; i < 100; i++) {
+ if (baseColor.contrastAPCA(colorLight) <= -60 && colorLight.contrastWCAG21(baseColor) >= CONTRAST) {
+ result = colorLight.toString();
+ break;
+ }
+ if (baseColor.contrastAPCA(colorDark) >= 60 && colorDark.contrastWCAG21(baseColor) >= CONTRAST) {
+ result = colorDark.toString();
+ break;
+ }
+ colorLight.l++;
+ colorDark.l--;
+ }
+ this.darkenMemoization.set(memoKey, result);
+ return result;
+ }
+ generateAdaptiveColorPalette(config) {
+ const hueIncrement = 360 / config.paletteSize;
+ const availableColors = [];
+ for (let i = 0; i < config.paletteSize; i++) {
+ const hue = i * hueIncrement + config.constantOffset;
+ const chroma = config.isDarkTheme ? Math.min(Math.round(config.baseChroma * 1.8), 100) : config.baseChroma;
+ const lightness = config.isDarkTheme ? Math.min(Math.round(config.baseLightness / 2.5), 100) : config.baseLightness;
+ const lchColor = new Color("lch", [
+ lightness,
+ chroma,
+ hue % 360
+ ]).toString();
+ availableColors.push(lchColor);
+ }
+ if (!config.isShuffling) {
+ return availableColors;
+ }
+ const result = [];
+ const available = [...availableColors];
+ let next = 0;
+ while (available.length > 0) {
+ result.push(available[next]);
+ available.splice(next, 1);
+ next = Math.round(next + available.length / 3) % available.length;
+ }
+ return this.rotatePalette(result, config.seed);
+ }
+ processColorPalette(config) {
+ const availableColors = config.palette.map(
+ (item) => new Color(item).to("lch").toString()
+ );
+ return this.rotatePalette(availableColors, config.seed);
+ }
+ rotatePalette(colors, seed) {
+ const result = [...colors];
+ const cut = result.splice(-seed, seed);
+ result.splice(0, 0, ...cut);
+ return result;
+ }
+ findClosestColorIndex(sourceColor, palette) {
+ if (!palette.length) {
+ return 0;
+ }
+ const source = new Color(sourceColor);
+ let bestIndex = 0;
+ let bestDistance = Number.POSITIVE_INFINITY;
+ palette.forEach((color, index) => {
+ const distance2 = source.deltaE2000(new Color(color));
+ if (distance2 < bestDistance) {
+ bestDistance = distance2;
+ bestIndex = index;
+ }
+ });
+ return bestIndex;
+ }
+};
+
+// src/CSSManager.ts
+var CSSManager = class {
+ constructor() {
+ this.appendBuffer = [];
+ this.isPending = false;
+ this.ATTRIBUTE = "colored-tags-style";
+ }
+ append(css) {
+ this.appendBuffer.push(css);
+ if (this.isPending) {
+ return;
+ }
+ this.isPending = true;
+ Promise.resolve().then(() => this.flush());
+ }
+ flush() {
+ let styleEl = document.head.querySelector(`[${this.ATTRIBUTE}]`);
+ if (!styleEl) {
+ styleEl = document.head.createEl("style", {
+ type: "text/css",
+ attr: { [this.ATTRIBUTE]: "" }
+ });
+ }
+ styleEl.appendText(this.appendBuffer.join("\n"));
+ this.appendBuffer = [];
+ this.isPending = false;
+ }
+ removeAll() {
+ document.head.querySelectorAll(`[${this.ATTRIBUTE}]`).forEach((el) => {
+ el.remove();
+ });
+ }
+};
+
+// src/TagManager.ts
+var TagManager = class {
+ constructor(knownTags) {
+ this.renderedTags = /* @__PURE__ */ new Set();
+ const normalized = [];
+ for (const [tagName, order] of Object.entries(knownTags || {})) {
+ const normalizedName = normalizeTagName(tagName);
+ if (normalizedName) {
+ normalized.push([normalizedName, order]);
+ }
+ }
+ this.tagsMap = new Map(normalized);
+ }
+ getTagsMap() {
+ return this.tagsMap;
+ }
+ clearRenderedTags() {
+ this.renderedTags.clear();
+ }
+ markAsRendered(tagName) {
+ this.renderedTags.add(tagName);
+ }
+ isRendered(tagName) {
+ return this.renderedTags.has(tagName);
+ }
+ async updateKnownTags(metadataCache) {
+ const orderedTags = this.collectTagPaths(metadataCache);
+ const nextMap = this.buildOrders(orderedTags);
+ const hasChanges = this.hasChanged(nextMap);
+ if (hasChanges) {
+ this.tagsMap = nextMap;
+ }
+ return hasChanges;
+ }
+ collectTagPaths(metadataCache) {
+ const paths = /* @__PURE__ */ new Set();
+ for (const rawTag of Object.keys(metadataCache.getTags())) {
+ const tag = normalizeTagName(rawTag);
+ if (!tag) {
+ continue;
+ }
+ const chunks = tag.split("/");
+ let combined = "";
+ for (const chunk of chunks) {
+ combined = combined ? `${combined}/${chunk}` : chunk;
+ paths.add(combined);
+ }
+ }
+ return Array.from(paths).sort((a2, b2) => {
+ const depthDiff = a2.split("/").length - b2.split("/").length;
+ return depthDiff !== 0 ? depthDiff : a2.localeCompare(b2);
+ });
+ }
+ buildOrders(tags) {
+ var _a, _b, _c;
+ const nextMap = /* @__PURE__ */ new Map();
+ const parentMaxOrder = /* @__PURE__ */ new Map();
+ for (const tag of tags) {
+ const previousOrder = this.tagsMap.get(tag);
+ if (previousOrder === void 0) {
+ continue;
+ }
+ const parentKey = this.getParentKey(tag);
+ parentMaxOrder.set(
+ parentKey,
+ Math.max((_a = parentMaxOrder.get(parentKey)) != null ? _a : 0, previousOrder)
+ );
+ }
+ for (const tag of tags) {
+ const parentKey = this.getParentKey(tag);
+ const previousOrder = this.tagsMap.get(tag);
+ const order = previousOrder != null ? previousOrder : ((_b = parentMaxOrder.get(parentKey)) != null ? _b : 0) + 1;
+ nextMap.set(tag, order);
+ parentMaxOrder.set(
+ parentKey,
+ Math.max((_c = parentMaxOrder.get(parentKey)) != null ? _c : 0, order)
+ );
+ }
+ return nextMap;
+ }
+ getParentKey(tag) {
+ const parentIndex = tag.lastIndexOf("/");
+ return parentIndex === -1 ? "" : tag.slice(0, parentIndex);
+ }
+ hasChanged(nextMap) {
+ if (nextMap.size !== this.tagsMap.size) {
+ return true;
+ }
+ for (const [tag, order] of nextMap.entries()) {
+ if (this.tagsMap.get(tag) !== order) {
+ return true;
+ }
+ }
+ return false;
+ }
+ exportKnownTags() {
+ return Object.fromEntries(this.tagsMap.entries());
+ }
+};
+
+// src/tag-appliers/PropertiesTagApplier.ts
+var PROPERTY_TAG_SELECTOR = '.metadata-property[data-property-key="tags" i] .multi-select-pill';
+var getPropertyTagTargets = (pillEl) => {
+ const removeButton = pillEl.querySelector(
+ ".multi-select-pill-remove-button"
+ );
+ return removeButton ? [pillEl, removeButton] : [pillEl];
+};
+var getPropertyTagName = (pillEl) => {
+ var _a;
+ const content = pillEl.querySelector(
+ ".multi-select-pill-content"
+ );
+ return normalizeTagText((_a = content == null ? void 0 : content.textContent) != null ? _a : pillEl.textContent);
+};
+var singleUseApplier2 = new TagApplier({
+ selector: PROPERTY_TAG_SELECTOR,
+ getTagText: getPropertyTagName,
+ getTagTargets: getPropertyTagTargets
+});
+var PropertiesTagApplier = class {
+ constructor() {
+ this.applier = new TagApplier({
+ selector: PROPERTY_TAG_SELECTOR,
+ getTagText: getPropertyTagName,
+ getTagTargets: getPropertyTagTargets
+ });
+ }
+ start(root = document.body) {
+ this.applier.start(root);
+ }
+ stop() {
+ this.applier.stop();
+ }
+};
+
+// src/main.ts
+var _ColoredTagsPlugin = class extends import_obsidian3.Plugin {
+ constructor() {
+ super(...arguments);
+ this.palettes = {
+ light: [],
+ dark: []
+ };
+ this.tagColorMap = /* @__PURE__ */ new Map();
+ this.baseViewTagApplier = new BaseViewTagApplier();
+ this.propertiesTagApplier = new PropertiesTagApplier();
+ this.saveKnownTagsPromise = null;
+ this.saveKnownTagsQueued = false;
+ }
+ async onload() {
+ await this.loadSettings();
+ this.colorService = new ColorService();
+ this.cssManager = new CSSManager();
+ this.tagManager = new TagManager(this.settings.knownTags);
+ this.app.workspace.onLayoutReady(async () => {
+ await this.saveKnownTags();
+ this.reload();
+ window.setTimeout(() => {
+ this.checkUpdates();
+ }, _ColoredTagsPlugin.INITIAL_UPDATE_CHECK_DELAY);
+ this.registerEvent(
+ this.app.workspace.on(
+ "editor-change",
+ (0, import_obsidian3.debounce)(
+ async () => {
+ await this.saveKnownTags();
+ this.update();
+ },
+ _ColoredTagsPlugin.EDITOR_CHANGE_DEBOUNCE,
+ true
+ )
+ )
+ );
+ this.registerEvent(
+ this.app.workspace.on(
+ "active-leaf-change",
+ (0, import_obsidian3.debounce)(
+ async () => {
+ await this.saveKnownTags();
+ this.update();
+ },
+ _ColoredTagsPlugin.LEAF_CHANGE_DEBOUNCE,
+ true
+ )
+ )
+ );
+ this.addSettingTab(new ColoredTagsPluginSettingTab(this.app, this));
+ this.registerEditorExtension(coloredClassApplierPlugin);
+ });
+ }
+ async saveKnownTags() {
+ this.saveKnownTagsQueued = true;
+ if (this.saveKnownTagsPromise) {
+ await this.saveKnownTagsPromise;
+ if (this.saveKnownTagsQueued) {
+ await this.saveKnownTags();
+ }
+ return;
+ }
+ this.saveKnownTagsPromise = this.flushKnownTagsUpdates().finally(() => {
+ this.saveKnownTagsPromise = null;
+ });
+ await this.saveKnownTagsPromise;
+ }
+ async flushKnownTagsUpdates() {
+ while (this.saveKnownTagsQueued) {
+ this.saveKnownTagsQueued = false;
+ const hasChanges = await this.tagManager.updateKnownTags(
+ this.app.metadataCache
+ );
+ if (!hasChanges) {
+ continue;
+ }
+ this.settings.knownTags = this.tagManager.exportKnownTags();
+ await this.saveData(this.settings);
+ }
+ }
+ update() {
+ const tags = this.tagManager.getTagsMap();
+ tags.forEach((order, tagName) => {
+ if (!this.tagManager.isRendered(tagName)) {
+ this.tagManager.markAsRendered(tagName);
+ this.colorizeTag(tagName);
+ }
+ });
+ }
+ async checkUpdates() {
+ var _a, _b;
+ try {
+ const { json: response } = await (0, import_obsidian3.requestUrl)({
+ url: "https://api.github.com/repos/pfrankov/obsidian-colored-tags/releases/latest",
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ contentType: "application/json"
+ });
+ if (response.tag_name !== this.manifest.version) {
+ const pluginName = (_b = (_a = this.manifest) == null ? void 0 : _a.name) != null ? _b : "Colored Tags";
+ new import_obsidian3.Notice(I18n.t("notices.updateAvailable", { pluginName }));
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+ reload(palettes) {
+ this.onunload();
+ this.palettes = palettes != null ? palettes : this.colorService.generatePalettes(this.settings.palette);
+ this.refreshTagColorMap();
+ this.baseViewTagApplier.start();
+ this.propertiesTagApplier.start();
+ this.update();
+ }
+ async saveSettings() {
+ const previousPalettes = {
+ light: [...this.palettes.light],
+ dark: [...this.palettes.dark]
+ };
+ const nextPalettes = this.colorService.generatePalettes(
+ this.settings.palette
+ );
+ if (this.havePalettesChanged(previousPalettes, nextPalettes)) {
+ this.remapTagColors(previousPalettes, nextPalettes);
+ }
+ await this.saveData(this.settings);
+ this.reload(nextPalettes);
+ }
+ colorizeTag(tagName) {
+ tagName = tagName.replace(/#/g, "");
+ const tagsMap = this.tagManager.getTagsMap();
+ const getColors = (palette) => this.colorService.getColors(
+ tagName,
+ palette,
+ tagsMap,
+ {
+ isMixing: this.settings.mixColors,
+ isTransition: this.settings.transition,
+ highTextContrast: this.settings.accessibility.highTextContrast
+ },
+ this.tagColorMap
+ );
+ const light = getColors(this.palettes.light);
+ const dark = getColors(this.palettes.dark);
+ const selectors = this.buildTagSelectors(tagName);
+ const removeButtonSelectors = this.buildRemoveButtonSelectors(tagName);
+ const groups = [
+ { prefix: "body", colors: light },
+ { prefix: "body.theme-dark", colors: dark }
+ ];
+ const buildScopedSelector = (prefix, base) => base.length ? base.map((s) => `${prefix} ${s}`).join(", ") : "";
+ const css = groups.map(({ prefix, colors }) => {
+ const scopedTags = buildScopedSelector(prefix, selectors);
+ const scopedButtons = buildScopedSelector(
+ prefix,
+ removeButtonSelectors
+ );
+ const rules = [];
+ if (scopedTags) {
+ rules.push(
+ `${scopedTags} {
+ background-color: ${colors.background};
+ color: ${colors.color};
+ background-image: linear-gradient(108deg, ${colors.linearGradient.join(
+ ", "
+ )});
+ }`
+ );
+ }
+ if (scopedButtons) {
+ rules.push(
+ `${scopedButtons} {
+ color: ${colors.color};
+ stroke: ${colors.color};
+ }`
+ );
+ }
+ return rules.join("\n");
+ }).join("\n");
+ this.cssManager.append(`
+${css}
+`);
+ }
+ buildTagSelectors(tagName) {
+ const tagHref = `#${tagName.replace(/\//g, "\\/")}`;
+ const tagFlat = tagName.replace(/[^0-9a-z-]/gi, "");
+ const tagLower = tagName.toLowerCase().replace(/\//g, "\\/");
+ const selectors = [
+ `a.tag[href="${tagHref}" i]`,
+ `a.tag.colored-tag-${tagLower}`,
+ `.cm-s-obsidian .cm-line span.cm-hashtag.colored-tag-${tagLower}`,
+ `.metadata-property[data-property-key="tags" i] .multi-select-pill.colored-tag-${tagLower}`
+ ];
+ if (tagFlat && !tagName.includes("/")) {
+ const flatLower = tagFlat.toLowerCase();
+ selectors.push(
+ `.cm-s-obsidian .cm-line span.cm-tag-${flatLower}.cm-hashtag`,
+ `.cm-s-obsidian .cm-line span.cm-tag-${tagFlat}.cm-hashtag`
+ );
+ }
+ return selectors;
+ }
+ buildRemoveButtonSelectors(tagName) {
+ const tagLower = tagName.toLowerCase().replace(/\//g, "\\/");
+ return tagLower ? [
+ `.metadata-property[data-property-key="tags" i] .multi-select-pill-remove-button.colored-tag-${tagLower}`
+ ] : [];
+ }
+ async loadSettings() {
+ const loadedData = await this.loadData();
+ this.settings = this.migrateSettings(loadedData);
+ }
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ migrateSettings(loadedData) {
+ var _a;
+ const data = loadedData ? { ...loadedData } : {};
+ let currentVersion = (_a = data._version) != null ? _a : 0;
+ let needToSave = false;
+ const applyMigration = (targetVersion, action) => {
+ if (currentVersion < targetVersion) {
+ action();
+ currentVersion = targetVersion;
+ data._version = targetVersion;
+ needToSave = true;
+ }
+ };
+ applyMigration(2, () => {
+ data.palette = 16;
+ });
+ applyMigration(3, () => {
+ data.palette = {
+ ...DEFAULT_SETTINGS.palette,
+ seed: data.seed || 0
+ };
+ if (data.chroma > 16 || data.lightness > 87) {
+ data.palette.selected = "adaptive-bright" /* ADAPTIVE_BRIGHT */;
+ }
+ delete data.chroma;
+ delete data.lightness;
+ delete data.seed;
+ });
+ applyMigration(4, () => {
+ data.tagColors = data.tagColors || {};
+ });
+ const settings = Object.assign({}, DEFAULT_SETTINGS, data);
+ if (needToSave) {
+ this.saveData(settings);
+ }
+ return settings;
+ }
+ onunload() {
+ this.tagManager.clearRenderedTags();
+ this.cssManager.removeAll();
+ this.baseViewTagApplier.stop();
+ this.propertiesTagApplier.stop();
+ }
+ refreshTagColorMap() {
+ this.tagColorMap = new Map(
+ Object.entries(this.settings.tagColors || {}).map(
+ ([tagName, paletteIndex]) => [
+ normalizeTagName(tagName),
+ paletteIndex
+ ]
+ )
+ );
+ }
+ havePalettesChanged(prev, next) {
+ return prev.light.length !== next.light.length || prev.dark.length !== next.dark.length || prev.light.some((color, index) => color !== next.light[index]) || prev.dark.some((color, index) => color !== next.dark[index]);
+ }
+ remapTagColors(previousPalettes, nextPalettes) {
+ if (!previousPalettes.light.length || !nextPalettes.light.length) {
+ return;
+ }
+ const remapped = {};
+ const nextPalette = nextPalettes.light;
+ const previousPalette = previousPalettes.light;
+ Object.entries(this.settings.tagColors || {}).forEach(
+ ([tagName, paletteIndex]) => {
+ const normalizedIndex = normalizePaletteIndex(
+ paletteIndex,
+ previousPalette.length
+ );
+ const sourceColor = previousPalette[normalizedIndex];
+ const normalizedTagName = normalizeTagName(tagName);
+ if (!normalizedTagName) {
+ return;
+ }
+ const bestMatch = this.colorService.findClosestColorIndex(
+ sourceColor,
+ nextPalette
+ );
+ remapped[normalizedTagName] = bestMatch;
+ }
+ );
+ this.settings.tagColors = remapped;
+ }
+};
+var ColoredTagsPlugin = _ColoredTagsPlugin;
+ColoredTagsPlugin.INITIAL_UPDATE_CHECK_DELAY = 5e3;
+// 5 seconds
+ColoredTagsPlugin.EDITOR_CHANGE_DEBOUNCE = 3e3;
+// 3 seconds
+ColoredTagsPlugin.LEAF_CHANGE_DEBOUNCE = 300;
+
+/* nosourcemap */
\ No newline at end of file
diff --git a/liph/.obsidian/plugins/colored-tags/manifest.json b/liph/.obsidian/plugins/colored-tags/manifest.json
new file mode 100755
index 0000000..971d4ee
--- /dev/null
+++ b/liph/.obsidian/plugins/colored-tags/manifest.json
@@ -0,0 +1,10 @@
+{
+ "id": "colored-tags",
+ "name": "Colored Tags",
+ "version": "6.1.2",
+ "minAppVersion": "0.15.0",
+ "description": "Colorizes tags in different colors. Colors of nested tags are mixed with the root tag to improve readability. Text color contrast is automatically matched to comply with AA level of WCAG 2.1.",
+ "author": "Pavel Frankov",
+ "authorUrl": "https://github.com/pfrankov",
+ "isDesktopOnly": false
+}
\ No newline at end of file
diff --git a/liph/.obsidian/plugins/colored-tags/styles.css b/liph/.obsidian/plugins/colored-tags/styles.css
new file mode 100755
index 0000000..a46c1ae
--- /dev/null
+++ b/liph/.obsidian/plugins/colored-tags/styles.css
@@ -0,0 +1,196 @@
+/* src/styles.css */
+.colored-tags-settings .tagsExample {
+ display: flex;
+ gap: 0.5em;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ margin: 1em 0 2em;
+}
+.colored-tags-settings .palette {
+ display: flex;
+ align-items: stretch;
+ border-radius: var(--radius-m);
+ overflow: hidden;
+}
+.colored-tags-settings .visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+.colored-tags-settings .community-palettes {
+ display: block;
+}
+.colored-tags-settings .community-palettes > *:first-child {
+ margin-inline-end: 0;
+}
+.colored-tags-settings .community-palettes__scroll {
+ margin-top: var(--size-4-3);
+ max-height: 400px;
+ overflow-y: auto;
+ padding: var(--size-4-3);
+}
+.colored-tags-settings .community-palettes__grid {
+ display: grid;
+ gap: var(--size-4-4);
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+}
+.colored-tags-settings .community-palettes__status {
+ color: var(--text-faint);
+ padding: var(--size-4-3);
+ border-radius: var(--radius-m);
+ background-color: var(--background-primary);
+ border: var(--border-width) solid var(--background-modifier-border);
+}
+.colored-tags-settings .community-palette-card {
+ display: flex;
+ flex-direction: column;
+ gap: var(--size-4-2);
+ padding: var(--size-4-3);
+ border-radius: var(--radius-m);
+ position: relative;
+ background-color: var(--dropdown-background);
+ box-shadow: var(--input-shadow);
+ cursor: pointer;
+ transition: all 120ms ease-in-out;
+}
+.colored-tags-settings .community-palette-card.is-selected {
+ box-shadow: 0 0 0 2px var(--interactive-accent), var(--input-shadow);
+ cursor: auto;
+}
+.colored-tags-settings .community-palette-card.is-selected::after {
+ content: "\2713";
+ position: absolute;
+ top: var(--size-4-2);
+ right: var(--size-4-2);
+ width: var(--size-4-4);
+ height: var(--size-4-4);
+ border-radius: var(--radius-round, 999px);
+ background-color: var(--interactive-accent);
+ color: var(--text-on-accent, var(--background-primary));
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-weight: var(--font-semibold, 600);
+}
+.colored-tags-settings .community-palette-card:hover {
+ background-color: var(--dropdown-background-hover);
+ box-shadow: var(--input-shadow-hover);
+}
+.colored-tags-settings .community-palette-card.is-selected:hover {
+ box-shadow: 0 0 0 2px var(--interactive-accent), var(--input-shadow);
+}
+.colored-tags-settings .community-palette-card:focus-visible {
+ outline: var(--border-width) solid var(--interactive-accent);
+ outline-offset: var(--outline-offset);
+ box-shadow: 0 0 0 3px var(--background-modifier-border-focus);
+}
+.colored-tags-settings .community-palette-card__preview {
+ display: flex;
+ overflow: hidden;
+ border-radius: var(--radius-m);
+ height: var(--input-height);
+}
+.colored-tags-settings .community-palette-card__meta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--size-4-2);
+ min-height: var(--line-height-tight);
+}
+.colored-tags-settings .community-palette-card__author {
+ font-family: var(--font-monospace);
+ color: var(--text-muted);
+}
+.colored-tags-settings .community-palette-card.is-selected .community-palette-card__author {
+ font-weight: var(--font-semibold, 600);
+}
+.colored-tags-settings .community-palette-card__upvotes {
+ color: var(--text-muted);
+ font-size: var(--font-ui-smaller);
+ font-weight: var(--font-medium);
+}
+.colored-tags-settings .tag-color-setting-item {
+ flex-direction: column;
+ align-items: stretch;
+ gap: var(--size-4-2);
+}
+.colored-tags-settings .tag-color-setting-item .setting-item-control {
+ width: 100%;
+}
+.colored-tags-settings .tag-color-setting {
+ display: flex;
+ flex-direction: column;
+ gap: var(--size-4-2);
+ width: 100%;
+}
+.colored-tags-settings .tag-color-setting__row {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: var(--size-4-3);
+ align-items: start;
+}
+.colored-tags-settings .tag-color-setting__input input[type=text] {
+ width: 220px;
+ font-weight: var(--font-normal, 400);
+}
+.theme-light .colored-tags-settings .tag-color-setting__input input[type=text] {
+ color-scheme: light;
+ background-color: var(--background-primary);
+ color: var(--text-normal);
+}
+.colored-tags-settings .tag-color-setting__palette {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--size-2-2);
+}
+.colored-tags-settings .tag-color-setting__swatch {
+ width: 32px;
+ height: 32px;
+ border-radius: var(--radius-s);
+ cursor: pointer;
+}
+.colored-tags-settings .tag-color-setting__swatch:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+.colored-tags-settings .tag-color-setting__swatch.is-selected {
+ box-shadow: 0 0 0 3px var(--interactive-accent);
+}
+.colored-tags-settings .tag-color-setting__chips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--size-2-2);
+ margin-top: var(--size-2-3);
+}
+.colored-tags-settings .tag-color-setting__chip {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ gap: var(--size-2-1);
+ text-decoration: none;
+}
+.colored-tags-settings .tag-color-setting__chip-remove {
+ background: none;
+ border: none;
+ color: var(--text-faint);
+ width: 18px;
+ height: 18px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ box-shadow: none;
+ font-size: var(--font-ui-smaller);
+ margin-left: -6px;
+}
+.colored-tags-settings .tag-color-setting__empty {
+ color: var(--text-faint);
+ font-style: italic;
+}
diff --git a/liph/.obsidian/plugins/dataview/main.js b/liph/.obsidian/plugins/dataview/main.js
new file mode 100755
index 0000000..898d702
--- /dev/null
+++ b/liph/.obsidian/plugins/dataview/main.js
@@ -0,0 +1,20876 @@
+'use strict';
+
+var obsidian = require('obsidian');
+var state = require('@codemirror/state');
+var view = require('@codemirror/view');
+var language = require('@codemirror/language');
+
+// these aren't really private, but nor are they really useful to document
+
+/**
+ * @private
+ */
+class LuxonError extends Error {}
+
+/**
+ * @private
+ */
+class InvalidDateTimeError extends LuxonError {
+ constructor(reason) {
+ super(`Invalid DateTime: ${reason.toMessage()}`);
+ }
+}
+
+/**
+ * @private
+ */
+class InvalidIntervalError extends LuxonError {
+ constructor(reason) {
+ super(`Invalid Interval: ${reason.toMessage()}`);
+ }
+}
+
+/**
+ * @private
+ */
+class InvalidDurationError extends LuxonError {
+ constructor(reason) {
+ super(`Invalid Duration: ${reason.toMessage()}`);
+ }
+}
+
+/**
+ * @private
+ */
+class ConflictingSpecificationError extends LuxonError {}
+
+/**
+ * @private
+ */
+class InvalidUnitError extends LuxonError {
+ constructor(unit) {
+ super(`Invalid unit ${unit}`);
+ }
+}
+
+/**
+ * @private
+ */
+class InvalidArgumentError extends LuxonError {}
+
+/**
+ * @private
+ */
+class ZoneIsAbstractError extends LuxonError {
+ constructor() {
+ super("Zone is an abstract class");
+ }
+}
+
+/**
+ * @private
+ */
+
+const n$1 = "numeric",
+ s$2 = "short",
+ l$2 = "long";
+
+const DATE_SHORT = {
+ year: n$1,
+ month: n$1,
+ day: n$1,
+};
+
+const DATE_MED = {
+ year: n$1,
+ month: s$2,
+ day: n$1,
+};
+
+const DATE_MED_WITH_WEEKDAY = {
+ year: n$1,
+ month: s$2,
+ day: n$1,
+ weekday: s$2,
+};
+
+const DATE_FULL = {
+ year: n$1,
+ month: l$2,
+ day: n$1,
+};
+
+const DATE_HUGE = {
+ year: n$1,
+ month: l$2,
+ day: n$1,
+ weekday: l$2,
+};
+
+const TIME_SIMPLE = {
+ hour: n$1,
+ minute: n$1,
+};
+
+const TIME_WITH_SECONDS = {
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+};
+
+const TIME_WITH_SHORT_OFFSET = {
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ timeZoneName: s$2,
+};
+
+const TIME_WITH_LONG_OFFSET = {
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ timeZoneName: l$2,
+};
+
+const TIME_24_SIMPLE = {
+ hour: n$1,
+ minute: n$1,
+ hourCycle: "h23",
+};
+
+const TIME_24_WITH_SECONDS = {
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ hourCycle: "h23",
+};
+
+const TIME_24_WITH_SHORT_OFFSET = {
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ hourCycle: "h23",
+ timeZoneName: s$2,
+};
+
+const TIME_24_WITH_LONG_OFFSET = {
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ hourCycle: "h23",
+ timeZoneName: l$2,
+};
+
+const DATETIME_SHORT = {
+ year: n$1,
+ month: n$1,
+ day: n$1,
+ hour: n$1,
+ minute: n$1,
+};
+
+const DATETIME_SHORT_WITH_SECONDS = {
+ year: n$1,
+ month: n$1,
+ day: n$1,
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+};
+
+const DATETIME_MED = {
+ year: n$1,
+ month: s$2,
+ day: n$1,
+ hour: n$1,
+ minute: n$1,
+};
+
+const DATETIME_MED_WITH_SECONDS = {
+ year: n$1,
+ month: s$2,
+ day: n$1,
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+};
+
+const DATETIME_MED_WITH_WEEKDAY = {
+ year: n$1,
+ month: s$2,
+ day: n$1,
+ weekday: s$2,
+ hour: n$1,
+ minute: n$1,
+};
+
+const DATETIME_FULL = {
+ year: n$1,
+ month: l$2,
+ day: n$1,
+ hour: n$1,
+ minute: n$1,
+ timeZoneName: s$2,
+};
+
+const DATETIME_FULL_WITH_SECONDS = {
+ year: n$1,
+ month: l$2,
+ day: n$1,
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ timeZoneName: s$2,
+};
+
+const DATETIME_HUGE = {
+ year: n$1,
+ month: l$2,
+ day: n$1,
+ weekday: l$2,
+ hour: n$1,
+ minute: n$1,
+ timeZoneName: l$2,
+};
+
+const DATETIME_HUGE_WITH_SECONDS = {
+ year: n$1,
+ month: l$2,
+ day: n$1,
+ weekday: l$2,
+ hour: n$1,
+ minute: n$1,
+ second: n$1,
+ timeZoneName: l$2,
+};
+
+/**
+ * @interface
+ */
+class Zone {
+ /**
+ * The type of zone
+ * @abstract
+ * @type {string}
+ */
+ get type() {
+ throw new ZoneIsAbstractError();
+ }
+
+ /**
+ * The name of this zone.
+ * @abstract
+ * @type {string}
+ */
+ get name() {
+ throw new ZoneIsAbstractError();
+ }
+
+ get ianaName() {
+ return this.name;
+ }
+
+ /**
+ * Returns whether the offset is known to be fixed for the whole year.
+ * @abstract
+ * @type {boolean}
+ */
+ get isUniversal() {
+ throw new ZoneIsAbstractError();
+ }
+
+ /**
+ * Returns the offset's common name (such as EST) at the specified timestamp
+ * @abstract
+ * @param {number} ts - Epoch milliseconds for which to get the name
+ * @param {Object} opts - Options to affect the format
+ * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.
+ * @param {string} opts.locale - What locale to return the offset name in.
+ * @return {string}
+ */
+ offsetName(ts, opts) {
+ throw new ZoneIsAbstractError();
+ }
+
+ /**
+ * Returns the offset's value as a string
+ * @abstract
+ * @param {number} ts - Epoch milliseconds for which to get the offset
+ * @param {string} format - What style of offset to return.
+ * Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
+ * @return {string}
+ */
+ formatOffset(ts, format) {
+ throw new ZoneIsAbstractError();
+ }
+
+ /**
+ * Return the offset in minutes for this zone at the specified timestamp.
+ * @abstract
+ * @param {number} ts - Epoch milliseconds for which to compute the offset
+ * @return {number}
+ */
+ offset(ts) {
+ throw new ZoneIsAbstractError();
+ }
+
+ /**
+ * Return whether this Zone is equal to another zone
+ * @abstract
+ * @param {Zone} otherZone - the zone to compare
+ * @return {boolean}
+ */
+ equals(otherZone) {
+ throw new ZoneIsAbstractError();
+ }
+
+ /**
+ * Return whether this Zone is valid.
+ * @abstract
+ * @type {boolean}
+ */
+ get isValid() {
+ throw new ZoneIsAbstractError();
+ }
+}
+
+let singleton$1 = null;
+
+/**
+ * Represents the local zone for this JavaScript environment.
+ * @implements {Zone}
+ */
+class SystemZone extends Zone {
+ /**
+ * Get a singleton instance of the local zone
+ * @return {SystemZone}
+ */
+ static get instance() {
+ if (singleton$1 === null) {
+ singleton$1 = new SystemZone();
+ }
+ return singleton$1;
+ }
+
+ /** @override **/
+ get type() {
+ return "system";
+ }
+
+ /** @override **/
+ get name() {
+ return new Intl.DateTimeFormat().resolvedOptions().timeZone;
+ }
+
+ /** @override **/
+ get isUniversal() {
+ return false;
+ }
+
+ /** @override **/
+ offsetName(ts, { format, locale }) {
+ return parseZoneInfo(ts, format, locale);
+ }
+
+ /** @override **/
+ formatOffset(ts, format) {
+ return formatOffset(this.offset(ts), format);
+ }
+
+ /** @override **/
+ offset(ts) {
+ return -new Date(ts).getTimezoneOffset();
+ }
+
+ /** @override **/
+ equals(otherZone) {
+ return otherZone.type === "system";
+ }
+
+ /** @override **/
+ get isValid() {
+ return true;
+ }
+}
+
+let dtfCache = {};
+function makeDTF(zone) {
+ if (!dtfCache[zone]) {
+ dtfCache[zone] = new Intl.DateTimeFormat("en-US", {
+ hour12: false,
+ timeZone: zone,
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ era: "short",
+ });
+ }
+ return dtfCache[zone];
+}
+
+const typeToPos = {
+ year: 0,
+ month: 1,
+ day: 2,
+ era: 3,
+ hour: 4,
+ minute: 5,
+ second: 6,
+};
+
+function hackyOffset(dtf, date) {
+ const formatted = dtf.format(date).replace(/\u200E/g, ""),
+ parsed = /(\d+)\/(\d+)\/(\d+) (AD|BC),? (\d+):(\d+):(\d+)/.exec(formatted),
+ [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed;
+ return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond];
+}
+
+function partsOffset(dtf, date) {
+ const formatted = dtf.formatToParts(date);
+ const filled = [];
+ for (let i = 0; i < formatted.length; i++) {
+ const { type, value } = formatted[i];
+ const pos = typeToPos[type];
+
+ if (type === "era") {
+ filled[pos] = value;
+ } else if (!isUndefined(pos)) {
+ filled[pos] = parseInt(value, 10);
+ }
+ }
+ return filled;
+}
+
+let ianaZoneCache = {};
+/**
+ * A zone identified by an IANA identifier, like America/New_York
+ * @implements {Zone}
+ */
+class IANAZone extends Zone {
+ /**
+ * @param {string} name - Zone name
+ * @return {IANAZone}
+ */
+ static create(name) {
+ if (!ianaZoneCache[name]) {
+ ianaZoneCache[name] = new IANAZone(name);
+ }
+ return ianaZoneCache[name];
+ }
+
+ /**
+ * Reset local caches. Should only be necessary in testing scenarios.
+ * @return {void}
+ */
+ static resetCache() {
+ ianaZoneCache = {};
+ dtfCache = {};
+ }
+
+ /**
+ * Returns whether the provided string is a valid specifier. This only checks the string's format, not that the specifier identifies a known zone; see isValidZone for that.
+ * @param {string} s - The string to check validity on
+ * @example IANAZone.isValidSpecifier("America/New_York") //=> true
+ * @example IANAZone.isValidSpecifier("Sport~~blorp") //=> false
+ * @deprecated This method returns false for some valid IANA names. Use isValidZone instead.
+ * @return {boolean}
+ */
+ static isValidSpecifier(s) {
+ return this.isValidZone(s);
+ }
+
+ /**
+ * Returns whether the provided string identifies a real zone
+ * @param {string} zone - The string to check
+ * @example IANAZone.isValidZone("America/New_York") //=> true
+ * @example IANAZone.isValidZone("Fantasia/Castle") //=> false
+ * @example IANAZone.isValidZone("Sport~~blorp") //=> false
+ * @return {boolean}
+ */
+ static isValidZone(zone) {
+ if (!zone) {
+ return false;
+ }
+ try {
+ new Intl.DateTimeFormat("en-US", { timeZone: zone }).format();
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ constructor(name) {
+ super();
+ /** @private **/
+ this.zoneName = name;
+ /** @private **/
+ this.valid = IANAZone.isValidZone(name);
+ }
+
+ /** @override **/
+ get type() {
+ return "iana";
+ }
+
+ /** @override **/
+ get name() {
+ return this.zoneName;
+ }
+
+ /** @override **/
+ get isUniversal() {
+ return false;
+ }
+
+ /** @override **/
+ offsetName(ts, { format, locale }) {
+ return parseZoneInfo(ts, format, locale, this.name);
+ }
+
+ /** @override **/
+ formatOffset(ts, format) {
+ return formatOffset(this.offset(ts), format);
+ }
+
+ /** @override **/
+ offset(ts) {
+ const date = new Date(ts);
+
+ if (isNaN(date)) return NaN;
+
+ const dtf = makeDTF(this.name);
+ let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts
+ ? partsOffset(dtf, date)
+ : hackyOffset(dtf, date);
+
+ if (adOrBc === "BC") {
+ year = -Math.abs(year) + 1;
+ }
+
+ // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat
+ const adjustedHour = hour === 24 ? 0 : hour;
+
+ const asUTC = objToLocalTS({
+ year,
+ month,
+ day,
+ hour: adjustedHour,
+ minute,
+ second,
+ millisecond: 0,
+ });
+
+ let asTS = +date;
+ const over = asTS % 1000;
+ asTS -= over >= 0 ? over : 1000 + over;
+ return (asUTC - asTS) / (60 * 1000);
+ }
+
+ /** @override **/
+ equals(otherZone) {
+ return otherZone.type === "iana" && otherZone.name === this.name;
+ }
+
+ /** @override **/
+ get isValid() {
+ return this.valid;
+ }
+}
+
+// todo - remap caching
+
+let intlLFCache = {};
+function getCachedLF(locString, opts = {}) {
+ const key = JSON.stringify([locString, opts]);
+ let dtf = intlLFCache[key];
+ if (!dtf) {
+ dtf = new Intl.ListFormat(locString, opts);
+ intlLFCache[key] = dtf;
+ }
+ return dtf;
+}
+
+let intlDTCache = {};
+function getCachedDTF(locString, opts = {}) {
+ const key = JSON.stringify([locString, opts]);
+ let dtf = intlDTCache[key];
+ if (!dtf) {
+ dtf = new Intl.DateTimeFormat(locString, opts);
+ intlDTCache[key] = dtf;
+ }
+ return dtf;
+}
+
+let intlNumCache = {};
+function getCachedINF(locString, opts = {}) {
+ const key = JSON.stringify([locString, opts]);
+ let inf = intlNumCache[key];
+ if (!inf) {
+ inf = new Intl.NumberFormat(locString, opts);
+ intlNumCache[key] = inf;
+ }
+ return inf;
+}
+
+let intlRelCache = {};
+function getCachedRTF(locString, opts = {}) {
+ const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options
+ const key = JSON.stringify([locString, cacheKeyOpts]);
+ let inf = intlRelCache[key];
+ if (!inf) {
+ inf = new Intl.RelativeTimeFormat(locString, opts);
+ intlRelCache[key] = inf;
+ }
+ return inf;
+}
+
+let sysLocaleCache = null;
+function systemLocale() {
+ if (sysLocaleCache) {
+ return sysLocaleCache;
+ } else {
+ sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale;
+ return sysLocaleCache;
+ }
+}
+
+function parseLocaleString(localeStr) {
+ // I really want to avoid writing a BCP 47 parser
+ // see, e.g. https://github.com/wooorm/bcp-47
+ // Instead, we'll do this:
+
+ // a) if the string has no -u extensions, just leave it alone
+ // b) if it does, use Intl to resolve everything
+ // c) if Intl fails, try again without the -u
+
+ // private subtags and unicode subtags have ordering requirements,
+ // and we're not properly parsing this, so just strip out the
+ // private ones if they exist.
+ const xIndex = localeStr.indexOf("-x-");
+ if (xIndex !== -1) {
+ localeStr = localeStr.substring(0, xIndex);
+ }
+
+ const uIndex = localeStr.indexOf("-u-");
+ if (uIndex === -1) {
+ return [localeStr];
+ } else {
+ let options;
+ let selectedStr;
+ try {
+ options = getCachedDTF(localeStr).resolvedOptions();
+ selectedStr = localeStr;
+ } catch (e) {
+ const smaller = localeStr.substring(0, uIndex);
+ options = getCachedDTF(smaller).resolvedOptions();
+ selectedStr = smaller;
+ }
+
+ const { numberingSystem, calendar } = options;
+ return [selectedStr, numberingSystem, calendar];
+ }
+}
+
+function intlConfigString(localeStr, numberingSystem, outputCalendar) {
+ if (outputCalendar || numberingSystem) {
+ if (!localeStr.includes("-u-")) {
+ localeStr += "-u";
+ }
+
+ if (outputCalendar) {
+ localeStr += `-ca-${outputCalendar}`;
+ }
+
+ if (numberingSystem) {
+ localeStr += `-nu-${numberingSystem}`;
+ }
+ return localeStr;
+ } else {
+ return localeStr;
+ }
+}
+
+function mapMonths(f) {
+ const ms = [];
+ for (let i = 1; i <= 12; i++) {
+ const dt = DateTime.utc(2009, i, 1);
+ ms.push(f(dt));
+ }
+ return ms;
+}
+
+function mapWeekdays(f) {
+ const ms = [];
+ for (let i = 1; i <= 7; i++) {
+ const dt = DateTime.utc(2016, 11, 13 + i);
+ ms.push(f(dt));
+ }
+ return ms;
+}
+
+function listStuff(loc, length, englishFn, intlFn) {
+ const mode = loc.listingMode();
+
+ if (mode === "error") {
+ return null;
+ } else if (mode === "en") {
+ return englishFn(length);
+ } else {
+ return intlFn(length);
+ }
+}
+
+function supportsFastNumbers(loc) {
+ if (loc.numberingSystem && loc.numberingSystem !== "latn") {
+ return false;
+ } else {
+ return (
+ loc.numberingSystem === "latn" ||
+ !loc.locale ||
+ loc.locale.startsWith("en") ||
+ new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === "latn"
+ );
+ }
+}
+
+/**
+ * @private
+ */
+
+class PolyNumberFormatter {
+ constructor(intl, forceSimple, opts) {
+ this.padTo = opts.padTo || 0;
+ this.floor = opts.floor || false;
+
+ const { padTo, floor, ...otherOpts } = opts;
+
+ if (!forceSimple || Object.keys(otherOpts).length > 0) {
+ const intlOpts = { useGrouping: false, ...opts };
+ if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;
+ this.inf = getCachedINF(intl, intlOpts);
+ }
+ }
+
+ format(i) {
+ if (this.inf) {
+ const fixed = this.floor ? Math.floor(i) : i;
+ return this.inf.format(fixed);
+ } else {
+ // to match the browser's numberformatter defaults
+ const fixed = this.floor ? Math.floor(i) : roundTo(i, 3);
+ return padStart(fixed, this.padTo);
+ }
+ }
+}
+
+/**
+ * @private
+ */
+
+class PolyDateFormatter {
+ constructor(dt, intl, opts) {
+ this.opts = opts;
+ this.originalZone = undefined;
+
+ let z = undefined;
+ if (this.opts.timeZone) {
+ // Don't apply any workarounds if a timeZone is explicitly provided in opts
+ this.dt = dt;
+ } else if (dt.zone.type === "fixed") {
+ // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.
+ // That is why fixed-offset TZ is set to that unless it is:
+ // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.
+ // 2. Unsupported by the browser:
+ // - some do not support Etc/
+ // - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata
+ const gmtOffset = -1 * (dt.offset / 60);
+ const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;
+ if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {
+ z = offsetZ;
+ this.dt = dt;
+ } else {
+ // Not all fixed-offset zones like Etc/+4:30 are present in tzdata so
+ // we manually apply the offset and substitute the zone as needed.
+ z = "UTC";
+ this.dt = dt.offset === 0 ? dt : dt.setZone("UTC").plus({ minutes: dt.offset });
+ this.originalZone = dt.zone;
+ }
+ } else if (dt.zone.type === "system") {
+ this.dt = dt;
+ } else if (dt.zone.type === "iana") {
+ this.dt = dt;
+ z = dt.zone.name;
+ } else {
+ // Custom zones can have any offset / offsetName so we just manually
+ // apply the offset and substitute the zone as needed.
+ z = "UTC";
+ this.dt = dt.setZone("UTC").plus({ minutes: dt.offset });
+ this.originalZone = dt.zone;
+ }
+
+ const intlOpts = { ...this.opts };
+ intlOpts.timeZone = intlOpts.timeZone || z;
+ this.dtf = getCachedDTF(intl, intlOpts);
+ }
+
+ format() {
+ if (this.originalZone) {
+ // If we have to substitute in the actual zone name, we have to use
+ // formatToParts so that the timezone can be replaced.
+ return this.formatToParts()
+ .map(({ value }) => value)
+ .join("");
+ }
+ return this.dtf.format(this.dt.toJSDate());
+ }
+
+ formatToParts() {
+ const parts = this.dtf.formatToParts(this.dt.toJSDate());
+ if (this.originalZone) {
+ return parts.map((part) => {
+ if (part.type === "timeZoneName") {
+ const offsetName = this.originalZone.offsetName(this.dt.ts, {
+ locale: this.dt.locale,
+ format: this.opts.timeZoneName,
+ });
+ return {
+ ...part,
+ value: offsetName,
+ };
+ } else {
+ return part;
+ }
+ });
+ }
+ return parts;
+ }
+
+ resolvedOptions() {
+ return this.dtf.resolvedOptions();
+ }
+}
+
+/**
+ * @private
+ */
+class PolyRelFormatter {
+ constructor(intl, isEnglish, opts) {
+ this.opts = { style: "long", ...opts };
+ if (!isEnglish && hasRelative()) {
+ this.rtf = getCachedRTF(intl, opts);
+ }
+ }
+
+ format(count, unit) {
+ if (this.rtf) {
+ return this.rtf.format(count, unit);
+ } else {
+ return formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== "long");
+ }
+ }
+
+ formatToParts(count, unit) {
+ if (this.rtf) {
+ return this.rtf.formatToParts(count, unit);
+ } else {
+ return [];
+ }
+ }
+}
+
+/**
+ * @private
+ */
+
+class Locale {
+ static fromOpts(opts) {
+ return Locale.create(opts.locale, opts.numberingSystem, opts.outputCalendar, opts.defaultToEN);
+ }
+
+ static create(locale, numberingSystem, outputCalendar, defaultToEN = false) {
+ const specifiedLocale = locale || Settings.defaultLocale;
+ // the system locale is useful for human readable strings but annoying for parsing/formatting known formats
+ const localeR = specifiedLocale || (defaultToEN ? "en-US" : systemLocale());
+ const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem;
+ const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;
+ return new Locale(localeR, numberingSystemR, outputCalendarR, specifiedLocale);
+ }
+
+ static resetCache() {
+ sysLocaleCache = null;
+ intlDTCache = {};
+ intlNumCache = {};
+ intlRelCache = {};
+ }
+
+ static fromObject({ locale, numberingSystem, outputCalendar } = {}) {
+ return Locale.create(locale, numberingSystem, outputCalendar);
+ }
+
+ constructor(locale, numbering, outputCalendar, specifiedLocale) {
+ const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);
+
+ this.locale = parsedLocale;
+ this.numberingSystem = numbering || parsedNumberingSystem || null;
+ this.outputCalendar = outputCalendar || parsedOutputCalendar || null;
+ this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);
+
+ this.weekdaysCache = { format: {}, standalone: {} };
+ this.monthsCache = { format: {}, standalone: {} };
+ this.meridiemCache = null;
+ this.eraCache = {};
+
+ this.specifiedLocale = specifiedLocale;
+ this.fastNumbersCached = null;
+ }
+
+ get fastNumbers() {
+ if (this.fastNumbersCached == null) {
+ this.fastNumbersCached = supportsFastNumbers(this);
+ }
+
+ return this.fastNumbersCached;
+ }
+
+ listingMode() {
+ const isActuallyEn = this.isEnglish();
+ const hasNoWeirdness =
+ (this.numberingSystem === null || this.numberingSystem === "latn") &&
+ (this.outputCalendar === null || this.outputCalendar === "gregory");
+ return isActuallyEn && hasNoWeirdness ? "en" : "intl";
+ }
+
+ clone(alts) {
+ if (!alts || Object.getOwnPropertyNames(alts).length === 0) {
+ return this;
+ } else {
+ return Locale.create(
+ alts.locale || this.specifiedLocale,
+ alts.numberingSystem || this.numberingSystem,
+ alts.outputCalendar || this.outputCalendar,
+ alts.defaultToEN || false
+ );
+ }
+ }
+
+ redefaultToEN(alts = {}) {
+ return this.clone({ ...alts, defaultToEN: true });
+ }
+
+ redefaultToSystem(alts = {}) {
+ return this.clone({ ...alts, defaultToEN: false });
+ }
+
+ months(length, format = false) {
+ return listStuff(this, length, months, () => {
+ const intl = format ? { month: length, day: "numeric" } : { month: length },
+ formatStr = format ? "format" : "standalone";
+ if (!this.monthsCache[formatStr][length]) {
+ this.monthsCache[formatStr][length] = mapMonths((dt) => this.extract(dt, intl, "month"));
+ }
+ return this.monthsCache[formatStr][length];
+ });
+ }
+
+ weekdays(length, format = false) {
+ return listStuff(this, length, weekdays, () => {
+ const intl = format
+ ? { weekday: length, year: "numeric", month: "long", day: "numeric" }
+ : { weekday: length },
+ formatStr = format ? "format" : "standalone";
+ if (!this.weekdaysCache[formatStr][length]) {
+ this.weekdaysCache[formatStr][length] = mapWeekdays((dt) =>
+ this.extract(dt, intl, "weekday")
+ );
+ }
+ return this.weekdaysCache[formatStr][length];
+ });
+ }
+
+ meridiems() {
+ return listStuff(
+ this,
+ undefined,
+ () => meridiems,
+ () => {
+ // In theory there could be aribitrary day periods. We're gonna assume there are exactly two
+ // for AM and PM. This is probably wrong, but it's makes parsing way easier.
+ if (!this.meridiemCache) {
+ const intl = { hour: "numeric", hourCycle: "h12" };
+ this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(
+ (dt) => this.extract(dt, intl, "dayperiod")
+ );
+ }
+
+ return this.meridiemCache;
+ }
+ );
+ }
+
+ eras(length) {
+ return listStuff(this, length, eras, () => {
+ const intl = { era: length };
+
+ // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates
+ // to definitely enumerate them.
+ if (!this.eraCache[length]) {
+ this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map((dt) =>
+ this.extract(dt, intl, "era")
+ );
+ }
+
+ return this.eraCache[length];
+ });
+ }
+
+ extract(dt, intlOpts, field) {
+ const df = this.dtFormatter(dt, intlOpts),
+ results = df.formatToParts(),
+ matching = results.find((m) => m.type.toLowerCase() === field);
+ return matching ? matching.value : null;
+ }
+
+ numberFormatter(opts = {}) {
+ // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave)
+ // (in contrast, the rest of the condition is used heavily)
+ return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts);
+ }
+
+ dtFormatter(dt, intlOpts = {}) {
+ return new PolyDateFormatter(dt, this.intl, intlOpts);
+ }
+
+ relFormatter(opts = {}) {
+ return new PolyRelFormatter(this.intl, this.isEnglish(), opts);
+ }
+
+ listFormatter(opts = {}) {
+ return getCachedLF(this.intl, opts);
+ }
+
+ isEnglish() {
+ return (
+ this.locale === "en" ||
+ this.locale.toLowerCase() === "en-us" ||
+ new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith("en-us")
+ );
+ }
+
+ equals(other) {
+ return (
+ this.locale === other.locale &&
+ this.numberingSystem === other.numberingSystem &&
+ this.outputCalendar === other.outputCalendar
+ );
+ }
+}
+
+let singleton = null;
+
+/**
+ * A zone with a fixed offset (meaning no DST)
+ * @implements {Zone}
+ */
+class FixedOffsetZone extends Zone {
+ /**
+ * Get a singleton instance of UTC
+ * @return {FixedOffsetZone}
+ */
+ static get utcInstance() {
+ if (singleton === null) {
+ singleton = new FixedOffsetZone(0);
+ }
+ return singleton;
+ }
+
+ /**
+ * Get an instance with a specified offset
+ * @param {number} offset - The offset in minutes
+ * @return {FixedOffsetZone}
+ */
+ static instance(offset) {
+ return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset);
+ }
+
+ /**
+ * Get an instance of FixedOffsetZone from a UTC offset string, like "UTC+6"
+ * @param {string} s - The offset string to parse
+ * @example FixedOffsetZone.parseSpecifier("UTC+6")
+ * @example FixedOffsetZone.parseSpecifier("UTC+06")
+ * @example FixedOffsetZone.parseSpecifier("UTC-6:00")
+ * @return {FixedOffsetZone}
+ */
+ static parseSpecifier(s) {
+ if (s) {
+ const r = s.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);
+ if (r) {
+ return new FixedOffsetZone(signedOffset(r[1], r[2]));
+ }
+ }
+ return null;
+ }
+
+ constructor(offset) {
+ super();
+ /** @private **/
+ this.fixed = offset;
+ }
+
+ /** @override **/
+ get type() {
+ return "fixed";
+ }
+
+ /** @override **/
+ get name() {
+ return this.fixed === 0 ? "UTC" : `UTC${formatOffset(this.fixed, "narrow")}`;
+ }
+
+ get ianaName() {
+ if (this.fixed === 0) {
+ return "Etc/UTC";
+ } else {
+ return `Etc/GMT${formatOffset(-this.fixed, "narrow")}`;
+ }
+ }
+
+ /** @override **/
+ offsetName() {
+ return this.name;
+ }
+
+ /** @override **/
+ formatOffset(ts, format) {
+ return formatOffset(this.fixed, format);
+ }
+
+ /** @override **/
+ get isUniversal() {
+ return true;
+ }
+
+ /** @override **/
+ offset() {
+ return this.fixed;
+ }
+
+ /** @override **/
+ equals(otherZone) {
+ return otherZone.type === "fixed" && otherZone.fixed === this.fixed;
+ }
+
+ /** @override **/
+ get isValid() {
+ return true;
+ }
+}
+
+/**
+ * A zone that failed to parse. You should never need to instantiate this.
+ * @implements {Zone}
+ */
+class InvalidZone extends Zone {
+ constructor(zoneName) {
+ super();
+ /** @private */
+ this.zoneName = zoneName;
+ }
+
+ /** @override **/
+ get type() {
+ return "invalid";
+ }
+
+ /** @override **/
+ get name() {
+ return this.zoneName;
+ }
+
+ /** @override **/
+ get isUniversal() {
+ return false;
+ }
+
+ /** @override **/
+ offsetName() {
+ return null;
+ }
+
+ /** @override **/
+ formatOffset() {
+ return "";
+ }
+
+ /** @override **/
+ offset() {
+ return NaN;
+ }
+
+ /** @override **/
+ equals() {
+ return false;
+ }
+
+ /** @override **/
+ get isValid() {
+ return false;
+ }
+}
+
+/**
+ * @private
+ */
+
+function normalizeZone(input, defaultZone) {
+ if (isUndefined(input) || input === null) {
+ return defaultZone;
+ } else if (input instanceof Zone) {
+ return input;
+ } else if (isString(input)) {
+ const lowered = input.toLowerCase();
+ if (lowered === "default") return defaultZone;
+ else if (lowered === "local" || lowered === "system") return SystemZone.instance;
+ else if (lowered === "utc" || lowered === "gmt") return FixedOffsetZone.utcInstance;
+ else return FixedOffsetZone.parseSpecifier(lowered) || IANAZone.create(input);
+ } else if (isNumber(input)) {
+ return FixedOffsetZone.instance(input);
+ } else if (typeof input === "object" && "offset" in input && typeof input.offset === "function") {
+ // This is dumb, but the instanceof check above doesn't seem to really work
+ // so we're duck checking it
+ return input;
+ } else {
+ return new InvalidZone(input);
+ }
+}
+
+let now = () => Date.now(),
+ defaultZone = "system",
+ defaultLocale = null,
+ defaultNumberingSystem = null,
+ defaultOutputCalendar = null,
+ twoDigitCutoffYear = 60,
+ throwOnInvalid;
+
+/**
+ * Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here.
+ */
+class Settings {
+ /**
+ * Get the callback for returning the current timestamp.
+ * @type {function}
+ */
+ static get now() {
+ return now;
+ }
+
+ /**
+ * Set the callback for returning the current timestamp.
+ * The function should return a number, which will be interpreted as an Epoch millisecond count
+ * @type {function}
+ * @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future
+ * @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time
+ */
+ static set now(n) {
+ now = n;
+ }
+
+ /**
+ * Set the default time zone to create DateTimes in. Does not affect existing instances.
+ * Use the value "system" to reset this value to the system's time zone.
+ * @type {string}
+ */
+ static set defaultZone(zone) {
+ defaultZone = zone;
+ }
+
+ /**
+ * Get the default time zone object currently used to create DateTimes. Does not affect existing instances.
+ * The default value is the system's time zone (the one set on the machine that runs this code).
+ * @type {Zone}
+ */
+ static get defaultZone() {
+ return normalizeZone(defaultZone, SystemZone.instance);
+ }
+
+ /**
+ * Get the default locale to create DateTimes with. Does not affect existing instances.
+ * @type {string}
+ */
+ static get defaultLocale() {
+ return defaultLocale;
+ }
+
+ /**
+ * Set the default locale to create DateTimes with. Does not affect existing instances.
+ * @type {string}
+ */
+ static set defaultLocale(locale) {
+ defaultLocale = locale;
+ }
+
+ /**
+ * Get the default numbering system to create DateTimes with. Does not affect existing instances.
+ * @type {string}
+ */
+ static get defaultNumberingSystem() {
+ return defaultNumberingSystem;
+ }
+
+ /**
+ * Set the default numbering system to create DateTimes with. Does not affect existing instances.
+ * @type {string}
+ */
+ static set defaultNumberingSystem(numberingSystem) {
+ defaultNumberingSystem = numberingSystem;
+ }
+
+ /**
+ * Get the default output calendar to create DateTimes with. Does not affect existing instances.
+ * @type {string}
+ */
+ static get defaultOutputCalendar() {
+ return defaultOutputCalendar;
+ }
+
+ /**
+ * Set the default output calendar to create DateTimes with. Does not affect existing instances.
+ * @type {string}
+ */
+ static set defaultOutputCalendar(outputCalendar) {
+ defaultOutputCalendar = outputCalendar;
+ }
+
+ /**
+ * Get the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.
+ * @type {number}
+ */
+ static get twoDigitCutoffYear() {
+ return twoDigitCutoffYear;
+ }
+
+ /**
+ * Set the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.
+ * @type {number}
+ * @example Settings.twoDigitCutoffYear = 0 // cut-off year is 0, so all 'yy' are interpreted as current century
+ * @example Settings.twoDigitCutoffYear = 50 // '49' -> 1949; '50' -> 2050
+ * @example Settings.twoDigitCutoffYear = 1950 // interpreted as 50
+ * @example Settings.twoDigitCutoffYear = 2050 // ALSO interpreted as 50
+ */
+ static set twoDigitCutoffYear(cutoffYear) {
+ twoDigitCutoffYear = cutoffYear % 100;
+ }
+
+ /**
+ * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
+ * @type {boolean}
+ */
+ static get throwOnInvalid() {
+ return throwOnInvalid;
+ }
+
+ /**
+ * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals
+ * @type {boolean}
+ */
+ static set throwOnInvalid(t) {
+ throwOnInvalid = t;
+ }
+
+ /**
+ * Reset Luxon's global caches. Should only be necessary in testing scenarios.
+ * @return {void}
+ */
+ static resetCaches() {
+ Locale.resetCache();
+ IANAZone.resetCache();
+ }
+}
+
+/*
+ This is just a junk drawer, containing anything used across multiple classes.
+ Because Luxon is small(ish), this should stay small and we won't worry about splitting
+ it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area.
+*/
+
+/**
+ * @private
+ */
+
+// TYPES
+
+function isUndefined(o) {
+ return typeof o === "undefined";
+}
+
+function isNumber(o) {
+ return typeof o === "number";
+}
+
+function isInteger(o) {
+ return typeof o === "number" && o % 1 === 0;
+}
+
+function isString(o) {
+ return typeof o === "string";
+}
+
+function isDate(o) {
+ return Object.prototype.toString.call(o) === "[object Date]";
+}
+
+// CAPABILITIES
+
+function hasRelative() {
+ try {
+ return typeof Intl !== "undefined" && !!Intl.RelativeTimeFormat;
+ } catch (e) {
+ return false;
+ }
+}
+
+// OBJECTS AND ARRAYS
+
+function maybeArray(thing) {
+ return Array.isArray(thing) ? thing : [thing];
+}
+
+function bestBy(arr, by, compare) {
+ if (arr.length === 0) {
+ return undefined;
+ }
+ return arr.reduce((best, next) => {
+ const pair = [by(next), next];
+ if (!best) {
+ return pair;
+ } else if (compare(best[0], pair[0]) === best[0]) {
+ return best;
+ } else {
+ return pair;
+ }
+ }, null)[1];
+}
+
+function pick(obj, keys) {
+ return keys.reduce((a, k) => {
+ a[k] = obj[k];
+ return a;
+ }, {});
+}
+
+function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+// NUMBERS AND STRINGS
+
+function integerBetween(thing, bottom, top) {
+ return isInteger(thing) && thing >= bottom && thing <= top;
+}
+
+// x % n but takes the sign of n instead of x
+function floorMod(x, n) {
+ return x - n * Math.floor(x / n);
+}
+
+function padStart(input, n = 2) {
+ const isNeg = input < 0;
+ let padded;
+ if (isNeg) {
+ padded = "-" + ("" + -input).padStart(n, "0");
+ } else {
+ padded = ("" + input).padStart(n, "0");
+ }
+ return padded;
+}
+
+function parseInteger(string) {
+ if (isUndefined(string) || string === null || string === "") {
+ return undefined;
+ } else {
+ return parseInt(string, 10);
+ }
+}
+
+function parseFloating(string) {
+ if (isUndefined(string) || string === null || string === "") {
+ return undefined;
+ } else {
+ return parseFloat(string);
+ }
+}
+
+function parseMillis(fraction) {
+ // Return undefined (instead of 0) in these cases, where fraction is not set
+ if (isUndefined(fraction) || fraction === null || fraction === "") {
+ return undefined;
+ } else {
+ const f = parseFloat("0." + fraction) * 1000;
+ return Math.floor(f);
+ }
+}
+
+function roundTo(number, digits, towardZero = false) {
+ const factor = 10 ** digits,
+ rounder = towardZero ? Math.trunc : Math.round;
+ return rounder(number * factor) / factor;
+}
+
+// DATE BASICS
+
+function isLeapYear(year) {
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
+}
+
+function daysInYear(year) {
+ return isLeapYear(year) ? 366 : 365;
+}
+
+function daysInMonth(year, month) {
+ const modMonth = floorMod(month - 1, 12) + 1,
+ modYear = year + (month - modMonth) / 12;
+
+ if (modMonth === 2) {
+ return isLeapYear(modYear) ? 29 : 28;
+ } else {
+ return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];
+ }
+}
+
+// convert a calendar object to a local timestamp (epoch, but with the offset baked in)
+function objToLocalTS(obj) {
+ let d = Date.UTC(
+ obj.year,
+ obj.month - 1,
+ obj.day,
+ obj.hour,
+ obj.minute,
+ obj.second,
+ obj.millisecond
+ );
+
+ // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that
+ if (obj.year < 100 && obj.year >= 0) {
+ d = new Date(d);
+ // set the month and day again, this is necessary because year 2000 is a leap year, but year 100 is not
+ // so if obj.year is in 99, but obj.day makes it roll over into year 100,
+ // the calculations done by Date.UTC are using year 2000 - which is incorrect
+ d.setUTCFullYear(obj.year, obj.month - 1, obj.day);
+ }
+ return +d;
+}
+
+function weeksInWeekYear(weekYear) {
+ const p1 =
+ (weekYear +
+ Math.floor(weekYear / 4) -
+ Math.floor(weekYear / 100) +
+ Math.floor(weekYear / 400)) %
+ 7,
+ last = weekYear - 1,
+ p2 = (last + Math.floor(last / 4) - Math.floor(last / 100) + Math.floor(last / 400)) % 7;
+ return p1 === 4 || p2 === 3 ? 53 : 52;
+}
+
+function untruncateYear(year) {
+ if (year > 99) {
+ return year;
+ } else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2000 + year;
+}
+
+// PARSING
+
+function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) {
+ const date = new Date(ts),
+ intlOpts = {
+ hourCycle: "h23",
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ };
+
+ if (timeZone) {
+ intlOpts.timeZone = timeZone;
+ }
+
+ const modified = { timeZoneName: offsetFormat, ...intlOpts };
+
+ const parsed = new Intl.DateTimeFormat(locale, modified)
+ .formatToParts(date)
+ .find((m) => m.type.toLowerCase() === "timezonename");
+ return parsed ? parsed.value : null;
+}
+
+// signedOffset('-5', '30') -> -330
+function signedOffset(offHourStr, offMinuteStr) {
+ let offHour = parseInt(offHourStr, 10);
+
+ // don't || this because we want to preserve -0
+ if (Number.isNaN(offHour)) {
+ offHour = 0;
+ }
+
+ const offMin = parseInt(offMinuteStr, 10) || 0,
+ offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;
+ return offHour * 60 + offMinSigned;
+}
+
+// COERCION
+
+function asNumber(value) {
+ const numericValue = Number(value);
+ if (typeof value === "boolean" || value === "" || Number.isNaN(numericValue))
+ throw new InvalidArgumentError(`Invalid unit value ${value}`);
+ return numericValue;
+}
+
+function normalizeObject(obj, normalizer) {
+ const normalized = {};
+ for (const u in obj) {
+ if (hasOwnProperty(obj, u)) {
+ const v = obj[u];
+ if (v === undefined || v === null) continue;
+ normalized[normalizer(u)] = asNumber(v);
+ }
+ }
+ return normalized;
+}
+
+function formatOffset(offset, format) {
+ const hours = Math.trunc(Math.abs(offset / 60)),
+ minutes = Math.trunc(Math.abs(offset % 60)),
+ sign = offset >= 0 ? "+" : "-";
+
+ switch (format) {
+ case "short":
+ return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;
+ case "narrow":
+ return `${sign}${hours}${minutes > 0 ? `:${minutes}` : ""}`;
+ case "techie":
+ return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;
+ default:
+ throw new RangeError(`Value format ${format} is out of range for property format`);
+ }
+}
+
+function timeObject(obj) {
+ return pick(obj, ["hour", "minute", "second", "millisecond"]);
+}
+
+/**
+ * @private
+ */
+
+const monthsLong = [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+];
+
+const monthsShort = [
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+];
+
+const monthsNarrow = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
+
+function months(length) {
+ switch (length) {
+ case "narrow":
+ return [...monthsNarrow];
+ case "short":
+ return [...monthsShort];
+ case "long":
+ return [...monthsLong];
+ case "numeric":
+ return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
+ case "2-digit":
+ return ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
+ default:
+ return null;
+ }
+}
+
+const weekdaysLong = [
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sunday",
+];
+
+const weekdaysShort = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
+
+const weekdaysNarrow = ["M", "T", "W", "T", "F", "S", "S"];
+
+function weekdays(length) {
+ switch (length) {
+ case "narrow":
+ return [...weekdaysNarrow];
+ case "short":
+ return [...weekdaysShort];
+ case "long":
+ return [...weekdaysLong];
+ case "numeric":
+ return ["1", "2", "3", "4", "5", "6", "7"];
+ default:
+ return null;
+ }
+}
+
+const meridiems = ["AM", "PM"];
+
+const erasLong = ["Before Christ", "Anno Domini"];
+
+const erasShort = ["BC", "AD"];
+
+const erasNarrow = ["B", "A"];
+
+function eras(length) {
+ switch (length) {
+ case "narrow":
+ return [...erasNarrow];
+ case "short":
+ return [...erasShort];
+ case "long":
+ return [...erasLong];
+ default:
+ return null;
+ }
+}
+
+function meridiemForDateTime(dt) {
+ return meridiems[dt.hour < 12 ? 0 : 1];
+}
+
+function weekdayForDateTime(dt, length) {
+ return weekdays(length)[dt.weekday - 1];
+}
+
+function monthForDateTime(dt, length) {
+ return months(length)[dt.month - 1];
+}
+
+function eraForDateTime(dt, length) {
+ return eras(length)[dt.year < 0 ? 0 : 1];
+}
+
+function formatRelativeTime(unit, count, numeric = "always", narrow = false) {
+ const units = {
+ years: ["year", "yr."],
+ quarters: ["quarter", "qtr."],
+ months: ["month", "mo."],
+ weeks: ["week", "wk."],
+ days: ["day", "day", "days"],
+ hours: ["hour", "hr."],
+ minutes: ["minute", "min."],
+ seconds: ["second", "sec."],
+ };
+
+ const lastable = ["hours", "minutes", "seconds"].indexOf(unit) === -1;
+
+ if (numeric === "auto" && lastable) {
+ const isDay = unit === "days";
+ switch (count) {
+ case 1:
+ return isDay ? "tomorrow" : `next ${units[unit][0]}`;
+ case -1:
+ return isDay ? "yesterday" : `last ${units[unit][0]}`;
+ case 0:
+ return isDay ? "today" : `this ${units[unit][0]}`;
+ }
+ }
+
+ const isInPast = Object.is(count, -0) || count < 0,
+ fmtValue = Math.abs(count),
+ singular = fmtValue === 1,
+ lilUnits = units[unit],
+ fmtUnit = narrow
+ ? singular
+ ? lilUnits[1]
+ : lilUnits[2] || lilUnits[1]
+ : singular
+ ? units[unit][0]
+ : unit;
+ return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`;
+}
+
+function stringifyTokens(splits, tokenToString) {
+ let s = "";
+ for (const token of splits) {
+ if (token.literal) {
+ s += token.val;
+ } else {
+ s += tokenToString(token.val);
+ }
+ }
+ return s;
+}
+
+const macroTokenToFormatOpts = {
+ D: DATE_SHORT,
+ DD: DATE_MED,
+ DDD: DATE_FULL,
+ DDDD: DATE_HUGE,
+ t: TIME_SIMPLE,
+ tt: TIME_WITH_SECONDS,
+ ttt: TIME_WITH_SHORT_OFFSET,
+ tttt: TIME_WITH_LONG_OFFSET,
+ T: TIME_24_SIMPLE,
+ TT: TIME_24_WITH_SECONDS,
+ TTT: TIME_24_WITH_SHORT_OFFSET,
+ TTTT: TIME_24_WITH_LONG_OFFSET,
+ f: DATETIME_SHORT,
+ ff: DATETIME_MED,
+ fff: DATETIME_FULL,
+ ffff: DATETIME_HUGE,
+ F: DATETIME_SHORT_WITH_SECONDS,
+ FF: DATETIME_MED_WITH_SECONDS,
+ FFF: DATETIME_FULL_WITH_SECONDS,
+ FFFF: DATETIME_HUGE_WITH_SECONDS,
+};
+
+/**
+ * @private
+ */
+
+class Formatter {
+ static create(locale, opts = {}) {
+ return new Formatter(locale, opts);
+ }
+
+ static parseFormat(fmt) {
+ // white-space is always considered a literal in user-provided formats
+ // the " " token has a special meaning (see unitForToken)
+
+ let current = null,
+ currentFull = "",
+ bracketed = false;
+ const splits = [];
+ for (let i = 0; i < fmt.length; i++) {
+ const c = fmt.charAt(i);
+ if (c === "'") {
+ if (currentFull.length > 0) {
+ splits.push({ literal: bracketed || /^\s+$/.test(currentFull), val: currentFull });
+ }
+ current = null;
+ currentFull = "";
+ bracketed = !bracketed;
+ } else if (bracketed) {
+ currentFull += c;
+ } else if (c === current) {
+ currentFull += c;
+ } else {
+ if (currentFull.length > 0) {
+ splits.push({ literal: /^\s+$/.test(currentFull), val: currentFull });
+ }
+ currentFull = c;
+ current = c;
+ }
+ }
+
+ if (currentFull.length > 0) {
+ splits.push({ literal: bracketed || /^\s+$/.test(currentFull), val: currentFull });
+ }
+
+ return splits;
+ }
+
+ static macroTokenToFormatOpts(token) {
+ return macroTokenToFormatOpts[token];
+ }
+
+ constructor(locale, formatOpts) {
+ this.opts = formatOpts;
+ this.loc = locale;
+ this.systemLoc = null;
+ }
+
+ formatWithSystemDefault(dt, opts) {
+ if (this.systemLoc === null) {
+ this.systemLoc = this.loc.redefaultToSystem();
+ }
+ const df = this.systemLoc.dtFormatter(dt, { ...this.opts, ...opts });
+ return df.format();
+ }
+
+ dtFormatter(dt, opts = {}) {
+ return this.loc.dtFormatter(dt, { ...this.opts, ...opts });
+ }
+
+ formatDateTime(dt, opts) {
+ return this.dtFormatter(dt, opts).format();
+ }
+
+ formatDateTimeParts(dt, opts) {
+ return this.dtFormatter(dt, opts).formatToParts();
+ }
+
+ formatInterval(interval, opts) {
+ const df = this.dtFormatter(interval.start, opts);
+ return df.dtf.formatRange(interval.start.toJSDate(), interval.end.toJSDate());
+ }
+
+ resolvedOptions(dt, opts) {
+ return this.dtFormatter(dt, opts).resolvedOptions();
+ }
+
+ num(n, p = 0) {
+ // we get some perf out of doing this here, annoyingly
+ if (this.opts.forceSimple) {
+ return padStart(n, p);
+ }
+
+ const opts = { ...this.opts };
+
+ if (p > 0) {
+ opts.padTo = p;
+ }
+
+ return this.loc.numberFormatter(opts).format(n);
+ }
+
+ formatDateTimeFromString(dt, fmt) {
+ const knownEnglish = this.loc.listingMode() === "en",
+ useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== "gregory",
+ string = (opts, extract) => this.loc.extract(dt, opts, extract),
+ formatOffset = (opts) => {
+ if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {
+ return "Z";
+ }
+
+ return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : "";
+ },
+ meridiem = () =>
+ knownEnglish
+ ? meridiemForDateTime(dt)
+ : string({ hour: "numeric", hourCycle: "h12" }, "dayperiod"),
+ month = (length, standalone) =>
+ knownEnglish
+ ? monthForDateTime(dt, length)
+ : string(standalone ? { month: length } : { month: length, day: "numeric" }, "month"),
+ weekday = (length, standalone) =>
+ knownEnglish
+ ? weekdayForDateTime(dt, length)
+ : string(
+ standalone ? { weekday: length } : { weekday: length, month: "long", day: "numeric" },
+ "weekday"
+ ),
+ maybeMacro = (token) => {
+ const formatOpts = Formatter.macroTokenToFormatOpts(token);
+ if (formatOpts) {
+ return this.formatWithSystemDefault(dt, formatOpts);
+ } else {
+ return token;
+ }
+ },
+ era = (length) =>
+ knownEnglish ? eraForDateTime(dt, length) : string({ era: length }, "era"),
+ tokenToString = (token) => {
+ // Where possible: https://cldr.unicode.org/translation/date-time/date-time-symbols
+ switch (token) {
+ // ms
+ case "S":
+ return this.num(dt.millisecond);
+ case "u":
+ // falls through
+ case "SSS":
+ return this.num(dt.millisecond, 3);
+ // seconds
+ case "s":
+ return this.num(dt.second);
+ case "ss":
+ return this.num(dt.second, 2);
+ // fractional seconds
+ case "uu":
+ return this.num(Math.floor(dt.millisecond / 10), 2);
+ case "uuu":
+ return this.num(Math.floor(dt.millisecond / 100));
+ // minutes
+ case "m":
+ return this.num(dt.minute);
+ case "mm":
+ return this.num(dt.minute, 2);
+ // hours
+ case "h":
+ return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12);
+ case "hh":
+ return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2);
+ case "H":
+ return this.num(dt.hour);
+ case "HH":
+ return this.num(dt.hour, 2);
+ // offset
+ case "Z":
+ // like +6
+ return formatOffset({ format: "narrow", allowZ: this.opts.allowZ });
+ case "ZZ":
+ // like +06:00
+ return formatOffset({ format: "short", allowZ: this.opts.allowZ });
+ case "ZZZ":
+ // like +0600
+ return formatOffset({ format: "techie", allowZ: this.opts.allowZ });
+ case "ZZZZ":
+ // like EST
+ return dt.zone.offsetName(dt.ts, { format: "short", locale: this.loc.locale });
+ case "ZZZZZ":
+ // like Eastern Standard Time
+ return dt.zone.offsetName(dt.ts, { format: "long", locale: this.loc.locale });
+ // zone
+ case "z":
+ // like America/New_York
+ return dt.zoneName;
+ // meridiems
+ case "a":
+ return meridiem();
+ // dates
+ case "d":
+ return useDateTimeFormatter ? string({ day: "numeric" }, "day") : this.num(dt.day);
+ case "dd":
+ return useDateTimeFormatter ? string({ day: "2-digit" }, "day") : this.num(dt.day, 2);
+ // weekdays - standalone
+ case "c":
+ // like 1
+ return this.num(dt.weekday);
+ case "ccc":
+ // like 'Tues'
+ return weekday("short", true);
+ case "cccc":
+ // like 'Tuesday'
+ return weekday("long", true);
+ case "ccccc":
+ // like 'T'
+ return weekday("narrow", true);
+ // weekdays - format
+ case "E":
+ // like 1
+ return this.num(dt.weekday);
+ case "EEE":
+ // like 'Tues'
+ return weekday("short", false);
+ case "EEEE":
+ // like 'Tuesday'
+ return weekday("long", false);
+ case "EEEEE":
+ // like 'T'
+ return weekday("narrow", false);
+ // months - standalone
+ case "L":
+ // like 1
+ return useDateTimeFormatter
+ ? string({ month: "numeric", day: "numeric" }, "month")
+ : this.num(dt.month);
+ case "LL":
+ // like 01, doesn't seem to work
+ return useDateTimeFormatter
+ ? string({ month: "2-digit", day: "numeric" }, "month")
+ : this.num(dt.month, 2);
+ case "LLL":
+ // like Jan
+ return month("short", true);
+ case "LLLL":
+ // like January
+ return month("long", true);
+ case "LLLLL":
+ // like J
+ return month("narrow", true);
+ // months - format
+ case "M":
+ // like 1
+ return useDateTimeFormatter
+ ? string({ month: "numeric" }, "month")
+ : this.num(dt.month);
+ case "MM":
+ // like 01
+ return useDateTimeFormatter
+ ? string({ month: "2-digit" }, "month")
+ : this.num(dt.month, 2);
+ case "MMM":
+ // like Jan
+ return month("short", false);
+ case "MMMM":
+ // like January
+ return month("long", false);
+ case "MMMMM":
+ // like J
+ return month("narrow", false);
+ // years
+ case "y":
+ // like 2014
+ return useDateTimeFormatter ? string({ year: "numeric" }, "year") : this.num(dt.year);
+ case "yy":
+ // like 14
+ return useDateTimeFormatter
+ ? string({ year: "2-digit" }, "year")
+ : this.num(dt.year.toString().slice(-2), 2);
+ case "yyyy":
+ // like 0012
+ return useDateTimeFormatter
+ ? string({ year: "numeric" }, "year")
+ : this.num(dt.year, 4);
+ case "yyyyyy":
+ // like 000012
+ return useDateTimeFormatter
+ ? string({ year: "numeric" }, "year")
+ : this.num(dt.year, 6);
+ // eras
+ case "G":
+ // like AD
+ return era("short");
+ case "GG":
+ // like Anno Domini
+ return era("long");
+ case "GGGGG":
+ return era("narrow");
+ case "kk":
+ return this.num(dt.weekYear.toString().slice(-2), 2);
+ case "kkkk":
+ return this.num(dt.weekYear, 4);
+ case "W":
+ return this.num(dt.weekNumber);
+ case "WW":
+ return this.num(dt.weekNumber, 2);
+ case "o":
+ return this.num(dt.ordinal);
+ case "ooo":
+ return this.num(dt.ordinal, 3);
+ case "q":
+ // like 1
+ return this.num(dt.quarter);
+ case "qq":
+ // like 01
+ return this.num(dt.quarter, 2);
+ case "X":
+ return this.num(Math.floor(dt.ts / 1000));
+ case "x":
+ return this.num(dt.ts);
+ default:
+ return maybeMacro(token);
+ }
+ };
+
+ return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);
+ }
+
+ formatDurationFromString(dur, fmt) {
+ const tokenToField = (token) => {
+ switch (token[0]) {
+ case "S":
+ return "millisecond";
+ case "s":
+ return "second";
+ case "m":
+ return "minute";
+ case "h":
+ return "hour";
+ case "d":
+ return "day";
+ case "w":
+ return "week";
+ case "M":
+ return "month";
+ case "y":
+ return "year";
+ default:
+ return null;
+ }
+ },
+ tokenToString = (lildur) => (token) => {
+ const mapped = tokenToField(token);
+ if (mapped) {
+ return this.num(lildur.get(mapped), token.length);
+ } else {
+ return token;
+ }
+ },
+ tokens = Formatter.parseFormat(fmt),
+ realTokens = tokens.reduce(
+ (found, { literal, val }) => (literal ? found : found.concat(val)),
+ []
+ ),
+ collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t));
+ return stringifyTokens(tokens, tokenToString(collapsed));
+ }
+}
+
+class Invalid {
+ constructor(reason, explanation) {
+ this.reason = reason;
+ this.explanation = explanation;
+ }
+
+ toMessage() {
+ if (this.explanation) {
+ return `${this.reason}: ${this.explanation}`;
+ } else {
+ return this.reason;
+ }
+ }
+}
+
+/*
+ * This file handles parsing for well-specified formats. Here's how it works:
+ * Two things go into parsing: a regex to match with and an extractor to take apart the groups in the match.
+ * An extractor is just a function that takes a regex match array and returns a { year: ..., month: ... } object
+ * parse() does the work of executing the regex and applying the extractor. It takes multiple regex/extractor pairs to try in sequence.
+ * Extractors can take a "cursor" representing the offset in the match to look at. This makes it easy to combine extractors.
+ * combineExtractors() does the work of combining them, keeping track of the cursor through multiple extractions.
+ * Some extractions are super dumb and simpleParse and fromStrings help DRY them.
+ */
+
+const ianaRegex = /[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/;
+
+function combineRegexes(...regexes) {
+ const full = regexes.reduce((f, r) => f + r.source, "");
+ return RegExp(`^${full}$`);
+}
+
+function combineExtractors(...extractors) {
+ return (m) =>
+ extractors
+ .reduce(
+ ([mergedVals, mergedZone, cursor], ex) => {
+ const [val, zone, next] = ex(m, cursor);
+ return [{ ...mergedVals, ...val }, zone || mergedZone, next];
+ },
+ [{}, null, 1]
+ )
+ .slice(0, 2);
+}
+
+function parse(s, ...patterns) {
+ if (s == null) {
+ return [null, null];
+ }
+
+ for (const [regex, extractor] of patterns) {
+ const m = regex.exec(s);
+ if (m) {
+ return extractor(m);
+ }
+ }
+ return [null, null];
+}
+
+function simpleParse(...keys) {
+ return (match, cursor) => {
+ const ret = {};
+ let i;
+
+ for (i = 0; i < keys.length; i++) {
+ ret[keys[i]] = parseInteger(match[cursor + i]);
+ }
+ return [ret, null, cursor + i];
+ };
+}
+
+// ISO and SQL parsing
+const offsetRegex = /(?:(Z)|([+-]\d\d)(?::?(\d\d))?)/;
+const isoExtendedZone = `(?:${offsetRegex.source}?(?:\\[(${ianaRegex.source})\\])?)?`;
+const isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/;
+const isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`);
+const isoTimeExtensionRegex = RegExp(`(?:T${isoTimeRegex.source})?`);
+const isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/;
+const isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/;
+const isoOrdinalRegex = /(\d{4})-?(\d{3})/;
+const extractISOWeekData = simpleParse("weekYear", "weekNumber", "weekDay");
+const extractISOOrdinalData = simpleParse("year", "ordinal");
+const sqlYmdRegex = /(\d{4})-(\d\d)-(\d\d)/; // dumbed-down version of the ISO one
+const sqlTimeRegex = RegExp(
+ `${isoTimeBaseRegex.source} ?(?:${offsetRegex.source}|(${ianaRegex.source}))?`
+);
+const sqlTimeExtensionRegex = RegExp(`(?: ${sqlTimeRegex.source})?`);
+
+function int(match, pos, fallback) {
+ const m = match[pos];
+ return isUndefined(m) ? fallback : parseInteger(m);
+}
+
+function extractISOYmd(match, cursor) {
+ const item = {
+ year: int(match, cursor),
+ month: int(match, cursor + 1, 1),
+ day: int(match, cursor + 2, 1),
+ };
+
+ return [item, null, cursor + 3];
+}
+
+function extractISOTime(match, cursor) {
+ const item = {
+ hours: int(match, cursor, 0),
+ minutes: int(match, cursor + 1, 0),
+ seconds: int(match, cursor + 2, 0),
+ milliseconds: parseMillis(match[cursor + 3]),
+ };
+
+ return [item, null, cursor + 4];
+}
+
+function extractISOOffset(match, cursor) {
+ const local = !match[cursor] && !match[cursor + 1],
+ fullOffset = signedOffset(match[cursor + 1], match[cursor + 2]),
+ zone = local ? null : FixedOffsetZone.instance(fullOffset);
+ return [{}, zone, cursor + 3];
+}
+
+function extractIANAZone(match, cursor) {
+ const zone = match[cursor] ? IANAZone.create(match[cursor]) : null;
+ return [{}, zone, cursor + 1];
+}
+
+// ISO time parsing
+
+const isoTimeOnly = RegExp(`^T?${isoTimeBaseRegex.source}$`);
+
+// ISO duration parsing
+
+const isoDuration =
+ /^-?P(?:(?:(-?\d{1,20}(?:\.\d{1,20})?)Y)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20}(?:\.\d{1,20})?)W)?(?:(-?\d{1,20}(?:\.\d{1,20})?)D)?(?:T(?:(-?\d{1,20}(?:\.\d{1,20})?)H)?(?:(-?\d{1,20}(?:\.\d{1,20})?)M)?(?:(-?\d{1,20})(?:[.,](-?\d{1,20}))?S)?)?)$/;
+
+function extractISODuration(match) {
+ const [s, yearStr, monthStr, weekStr, dayStr, hourStr, minuteStr, secondStr, millisecondsStr] =
+ match;
+
+ const hasNegativePrefix = s[0] === "-";
+ const negativeSeconds = secondStr && secondStr[0] === "-";
+
+ const maybeNegate = (num, force = false) =>
+ num !== undefined && (force || (num && hasNegativePrefix)) ? -num : num;
+
+ return [
+ {
+ years: maybeNegate(parseFloating(yearStr)),
+ months: maybeNegate(parseFloating(monthStr)),
+ weeks: maybeNegate(parseFloating(weekStr)),
+ days: maybeNegate(parseFloating(dayStr)),
+ hours: maybeNegate(parseFloating(hourStr)),
+ minutes: maybeNegate(parseFloating(minuteStr)),
+ seconds: maybeNegate(parseFloating(secondStr), secondStr === "-0"),
+ milliseconds: maybeNegate(parseMillis(millisecondsStr), negativeSeconds),
+ },
+ ];
+}
+
+// These are a little braindead. EDT *should* tell us that we're in, say, America/New_York
+// and not just that we're in -240 *right now*. But since I don't think these are used that often
+// I'm just going to ignore that
+const obsOffsets = {
+ GMT: 0,
+ EDT: -4 * 60,
+ EST: -5 * 60,
+ CDT: -5 * 60,
+ CST: -6 * 60,
+ MDT: -6 * 60,
+ MST: -7 * 60,
+ PDT: -7 * 60,
+ PST: -8 * 60,
+};
+
+function fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
+ const result = {
+ year: yearStr.length === 2 ? untruncateYear(parseInteger(yearStr)) : parseInteger(yearStr),
+ month: monthsShort.indexOf(monthStr) + 1,
+ day: parseInteger(dayStr),
+ hour: parseInteger(hourStr),
+ minute: parseInteger(minuteStr),
+ };
+
+ if (secondStr) result.second = parseInteger(secondStr);
+ if (weekdayStr) {
+ result.weekday =
+ weekdayStr.length > 3
+ ? weekdaysLong.indexOf(weekdayStr) + 1
+ : weekdaysShort.indexOf(weekdayStr) + 1;
+ }
+
+ return result;
+}
+
+// RFC 2822/5322
+const rfc2822 =
+ /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/;
+
+function extractRFC2822(match) {
+ const [
+ ,
+ weekdayStr,
+ dayStr,
+ monthStr,
+ yearStr,
+ hourStr,
+ minuteStr,
+ secondStr,
+ obsOffset,
+ milOffset,
+ offHourStr,
+ offMinuteStr,
+ ] = match,
+ result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);
+
+ let offset;
+ if (obsOffset) {
+ offset = obsOffsets[obsOffset];
+ } else if (milOffset) {
+ offset = 0;
+ } else {
+ offset = signedOffset(offHourStr, offMinuteStr);
+ }
+
+ return [result, new FixedOffsetZone(offset)];
+}
+
+function preprocessRFC2822(s) {
+ // Remove comments and folding whitespace and replace multiple-spaces with a single space
+ return s
+ .replace(/\([^()]*\)|[\n\t]/g, " ")
+ .replace(/(\s\s+)/g, " ")
+ .trim();
+}
+
+// http date
+
+const rfc1123 =
+ /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/,
+ rfc850 =
+ /^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/,
+ ascii =
+ /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/;
+
+function extractRFC1123Or850(match) {
+ const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr] = match,
+ result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);
+ return [result, FixedOffsetZone.utcInstance];
+}
+
+function extractASCII(match) {
+ const [, weekdayStr, monthStr, dayStr, hourStr, minuteStr, secondStr, yearStr] = match,
+ result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);
+ return [result, FixedOffsetZone.utcInstance];
+}
+
+const isoYmdWithTimeExtensionRegex = combineRegexes(isoYmdRegex, isoTimeExtensionRegex);
+const isoWeekWithTimeExtensionRegex = combineRegexes(isoWeekRegex, isoTimeExtensionRegex);
+const isoOrdinalWithTimeExtensionRegex = combineRegexes(isoOrdinalRegex, isoTimeExtensionRegex);
+const isoTimeCombinedRegex = combineRegexes(isoTimeRegex);
+
+const extractISOYmdTimeAndOffset = combineExtractors(
+ extractISOYmd,
+ extractISOTime,
+ extractISOOffset,
+ extractIANAZone
+);
+const extractISOWeekTimeAndOffset = combineExtractors(
+ extractISOWeekData,
+ extractISOTime,
+ extractISOOffset,
+ extractIANAZone
+);
+const extractISOOrdinalDateAndTime = combineExtractors(
+ extractISOOrdinalData,
+ extractISOTime,
+ extractISOOffset,
+ extractIANAZone
+);
+const extractISOTimeAndOffset = combineExtractors(
+ extractISOTime,
+ extractISOOffset,
+ extractIANAZone
+);
+
+/*
+ * @private
+ */
+
+function parseISODate(s) {
+ return parse(
+ s,
+ [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],
+ [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset],
+ [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime],
+ [isoTimeCombinedRegex, extractISOTimeAndOffset]
+ );
+}
+
+function parseRFC2822Date(s) {
+ return parse(preprocessRFC2822(s), [rfc2822, extractRFC2822]);
+}
+
+function parseHTTPDate(s) {
+ return parse(
+ s,
+ [rfc1123, extractRFC1123Or850],
+ [rfc850, extractRFC1123Or850],
+ [ascii, extractASCII]
+ );
+}
+
+function parseISODuration(s) {
+ return parse(s, [isoDuration, extractISODuration]);
+}
+
+const extractISOTimeOnly = combineExtractors(extractISOTime);
+
+function parseISOTimeOnly(s) {
+ return parse(s, [isoTimeOnly, extractISOTimeOnly]);
+}
+
+const sqlYmdWithTimeExtensionRegex = combineRegexes(sqlYmdRegex, sqlTimeExtensionRegex);
+const sqlTimeCombinedRegex = combineRegexes(sqlTimeRegex);
+
+const extractISOTimeOffsetAndIANAZone = combineExtractors(
+ extractISOTime,
+ extractISOOffset,
+ extractIANAZone
+);
+
+function parseSQL(s) {
+ return parse(
+ s,
+ [sqlYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],
+ [sqlTimeCombinedRegex, extractISOTimeOffsetAndIANAZone]
+ );
+}
+
+const INVALID$2 = "Invalid Duration";
+
+// unit conversion constants
+const lowOrderMatrix = {
+ weeks: {
+ days: 7,
+ hours: 7 * 24,
+ minutes: 7 * 24 * 60,
+ seconds: 7 * 24 * 60 * 60,
+ milliseconds: 7 * 24 * 60 * 60 * 1000,
+ },
+ days: {
+ hours: 24,
+ minutes: 24 * 60,
+ seconds: 24 * 60 * 60,
+ milliseconds: 24 * 60 * 60 * 1000,
+ },
+ hours: { minutes: 60, seconds: 60 * 60, milliseconds: 60 * 60 * 1000 },
+ minutes: { seconds: 60, milliseconds: 60 * 1000 },
+ seconds: { milliseconds: 1000 },
+ },
+ casualMatrix = {
+ years: {
+ quarters: 4,
+ months: 12,
+ weeks: 52,
+ days: 365,
+ hours: 365 * 24,
+ minutes: 365 * 24 * 60,
+ seconds: 365 * 24 * 60 * 60,
+ milliseconds: 365 * 24 * 60 * 60 * 1000,
+ },
+ quarters: {
+ months: 3,
+ weeks: 13,
+ days: 91,
+ hours: 91 * 24,
+ minutes: 91 * 24 * 60,
+ seconds: 91 * 24 * 60 * 60,
+ milliseconds: 91 * 24 * 60 * 60 * 1000,
+ },
+ months: {
+ weeks: 4,
+ days: 30,
+ hours: 30 * 24,
+ minutes: 30 * 24 * 60,
+ seconds: 30 * 24 * 60 * 60,
+ milliseconds: 30 * 24 * 60 * 60 * 1000,
+ },
+
+ ...lowOrderMatrix,
+ },
+ daysInYearAccurate = 146097.0 / 400,
+ daysInMonthAccurate = 146097.0 / 4800,
+ accurateMatrix = {
+ years: {
+ quarters: 4,
+ months: 12,
+ weeks: daysInYearAccurate / 7,
+ days: daysInYearAccurate,
+ hours: daysInYearAccurate * 24,
+ minutes: daysInYearAccurate * 24 * 60,
+ seconds: daysInYearAccurate * 24 * 60 * 60,
+ milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1000,
+ },
+ quarters: {
+ months: 3,
+ weeks: daysInYearAccurate / 28,
+ days: daysInYearAccurate / 4,
+ hours: (daysInYearAccurate * 24) / 4,
+ minutes: (daysInYearAccurate * 24 * 60) / 4,
+ seconds: (daysInYearAccurate * 24 * 60 * 60) / 4,
+ milliseconds: (daysInYearAccurate * 24 * 60 * 60 * 1000) / 4,
+ },
+ months: {
+ weeks: daysInMonthAccurate / 7,
+ days: daysInMonthAccurate,
+ hours: daysInMonthAccurate * 24,
+ minutes: daysInMonthAccurate * 24 * 60,
+ seconds: daysInMonthAccurate * 24 * 60 * 60,
+ milliseconds: daysInMonthAccurate * 24 * 60 * 60 * 1000,
+ },
+ ...lowOrderMatrix,
+ };
+
+// units ordered by size
+const orderedUnits$1 = [
+ "years",
+ "quarters",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+];
+
+const reverseUnits = orderedUnits$1.slice(0).reverse();
+
+// clone really means "create another instance just like this one, but with these changes"
+function clone$1(dur, alts, clear = false) {
+ // deep merge for vals
+ const conf = {
+ values: clear ? alts.values : { ...dur.values, ...(alts.values || {}) },
+ loc: dur.loc.clone(alts.loc),
+ conversionAccuracy: alts.conversionAccuracy || dur.conversionAccuracy,
+ matrix: alts.matrix || dur.matrix,
+ };
+ return new Duration(conf);
+}
+
+function durationToMillis(matrix, vals) {
+ let sum = vals.milliseconds ?? 0;
+ for (const unit of reverseUnits.slice(1)) {
+ if (vals[unit]) {
+ sum += vals[unit] * matrix[unit]["milliseconds"];
+ }
+ }
+ return sum;
+}
+
+// NB: mutates parameters
+function normalizeValues(matrix, vals) {
+ // the logic below assumes the overall value of the duration is positive
+ // if this is not the case, factor is used to make it so
+ const factor = durationToMillis(matrix, vals) < 0 ? -1 : 1;
+
+ orderedUnits$1.reduceRight((previous, current) => {
+ if (!isUndefined(vals[current])) {
+ if (previous) {
+ const previousVal = vals[previous] * factor;
+ const conv = matrix[current][previous];
+
+ // if (previousVal < 0):
+ // lower order unit is negative (e.g. { years: 2, days: -2 })
+ // normalize this by reducing the higher order unit by the appropriate amount
+ // and increasing the lower order unit
+ // this can never make the higher order unit negative, because this function only operates
+ // on positive durations, so the amount of time represented by the lower order unit cannot
+ // be larger than the higher order unit
+ // else:
+ // lower order unit is positive (e.g. { years: 2, days: 450 } or { years: -2, days: 450 })
+ // in this case we attempt to convert as much as possible from the lower order unit into
+ // the higher order one
+ //
+ // Math.floor takes care of both of these cases, rounding away from 0
+ // if previousVal < 0 it makes the absolute value larger
+ // if previousVal >= it makes the absolute value smaller
+ const rollUp = Math.floor(previousVal / conv);
+ vals[current] += rollUp * factor;
+ vals[previous] -= rollUp * conv * factor;
+ }
+ return current;
+ } else {
+ return previous;
+ }
+ }, null);
+
+ // try to convert any decimals into smaller units if possible
+ // for example for { years: 2.5, days: 0, seconds: 0 } we want to get { years: 2, days: 182, hours: 12 }
+ orderedUnits$1.reduce((previous, current) => {
+ if (!isUndefined(vals[current])) {
+ if (previous) {
+ const fraction = vals[previous] % 1;
+ vals[previous] -= fraction;
+ vals[current] += fraction * matrix[previous][current];
+ }
+ return current;
+ } else {
+ return previous;
+ }
+ }, null);
+}
+
+// Remove all properties with a value of 0 from an object
+function removeZeroes(vals) {
+ const newVals = {};
+ for (const [key, value] of Object.entries(vals)) {
+ if (value !== 0) {
+ newVals[key] = value;
+ }
+ }
+ return newVals;
+}
+
+/**
+ * A Duration object represents a period of time, like "2 months" or "1 day, 1 hour". Conceptually, it's just a map of units to their quantities, accompanied by some additional configuration and methods for creating, parsing, interrogating, transforming, and formatting them. They can be used on their own or in conjunction with other Luxon types; for example, you can use {@link DateTime#plus} to add a Duration object to a DateTime, producing another DateTime.
+ *
+ * Here is a brief overview of commonly used methods and getters in Duration:
+ *
+ * * **Creation** To create a Duration, use {@link Duration.fromMillis}, {@link Duration.fromObject}, or {@link Duration.fromISO}.
+ * * **Unit values** See the {@link Duration#years}, {@link Duration#months}, {@link Duration#weeks}, {@link Duration#days}, {@link Duration#hours}, {@link Duration#minutes}, {@link Duration#seconds}, {@link Duration#milliseconds} accessors.
+ * * **Configuration** See {@link Duration#locale} and {@link Duration#numberingSystem} accessors.
+ * * **Transformation** To create new Durations out of old ones use {@link Duration#plus}, {@link Duration#minus}, {@link Duration#normalize}, {@link Duration#set}, {@link Duration#reconfigure}, {@link Duration#shiftTo}, and {@link Duration#negate}.
+ * * **Output** To convert the Duration into other representations, see {@link Duration#as}, {@link Duration#toISO}, {@link Duration#toFormat}, and {@link Duration#toJSON}
+ *
+ * There's are more methods documented below. In addition, for more information on subtler topics like internationalization and validity, see the external documentation.
+ */
+class Duration {
+ /**
+ * @private
+ */
+ constructor(config) {
+ const accurate = config.conversionAccuracy === "longterm" || false;
+ let matrix = accurate ? accurateMatrix : casualMatrix;
+
+ if (config.matrix) {
+ matrix = config.matrix;
+ }
+
+ /**
+ * @access private
+ */
+ this.values = config.values;
+ /**
+ * @access private
+ */
+ this.loc = config.loc || Locale.create();
+ /**
+ * @access private
+ */
+ this.conversionAccuracy = accurate ? "longterm" : "casual";
+ /**
+ * @access private
+ */
+ this.invalid = config.invalid || null;
+ /**
+ * @access private
+ */
+ this.matrix = matrix;
+ /**
+ * @access private
+ */
+ this.isLuxonDuration = true;
+ }
+
+ /**
+ * Create Duration from a number of milliseconds.
+ * @param {number} count of milliseconds
+ * @param {Object} opts - options for parsing
+ * @param {string} [opts.locale='en-US'] - the locale to use
+ * @param {string} opts.numberingSystem - the numbering system to use
+ * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
+ * @return {Duration}
+ */
+ static fromMillis(count, opts) {
+ return Duration.fromObject({ milliseconds: count }, opts);
+ }
+
+ /**
+ * Create a Duration from a JavaScript object with keys like 'years' and 'hours'.
+ * If this object is empty then a zero milliseconds duration is returned.
+ * @param {Object} obj - the object to create the DateTime from
+ * @param {number} obj.years
+ * @param {number} obj.quarters
+ * @param {number} obj.months
+ * @param {number} obj.weeks
+ * @param {number} obj.days
+ * @param {number} obj.hours
+ * @param {number} obj.minutes
+ * @param {number} obj.seconds
+ * @param {number} obj.milliseconds
+ * @param {Object} [opts=[]] - options for creating this Duration
+ * @param {string} [opts.locale='en-US'] - the locale to use
+ * @param {string} opts.numberingSystem - the numbering system to use
+ * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use
+ * @param {string} [opts.matrix=Object] - the custom conversion system to use
+ * @return {Duration}
+ */
+ static fromObject(obj, opts = {}) {
+ if (obj == null || typeof obj !== "object") {
+ throw new InvalidArgumentError(
+ `Duration.fromObject: argument expected to be an object, got ${
+ obj === null ? "null" : typeof obj
+ }`
+ );
+ }
+
+ return new Duration({
+ values: normalizeObject(obj, Duration.normalizeUnit),
+ loc: Locale.fromObject(opts),
+ conversionAccuracy: opts.conversionAccuracy,
+ matrix: opts.matrix,
+ });
+ }
+
+ /**
+ * Create a Duration from DurationLike.
+ *
+ * @param {Object | number | Duration} durationLike
+ * One of:
+ * - object with keys like 'years' and 'hours'.
+ * - number representing milliseconds
+ * - Duration instance
+ * @return {Duration}
+ */
+ static fromDurationLike(durationLike) {
+ if (isNumber(durationLike)) {
+ return Duration.fromMillis(durationLike);
+ } else if (Duration.isDuration(durationLike)) {
+ return durationLike;
+ } else if (typeof durationLike === "object") {
+ return Duration.fromObject(durationLike);
+ } else {
+ throw new InvalidArgumentError(
+ `Unknown duration argument ${durationLike} of type ${typeof durationLike}`
+ );
+ }
+ }
+
+ /**
+ * Create a Duration from an ISO 8601 duration string.
+ * @param {string} text - text to parse
+ * @param {Object} opts - options for parsing
+ * @param {string} [opts.locale='en-US'] - the locale to use
+ * @param {string} opts.numberingSystem - the numbering system to use
+ * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use
+ * @param {string} [opts.matrix=Object] - the preset conversion system to use
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Durations
+ * @example Duration.fromISO('P3Y6M1W4DT12H30M5S').toObject() //=> { years: 3, months: 6, weeks: 1, days: 4, hours: 12, minutes: 30, seconds: 5 }
+ * @example Duration.fromISO('PT23H').toObject() //=> { hours: 23 }
+ * @example Duration.fromISO('P5Y3M').toObject() //=> { years: 5, months: 3 }
+ * @return {Duration}
+ */
+ static fromISO(text, opts) {
+ const [parsed] = parseISODuration(text);
+ if (parsed) {
+ return Duration.fromObject(parsed, opts);
+ } else {
+ return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
+ }
+ }
+
+ /**
+ * Create a Duration from an ISO 8601 time string.
+ * @param {string} text - text to parse
+ * @param {Object} opts - options for parsing
+ * @param {string} [opts.locale='en-US'] - the locale to use
+ * @param {string} opts.numberingSystem - the numbering system to use
+ * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use
+ * @param {string} [opts.matrix=Object] - the conversion system to use
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Times
+ * @example Duration.fromISOTime('11:22:33.444').toObject() //=> { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 }
+ * @example Duration.fromISOTime('11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
+ * @example Duration.fromISOTime('T11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
+ * @example Duration.fromISOTime('1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
+ * @example Duration.fromISOTime('T1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }
+ * @return {Duration}
+ */
+ static fromISOTime(text, opts) {
+ const [parsed] = parseISOTimeOnly(text);
+ if (parsed) {
+ return Duration.fromObject(parsed, opts);
+ } else {
+ return Duration.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
+ }
+ }
+
+ /**
+ * Create an invalid Duration.
+ * @param {string} reason - simple string of why this datetime is invalid. Should not contain parameters or anything else data-dependent
+ * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
+ * @return {Duration}
+ */
+ static invalid(reason, explanation = null) {
+ if (!reason) {
+ throw new InvalidArgumentError("need to specify a reason the Duration is invalid");
+ }
+
+ const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
+
+ if (Settings.throwOnInvalid) {
+ throw new InvalidDurationError(invalid);
+ } else {
+ return new Duration({ invalid });
+ }
+ }
+
+ /**
+ * @private
+ */
+ static normalizeUnit(unit) {
+ const normalized = {
+ year: "years",
+ years: "years",
+ quarter: "quarters",
+ quarters: "quarters",
+ month: "months",
+ months: "months",
+ week: "weeks",
+ weeks: "weeks",
+ day: "days",
+ days: "days",
+ hour: "hours",
+ hours: "hours",
+ minute: "minutes",
+ minutes: "minutes",
+ second: "seconds",
+ seconds: "seconds",
+ millisecond: "milliseconds",
+ milliseconds: "milliseconds",
+ }[unit ? unit.toLowerCase() : unit];
+
+ if (!normalized) throw new InvalidUnitError(unit);
+
+ return normalized;
+ }
+
+ /**
+ * Check if an object is a Duration. Works across context boundaries
+ * @param {object} o
+ * @return {boolean}
+ */
+ static isDuration(o) {
+ return (o && o.isLuxonDuration) || false;
+ }
+
+ /**
+ * Get the locale of a Duration, such 'en-GB'
+ * @type {string}
+ */
+ get locale() {
+ return this.isValid ? this.loc.locale : null;
+ }
+
+ /**
+ * Get the numbering system of a Duration, such 'beng'. The numbering system is used when formatting the Duration
+ *
+ * @type {string}
+ */
+ get numberingSystem() {
+ return this.isValid ? this.loc.numberingSystem : null;
+ }
+
+ /**
+ * Returns a string representation of this Duration formatted according to the specified format string. You may use these tokens:
+ * * `S` for milliseconds
+ * * `s` for seconds
+ * * `m` for minutes
+ * * `h` for hours
+ * * `d` for days
+ * * `w` for weeks
+ * * `M` for months
+ * * `y` for years
+ * Notes:
+ * * Add padding by repeating the token, e.g. "yy" pads the years to two digits, "hhhh" pads the hours out to four digits
+ * * Tokens can be escaped by wrapping with single quotes.
+ * * The duration will be converted to the set of units in the format string using {@link Duration#shiftTo} and the Durations's conversion accuracy setting.
+ * @param {string} fmt - the format string
+ * @param {Object} opts - options
+ * @param {boolean} [opts.floor=true] - floor numerical values
+ * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2"
+ * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002"
+ * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000"
+ * @return {string}
+ */
+ toFormat(fmt, opts = {}) {
+ // reverse-compat since 1.2; we always round down now, never up, and we do it by default
+ const fmtOpts = {
+ ...opts,
+ floor: opts.round !== false && opts.floor !== false,
+ };
+ return this.isValid
+ ? Formatter.create(this.loc, fmtOpts).formatDurationFromString(this, fmt)
+ : INVALID$2;
+ }
+
+ /**
+ * Returns a string representation of a Duration with all units included.
+ * To modify its behavior use the `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant.
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
+ * @param opts - On option object to override the formatting. Accepts the same keys as the options parameter of the native `Int.NumberFormat` constructor, as well as `listStyle`.
+ * @example
+ * ```js
+ * var dur = Duration.fromObject({ days: 1, hours: 5, minutes: 6 })
+ * dur.toHuman() //=> '1 day, 5 hours, 6 minutes'
+ * dur.toHuman({ listStyle: "long" }) //=> '1 day, 5 hours, and 6 minutes'
+ * dur.toHuman({ unitDisplay: "short" }) //=> '1 day, 5 hr, 6 min'
+ * ```
+ */
+ toHuman(opts = {}) {
+ if (!this.isValid) return INVALID$2;
+
+ const l = orderedUnits$1
+ .map((unit) => {
+ const val = this.values[unit];
+ if (isUndefined(val)) {
+ return null;
+ }
+ return this.loc
+ .numberFormatter({ style: "unit", unitDisplay: "long", ...opts, unit: unit.slice(0, -1) })
+ .format(val);
+ })
+ .filter((n) => n);
+
+ return this.loc
+ .listFormatter({ type: "conjunction", style: opts.listStyle || "narrow", ...opts })
+ .format(l);
+ }
+
+ /**
+ * Returns a JavaScript object with this Duration's values.
+ * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 }
+ * @return {Object}
+ */
+ toObject() {
+ if (!this.isValid) return {};
+ return { ...this.values };
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this Duration.
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Durations
+ * @example Duration.fromObject({ years: 3, seconds: 45 }).toISO() //=> 'P3YT45S'
+ * @example Duration.fromObject({ months: 4, seconds: 45 }).toISO() //=> 'P4MT45S'
+ * @example Duration.fromObject({ months: 5 }).toISO() //=> 'P5M'
+ * @example Duration.fromObject({ minutes: 5 }).toISO() //=> 'PT5M'
+ * @example Duration.fromObject({ milliseconds: 6 }).toISO() //=> 'PT0.006S'
+ * @return {string}
+ */
+ toISO() {
+ // we could use the formatter, but this is an easier way to get the minimum string
+ if (!this.isValid) return null;
+
+ let s = "P";
+ if (this.years !== 0) s += this.years + "Y";
+ if (this.months !== 0 || this.quarters !== 0) s += this.months + this.quarters * 3 + "M";
+ if (this.weeks !== 0) s += this.weeks + "W";
+ if (this.days !== 0) s += this.days + "D";
+ if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0)
+ s += "T";
+ if (this.hours !== 0) s += this.hours + "H";
+ if (this.minutes !== 0) s += this.minutes + "M";
+ if (this.seconds !== 0 || this.milliseconds !== 0)
+ // this will handle "floating point madness" by removing extra decimal places
+ // https://stackoverflow.com/questions/588004/is-floating-point-math-broken
+ s += roundTo(this.seconds + this.milliseconds / 1000, 3) + "S";
+ if (s === "P") s += "T0S";
+ return s;
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this Duration, formatted as a time of day.
+ * Note that this will return null if the duration is invalid, negative, or equal to or greater than 24 hours.
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Times
+ * @param {Object} opts - options
+ * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
+ * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
+ * @param {boolean} [opts.includePrefix=false] - include the `T` prefix
+ * @param {string} [opts.format='extended'] - choose between the basic and extended format
+ * @example Duration.fromObject({ hours: 11 }).toISOTime() //=> '11:00:00.000'
+ * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressMilliseconds: true }) //=> '11:00:00'
+ * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressSeconds: true }) //=> '11:00'
+ * @example Duration.fromObject({ hours: 11 }).toISOTime({ includePrefix: true }) //=> 'T11:00:00.000'
+ * @example Duration.fromObject({ hours: 11 }).toISOTime({ format: 'basic' }) //=> '110000.000'
+ * @return {string}
+ */
+ toISOTime(opts = {}) {
+ if (!this.isValid) return null;
+
+ const millis = this.toMillis();
+ if (millis < 0 || millis >= 86400000) return null;
+
+ opts = {
+ suppressMilliseconds: false,
+ suppressSeconds: false,
+ includePrefix: false,
+ format: "extended",
+ ...opts,
+ includeOffset: false,
+ };
+
+ const dateTime = DateTime.fromMillis(millis, { zone: "UTC" });
+ return dateTime.toISOTime(opts);
+ }
+
+ /**
+ * Returns an ISO 8601 representation of this Duration appropriate for use in JSON.
+ * @return {string}
+ */
+ toJSON() {
+ return this.toISO();
+ }
+
+ /**
+ * Returns an ISO 8601 representation of this Duration appropriate for use in debugging.
+ * @return {string}
+ */
+ toString() {
+ return this.toISO();
+ }
+
+ /**
+ * Returns an milliseconds value of this Duration.
+ * @return {number}
+ */
+ toMillis() {
+ if (!this.isValid) return NaN;
+
+ return durationToMillis(this.matrix, this.values);
+ }
+
+ /**
+ * Returns an milliseconds value of this Duration. Alias of {@link toMillis}
+ * @return {number}
+ */
+ valueOf() {
+ return this.toMillis();
+ }
+
+ /**
+ * Make this Duration longer by the specified amount. Return a newly-constructed Duration.
+ * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
+ * @return {Duration}
+ */
+ plus(duration) {
+ if (!this.isValid) return this;
+
+ const dur = Duration.fromDurationLike(duration),
+ result = {};
+
+ for (const k of orderedUnits$1) {
+ if (hasOwnProperty(dur.values, k) || hasOwnProperty(this.values, k)) {
+ result[k] = dur.get(k) + this.get(k);
+ }
+ }
+
+ return clone$1(this, { values: result }, true);
+ }
+
+ /**
+ * Make this Duration shorter by the specified amount. Return a newly-constructed Duration.
+ * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
+ * @return {Duration}
+ */
+ minus(duration) {
+ if (!this.isValid) return this;
+
+ const dur = Duration.fromDurationLike(duration);
+ return this.plus(dur.negate());
+ }
+
+ /**
+ * Scale this Duration by the specified amount. Return a newly-constructed Duration.
+ * @param {function} fn - The function to apply to each unit. Arity is 1 or 2: the value of the unit and, optionally, the unit name. Must return a number.
+ * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits(x => x * 2) //=> { hours: 2, minutes: 60 }
+ * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits((x, u) => u === "hours" ? x * 2 : x) //=> { hours: 2, minutes: 30 }
+ * @return {Duration}
+ */
+ mapUnits(fn) {
+ if (!this.isValid) return this;
+ const result = {};
+ for (const k of Object.keys(this.values)) {
+ result[k] = asNumber(fn(this.values[k], k));
+ }
+ return clone$1(this, { values: result }, true);
+ }
+
+ /**
+ * Get the value of unit.
+ * @param {string} unit - a unit such as 'minute' or 'day'
+ * @example Duration.fromObject({years: 2, days: 3}).get('years') //=> 2
+ * @example Duration.fromObject({years: 2, days: 3}).get('months') //=> 0
+ * @example Duration.fromObject({years: 2, days: 3}).get('days') //=> 3
+ * @return {number}
+ */
+ get(unit) {
+ return this[Duration.normalizeUnit(unit)];
+ }
+
+ /**
+ * "Set" the values of specified units. Return a newly-constructed Duration.
+ * @param {Object} values - a mapping of units to numbers
+ * @example dur.set({ years: 2017 })
+ * @example dur.set({ hours: 8, minutes: 30 })
+ * @return {Duration}
+ */
+ set(values) {
+ if (!this.isValid) return this;
+
+ const mixed = { ...this.values, ...normalizeObject(values, Duration.normalizeUnit) };
+ return clone$1(this, { values: mixed });
+ }
+
+ /**
+ * "Set" the locale and/or numberingSystem. Returns a newly-constructed Duration.
+ * @example dur.reconfigure({ locale: 'en-GB' })
+ * @return {Duration}
+ */
+ reconfigure({ locale, numberingSystem, conversionAccuracy, matrix } = {}) {
+ const loc = this.loc.clone({ locale, numberingSystem });
+ const opts = { loc, matrix, conversionAccuracy };
+ return clone$1(this, opts);
+ }
+
+ /**
+ * Return the length of the duration in the specified unit.
+ * @param {string} unit - a unit such as 'minutes' or 'days'
+ * @example Duration.fromObject({years: 1}).as('days') //=> 365
+ * @example Duration.fromObject({years: 1}).as('months') //=> 12
+ * @example Duration.fromObject({hours: 60}).as('days') //=> 2.5
+ * @return {number}
+ */
+ as(unit) {
+ return this.isValid ? this.shiftTo(unit).get(unit) : NaN;
+ }
+
+ /**
+ * Reduce this Duration to its canonical representation in its current units.
+ * Assuming the overall value of the Duration is positive, this means:
+ * - excessive values for lower-order units are converted to higher-order units (if possible, see first and second example)
+ * - negative lower-order units are converted to higher order units (there must be such a higher order unit, otherwise
+ * the overall value would be negative, see second example)
+ * - fractional values for higher-order units are converted to lower-order units (if possible, see fourth example)
+ *
+ * If the overall value is negative, the result of this method is equivalent to `this.negate().normalize().negate()`.
+ * @example Duration.fromObject({ years: 2, days: 5000 }).normalize().toObject() //=> { years: 15, days: 255 }
+ * @example Duration.fromObject({ days: 5000 }).normalize().toObject() //=> { days: 5000 }
+ * @example Duration.fromObject({ hours: 12, minutes: -45 }).normalize().toObject() //=> { hours: 11, minutes: 15 }
+ * @example Duration.fromObject({ years: 2.5, days: 0, hours: 0 }).normalize().toObject() //=> { years: 2, days: 182, hours: 12 }
+ * @return {Duration}
+ */
+ normalize() {
+ if (!this.isValid) return this;
+ const vals = this.toObject();
+ normalizeValues(this.matrix, vals);
+ return clone$1(this, { values: vals }, true);
+ }
+
+ /**
+ * Rescale units to its largest representation
+ * @example Duration.fromObject({ milliseconds: 90000 }).rescale().toObject() //=> { minutes: 1, seconds: 30 }
+ * @return {Duration}
+ */
+ rescale() {
+ if (!this.isValid) return this;
+ const vals = removeZeroes(this.normalize().shiftToAll().toObject());
+ return clone$1(this, { values: vals }, true);
+ }
+
+ /**
+ * Convert this Duration into its representation in a different set of units.
+ * @example Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo('minutes', 'milliseconds').toObject() //=> { minutes: 60, milliseconds: 30000 }
+ * @return {Duration}
+ */
+ shiftTo(...units) {
+ if (!this.isValid) return this;
+
+ if (units.length === 0) {
+ return this;
+ }
+
+ units = units.map((u) => Duration.normalizeUnit(u));
+
+ const built = {},
+ accumulated = {},
+ vals = this.toObject();
+ let lastUnit;
+
+ for (const k of orderedUnits$1) {
+ if (units.indexOf(k) >= 0) {
+ lastUnit = k;
+
+ let own = 0;
+
+ // anything we haven't boiled down yet should get boiled to this unit
+ for (const ak in accumulated) {
+ own += this.matrix[ak][k] * accumulated[ak];
+ accumulated[ak] = 0;
+ }
+
+ // plus anything that's already in this unit
+ if (isNumber(vals[k])) {
+ own += vals[k];
+ }
+
+ // only keep the integer part for now in the hopes of putting any decimal part
+ // into a smaller unit later
+ const i = Math.trunc(own);
+ built[k] = i;
+ accumulated[k] = (own * 1000 - i * 1000) / 1000;
+
+ // otherwise, keep it in the wings to boil it later
+ } else if (isNumber(vals[k])) {
+ accumulated[k] = vals[k];
+ }
+ }
+
+ // anything leftover becomes the decimal for the last unit
+ // lastUnit must be defined since units is not empty
+ for (const key in accumulated) {
+ if (accumulated[key] !== 0) {
+ built[lastUnit] +=
+ key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key];
+ }
+ }
+
+ normalizeValues(this.matrix, built);
+ return clone$1(this, { values: built }, true);
+ }
+
+ /**
+ * Shift this Duration to all available units.
+ * Same as shiftTo("years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds")
+ * @return {Duration}
+ */
+ shiftToAll() {
+ if (!this.isValid) return this;
+ return this.shiftTo(
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds"
+ );
+ }
+
+ /**
+ * Return the negative of this Duration.
+ * @example Duration.fromObject({ hours: 1, seconds: 30 }).negate().toObject() //=> { hours: -1, seconds: -30 }
+ * @return {Duration}
+ */
+ negate() {
+ if (!this.isValid) return this;
+ const negated = {};
+ for (const k of Object.keys(this.values)) {
+ negated[k] = this.values[k] === 0 ? 0 : -this.values[k];
+ }
+ return clone$1(this, { values: negated }, true);
+ }
+
+ /**
+ * Get the years.
+ * @type {number}
+ */
+ get years() {
+ return this.isValid ? this.values.years || 0 : NaN;
+ }
+
+ /**
+ * Get the quarters.
+ * @type {number}
+ */
+ get quarters() {
+ return this.isValid ? this.values.quarters || 0 : NaN;
+ }
+
+ /**
+ * Get the months.
+ * @type {number}
+ */
+ get months() {
+ return this.isValid ? this.values.months || 0 : NaN;
+ }
+
+ /**
+ * Get the weeks
+ * @type {number}
+ */
+ get weeks() {
+ return this.isValid ? this.values.weeks || 0 : NaN;
+ }
+
+ /**
+ * Get the days.
+ * @type {number}
+ */
+ get days() {
+ return this.isValid ? this.values.days || 0 : NaN;
+ }
+
+ /**
+ * Get the hours.
+ * @type {number}
+ */
+ get hours() {
+ return this.isValid ? this.values.hours || 0 : NaN;
+ }
+
+ /**
+ * Get the minutes.
+ * @type {number}
+ */
+ get minutes() {
+ return this.isValid ? this.values.minutes || 0 : NaN;
+ }
+
+ /**
+ * Get the seconds.
+ * @return {number}
+ */
+ get seconds() {
+ return this.isValid ? this.values.seconds || 0 : NaN;
+ }
+
+ /**
+ * Get the milliseconds.
+ * @return {number}
+ */
+ get milliseconds() {
+ return this.isValid ? this.values.milliseconds || 0 : NaN;
+ }
+
+ /**
+ * Returns whether the Duration is invalid. Invalid durations are returned by diff operations
+ * on invalid DateTimes or Intervals.
+ * @return {boolean}
+ */
+ get isValid() {
+ return this.invalid === null;
+ }
+
+ /**
+ * Returns an error code if this Duration became invalid, or null if the Duration is valid
+ * @return {string}
+ */
+ get invalidReason() {
+ return this.invalid ? this.invalid.reason : null;
+ }
+
+ /**
+ * Returns an explanation of why this Duration became invalid, or null if the Duration is valid
+ * @type {string}
+ */
+ get invalidExplanation() {
+ return this.invalid ? this.invalid.explanation : null;
+ }
+
+ /**
+ * Equality check
+ * Two Durations are equal iff they have the same units and the same values for each unit.
+ * @param {Duration} other
+ * @return {boolean}
+ */
+ equals(other) {
+ if (!this.isValid || !other.isValid) {
+ return false;
+ }
+
+ if (!this.loc.equals(other.loc)) {
+ return false;
+ }
+
+ function eq(v1, v2) {
+ // Consider 0 and undefined as equal
+ if (v1 === undefined || v1 === 0) return v2 === undefined || v2 === 0;
+ return v1 === v2;
+ }
+
+ for (const u of orderedUnits$1) {
+ if (!eq(this.values[u], other.values[u])) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+const INVALID$1 = "Invalid Interval";
+
+// checks if the start is equal to or before the end
+function validateStartEnd(start, end) {
+ if (!start || !start.isValid) {
+ return Interval.invalid("missing or invalid start");
+ } else if (!end || !end.isValid) {
+ return Interval.invalid("missing or invalid end");
+ } else if (end < start) {
+ return Interval.invalid(
+ "end before start",
+ `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`
+ );
+ } else {
+ return null;
+ }
+}
+
+/**
+ * An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them.
+ *
+ * Here is a brief overview of the most commonly used methods and getters in Interval:
+ *
+ * * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}.
+ * * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end.
+ * * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame}, {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}.
+ * * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually}, {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}.
+ * * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs}
+ * * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate}, {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}.
+ */
+class Interval {
+ /**
+ * @private
+ */
+ constructor(config) {
+ /**
+ * @access private
+ */
+ this.s = config.start;
+ /**
+ * @access private
+ */
+ this.e = config.end;
+ /**
+ * @access private
+ */
+ this.invalid = config.invalid || null;
+ /**
+ * @access private
+ */
+ this.isLuxonInterval = true;
+ }
+
+ /**
+ * Create an invalid Interval.
+ * @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent
+ * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
+ * @return {Interval}
+ */
+ static invalid(reason, explanation = null) {
+ if (!reason) {
+ throw new InvalidArgumentError("need to specify a reason the Interval is invalid");
+ }
+
+ const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
+
+ if (Settings.throwOnInvalid) {
+ throw new InvalidIntervalError(invalid);
+ } else {
+ return new Interval({ invalid });
+ }
+ }
+
+ /**
+ * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.
+ * @param {DateTime|Date|Object} start
+ * @param {DateTime|Date|Object} end
+ * @return {Interval}
+ */
+ static fromDateTimes(start, end) {
+ const builtStart = friendlyDateTime(start),
+ builtEnd = friendlyDateTime(end);
+
+ const validateError = validateStartEnd(builtStart, builtEnd);
+
+ if (validateError == null) {
+ return new Interval({
+ start: builtStart,
+ end: builtEnd,
+ });
+ } else {
+ return validateError;
+ }
+ }
+
+ /**
+ * Create an Interval from a start DateTime and a Duration to extend to.
+ * @param {DateTime|Date|Object} start
+ * @param {Duration|Object|number} duration - the length of the Interval.
+ * @return {Interval}
+ */
+ static after(start, duration) {
+ const dur = Duration.fromDurationLike(duration),
+ dt = friendlyDateTime(start);
+ return Interval.fromDateTimes(dt, dt.plus(dur));
+ }
+
+ /**
+ * Create an Interval from an end DateTime and a Duration to extend backwards to.
+ * @param {DateTime|Date|Object} end
+ * @param {Duration|Object|number} duration - the length of the Interval.
+ * @return {Interval}
+ */
+ static before(end, duration) {
+ const dur = Duration.fromDurationLike(duration),
+ dt = friendlyDateTime(end);
+ return Interval.fromDateTimes(dt.minus(dur), dt);
+ }
+
+ /**
+ * Create an Interval from an ISO 8601 string.
+ * Accepts `/`, `/`, and `/` formats.
+ * @param {string} text - the ISO string to parse
+ * @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO}
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
+ * @return {Interval}
+ */
+ static fromISO(text, opts) {
+ const [s, e] = (text || "").split("/", 2);
+ if (s && e) {
+ let start, startIsValid;
+ try {
+ start = DateTime.fromISO(s, opts);
+ startIsValid = start.isValid;
+ } catch (e) {
+ startIsValid = false;
+ }
+
+ let end, endIsValid;
+ try {
+ end = DateTime.fromISO(e, opts);
+ endIsValid = end.isValid;
+ } catch (e) {
+ endIsValid = false;
+ }
+
+ if (startIsValid && endIsValid) {
+ return Interval.fromDateTimes(start, end);
+ }
+
+ if (startIsValid) {
+ const dur = Duration.fromISO(e, opts);
+ if (dur.isValid) {
+ return Interval.after(start, dur);
+ }
+ } else if (endIsValid) {
+ const dur = Duration.fromISO(s, opts);
+ if (dur.isValid) {
+ return Interval.before(end, dur);
+ }
+ }
+ }
+ return Interval.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
+ }
+
+ /**
+ * Check if an object is an Interval. Works across context boundaries
+ * @param {object} o
+ * @return {boolean}
+ */
+ static isInterval(o) {
+ return (o && o.isLuxonInterval) || false;
+ }
+
+ /**
+ * Returns the start of the Interval
+ * @type {DateTime}
+ */
+ get start() {
+ return this.isValid ? this.s : null;
+ }
+
+ /**
+ * Returns the end of the Interval
+ * @type {DateTime}
+ */
+ get end() {
+ return this.isValid ? this.e : null;
+ }
+
+ /**
+ * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
+ * @type {boolean}
+ */
+ get isValid() {
+ return this.invalidReason === null;
+ }
+
+ /**
+ * Returns an error code if this Interval is invalid, or null if the Interval is valid
+ * @type {string}
+ */
+ get invalidReason() {
+ return this.invalid ? this.invalid.reason : null;
+ }
+
+ /**
+ * Returns an explanation of why this Interval became invalid, or null if the Interval is valid
+ * @type {string}
+ */
+ get invalidExplanation() {
+ return this.invalid ? this.invalid.explanation : null;
+ }
+
+ /**
+ * Returns the length of the Interval in the specified unit.
+ * @param {string} unit - the unit (such as 'hours' or 'days') to return the length in.
+ * @return {number}
+ */
+ length(unit = "milliseconds") {
+ return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN;
+ }
+
+ /**
+ * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.
+ * Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'
+ * asks 'what dates are included in this interval?', not 'how many days long is this interval?'
+ * @param {string} [unit='milliseconds'] - the unit of time to count.
+ * @return {number}
+ */
+ count(unit = "milliseconds") {
+ if (!this.isValid) return NaN;
+ const start = this.start.startOf(unit),
+ end = this.end.startOf(unit);
+ return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf());
+ }
+
+ /**
+ * Returns whether this Interval's start and end are both in the same unit of time
+ * @param {string} unit - the unit of time to check sameness on
+ * @return {boolean}
+ */
+ hasSame(unit) {
+ return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false;
+ }
+
+ /**
+ * Return whether this Interval has the same start and end DateTimes.
+ * @return {boolean}
+ */
+ isEmpty() {
+ return this.s.valueOf() === this.e.valueOf();
+ }
+
+ /**
+ * Return whether this Interval's start is after the specified DateTime.
+ * @param {DateTime} dateTime
+ * @return {boolean}
+ */
+ isAfter(dateTime) {
+ if (!this.isValid) return false;
+ return this.s > dateTime;
+ }
+
+ /**
+ * Return whether this Interval's end is before the specified DateTime.
+ * @param {DateTime} dateTime
+ * @return {boolean}
+ */
+ isBefore(dateTime) {
+ if (!this.isValid) return false;
+ return this.e <= dateTime;
+ }
+
+ /**
+ * Return whether this Interval contains the specified DateTime.
+ * @param {DateTime} dateTime
+ * @return {boolean}
+ */
+ contains(dateTime) {
+ if (!this.isValid) return false;
+ return this.s <= dateTime && this.e > dateTime;
+ }
+
+ /**
+ * "Sets" the start and/or end dates. Returns a newly-constructed Interval.
+ * @param {Object} values - the values to set
+ * @param {DateTime} values.start - the starting DateTime
+ * @param {DateTime} values.end - the ending DateTime
+ * @return {Interval}
+ */
+ set({ start, end } = {}) {
+ if (!this.isValid) return this;
+ return Interval.fromDateTimes(start || this.s, end || this.e);
+ }
+
+ /**
+ * Split this Interval at each of the specified DateTimes
+ * @param {...DateTime} dateTimes - the unit of time to count.
+ * @return {Array}
+ */
+ splitAt(...dateTimes) {
+ if (!this.isValid) return [];
+ const sorted = dateTimes
+ .map(friendlyDateTime)
+ .filter((d) => this.contains(d))
+ .sort(),
+ results = [];
+ let { s } = this,
+ i = 0;
+
+ while (s < this.e) {
+ const added = sorted[i] || this.e,
+ next = +added > +this.e ? this.e : added;
+ results.push(Interval.fromDateTimes(s, next));
+ s = next;
+ i += 1;
+ }
+
+ return results;
+ }
+
+ /**
+ * Split this Interval into smaller Intervals, each of the specified length.
+ * Left over time is grouped into a smaller interval
+ * @param {Duration|Object|number} duration - The length of each resulting interval.
+ * @return {Array}
+ */
+ splitBy(duration) {
+ const dur = Duration.fromDurationLike(duration);
+
+ if (!this.isValid || !dur.isValid || dur.as("milliseconds") === 0) {
+ return [];
+ }
+
+ let { s } = this,
+ idx = 1,
+ next;
+
+ const results = [];
+ while (s < this.e) {
+ const added = this.start.plus(dur.mapUnits((x) => x * idx));
+ next = +added > +this.e ? this.e : added;
+ results.push(Interval.fromDateTimes(s, next));
+ s = next;
+ idx += 1;
+ }
+
+ return results;
+ }
+
+ /**
+ * Split this Interval into the specified number of smaller intervals.
+ * @param {number} numberOfParts - The number of Intervals to divide the Interval into.
+ * @return {Array}
+ */
+ divideEqually(numberOfParts) {
+ if (!this.isValid) return [];
+ return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts);
+ }
+
+ /**
+ * Return whether this Interval overlaps with the specified Interval
+ * @param {Interval} other
+ * @return {boolean}
+ */
+ overlaps(other) {
+ return this.e > other.s && this.s < other.e;
+ }
+
+ /**
+ * Return whether this Interval's end is adjacent to the specified Interval's start.
+ * @param {Interval} other
+ * @return {boolean}
+ */
+ abutsStart(other) {
+ if (!this.isValid) return false;
+ return +this.e === +other.s;
+ }
+
+ /**
+ * Return whether this Interval's start is adjacent to the specified Interval's end.
+ * @param {Interval} other
+ * @return {boolean}
+ */
+ abutsEnd(other) {
+ if (!this.isValid) return false;
+ return +other.e === +this.s;
+ }
+
+ /**
+ * Return whether this Interval engulfs the start and end of the specified Interval.
+ * @param {Interval} other
+ * @return {boolean}
+ */
+ engulfs(other) {
+ if (!this.isValid) return false;
+ return this.s <= other.s && this.e >= other.e;
+ }
+
+ /**
+ * Return whether this Interval has the same start and end as the specified Interval.
+ * @param {Interval} other
+ * @return {boolean}
+ */
+ equals(other) {
+ if (!this.isValid || !other.isValid) {
+ return false;
+ }
+
+ return this.s.equals(other.s) && this.e.equals(other.e);
+ }
+
+ /**
+ * Return an Interval representing the intersection of this Interval and the specified Interval.
+ * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.
+ * Returns null if the intersection is empty, meaning, the intervals don't intersect.
+ * @param {Interval} other
+ * @return {Interval}
+ */
+ intersection(other) {
+ if (!this.isValid) return this;
+ const s = this.s > other.s ? this.s : other.s,
+ e = this.e < other.e ? this.e : other.e;
+
+ if (s >= e) {
+ return null;
+ } else {
+ return Interval.fromDateTimes(s, e);
+ }
+ }
+
+ /**
+ * Return an Interval representing the union of this Interval and the specified Interval.
+ * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.
+ * @param {Interval} other
+ * @return {Interval}
+ */
+ union(other) {
+ if (!this.isValid) return this;
+ const s = this.s < other.s ? this.s : other.s,
+ e = this.e > other.e ? this.e : other.e;
+ return Interval.fromDateTimes(s, e);
+ }
+
+ /**
+ * Merge an array of Intervals into a equivalent minimal set of Intervals.
+ * Combines overlapping and adjacent Intervals.
+ * @param {Array} intervals
+ * @return {Array}
+ */
+ static merge(intervals) {
+ const [found, final] = intervals
+ .sort((a, b) => a.s - b.s)
+ .reduce(
+ ([sofar, current], item) => {
+ if (!current) {
+ return [sofar, item];
+ } else if (current.overlaps(item) || current.abutsStart(item)) {
+ return [sofar, current.union(item)];
+ } else {
+ return [sofar.concat([current]), item];
+ }
+ },
+ [[], null]
+ );
+ if (final) {
+ found.push(final);
+ }
+ return found;
+ }
+
+ /**
+ * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.
+ * @param {Array} intervals
+ * @return {Array}
+ */
+ static xor(intervals) {
+ let start = null,
+ currentCount = 0;
+ const results = [],
+ ends = intervals.map((i) => [
+ { time: i.s, type: "s" },
+ { time: i.e, type: "e" },
+ ]),
+ flattened = Array.prototype.concat(...ends),
+ arr = flattened.sort((a, b) => a.time - b.time);
+
+ for (const i of arr) {
+ currentCount += i.type === "s" ? 1 : -1;
+
+ if (currentCount === 1) {
+ start = i.time;
+ } else {
+ if (start && +start !== +i.time) {
+ results.push(Interval.fromDateTimes(start, i.time));
+ }
+
+ start = null;
+ }
+ }
+
+ return Interval.merge(results);
+ }
+
+ /**
+ * Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals.
+ * @param {...Interval} intervals
+ * @return {Array}
+ */
+ difference(...intervals) {
+ return Interval.xor([this].concat(intervals))
+ .map((i) => this.intersection(i))
+ .filter((i) => i && !i.isEmpty());
+ }
+
+ /**
+ * Returns a string representation of this Interval appropriate for debugging.
+ * @return {string}
+ */
+ toString() {
+ if (!this.isValid) return INVALID$1;
+ return `[${this.s.toISO()} – ${this.e.toISO()})`;
+ }
+
+ /**
+ * Returns a localized string representing this Interval. Accepts the same options as the
+ * Intl.DateTimeFormat constructor and any presets defined by Luxon, such as
+ * {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method
+ * is browser-specific, but in general it will return an appropriate representation of the
+ * Interval in the assigned locale. Defaults to the system's locale if no locale has been
+ * specified.
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
+ * @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or
+ * Intl.DateTimeFormat constructor options.
+ * @param {Object} opts - Options to override the configuration of the start DateTime.
+ * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 – 11/8/2022
+ * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 – 8, 2022
+ * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7–8 novembre 2022
+ * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 – 8:00 PM
+ * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 – 8:00 p
+ * @return {string}
+ */
+ toLocaleString(formatOpts = DATE_SHORT, opts = {}) {
+ return this.isValid
+ ? Formatter.create(this.s.loc.clone(opts), formatOpts).formatInterval(this)
+ : INVALID$1;
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this Interval.
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
+ * @param {Object} opts - The same options as {@link DateTime#toISO}
+ * @return {string}
+ */
+ toISO(opts) {
+ if (!this.isValid) return INVALID$1;
+ return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`;
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of date of this Interval.
+ * The time components are ignored.
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
+ * @return {string}
+ */
+ toISODate() {
+ if (!this.isValid) return INVALID$1;
+ return `${this.s.toISODate()}/${this.e.toISODate()}`;
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of time of this Interval.
+ * The date components are ignored.
+ * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
+ * @param {Object} opts - The same options as {@link DateTime#toISO}
+ * @return {string}
+ */
+ toISOTime(opts) {
+ if (!this.isValid) return INVALID$1;
+ return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`;
+ }
+
+ /**
+ * Returns a string representation of this Interval formatted according to the specified format
+ * string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible
+ * formatting tool.
+ * @param {string} dateFormat - The format string. This string formats the start and end time.
+ * See {@link DateTime#toFormat} for details.
+ * @param {Object} opts - Options.
+ * @param {string} [opts.separator = ' – '] - A separator to place between the start and end
+ * representations.
+ * @return {string}
+ */
+ toFormat(dateFormat, { separator = " – " } = {}) {
+ if (!this.isValid) return INVALID$1;
+ return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`;
+ }
+
+ /**
+ * Return a Duration representing the time spanned by this interval.
+ * @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration.
+ * @param {Object} opts - options that affect the creation of the Duration
+ * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
+ * @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }
+ * @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }
+ * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }
+ * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }
+ * @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }
+ * @return {Duration}
+ */
+ toDuration(unit, opts) {
+ if (!this.isValid) {
+ return Duration.invalid(this.invalidReason);
+ }
+ return this.e.diff(this.s, unit, opts);
+ }
+
+ /**
+ * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes
+ * @param {function} mapFn
+ * @return {Interval}
+ * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())
+ * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))
+ */
+ mapEndpoints(mapFn) {
+ return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e));
+ }
+}
+
+/**
+ * The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment.
+ */
+class Info {
+ /**
+ * Return whether the specified zone contains a DST.
+ * @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone.
+ * @return {boolean}
+ */
+ static hasDST(zone = Settings.defaultZone) {
+ const proto = DateTime.now().setZone(zone).set({ month: 12 });
+
+ return !zone.isUniversal && proto.offset !== proto.set({ month: 6 }).offset;
+ }
+
+ /**
+ * Return whether the specified zone is a valid IANA specifier.
+ * @param {string} zone - Zone to check
+ * @return {boolean}
+ */
+ static isValidIANAZone(zone) {
+ return IANAZone.isValidZone(zone);
+ }
+
+ /**
+ * Converts the input into a {@link Zone} instance.
+ *
+ * * If `input` is already a Zone instance, it is returned unchanged.
+ * * If `input` is a string containing a valid time zone name, a Zone instance
+ * with that name is returned.
+ * * If `input` is a string that doesn't refer to a known time zone, a Zone
+ * instance with {@link Zone#isValid} == false is returned.
+ * * If `input is a number, a Zone instance with the specified fixed offset
+ * in minutes is returned.
+ * * If `input` is `null` or `undefined`, the default zone is returned.
+ * @param {string|Zone|number} [input] - the value to be converted
+ * @return {Zone}
+ */
+ static normalizeZone(input) {
+ return normalizeZone(input, Settings.defaultZone);
+ }
+
+ /**
+ * Return an array of standalone month names.
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
+ * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long"
+ * @param {Object} opts - options
+ * @param {string} [opts.locale] - the locale code
+ * @param {string} [opts.numberingSystem=null] - the numbering system
+ * @param {string} [opts.locObj=null] - an existing locale object to use
+ * @param {string} [opts.outputCalendar='gregory'] - the calendar
+ * @example Info.months()[0] //=> 'January'
+ * @example Info.months('short')[0] //=> 'Jan'
+ * @example Info.months('numeric')[0] //=> '1'
+ * @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.'
+ * @example Info.months('numeric', { locale: 'ar' })[0] //=> '١'
+ * @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'Rabiʻ I'
+ * @return {Array}
+ */
+ static months(
+ length = "long",
+ { locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}
+ ) {
+ return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length);
+ }
+
+ /**
+ * Return an array of format month names.
+ * Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that
+ * changes the string.
+ * See {@link Info#months}
+ * @param {string} [length='long'] - the length of the month representation, such as "numeric", "2-digit", "narrow", "short", "long"
+ * @param {Object} opts - options
+ * @param {string} [opts.locale] - the locale code
+ * @param {string} [opts.numberingSystem=null] - the numbering system
+ * @param {string} [opts.locObj=null] - an existing locale object to use
+ * @param {string} [opts.outputCalendar='gregory'] - the calendar
+ * @return {Array}
+ */
+ static monthsFormat(
+ length = "long",
+ { locale = null, numberingSystem = null, locObj = null, outputCalendar = "gregory" } = {}
+ ) {
+ return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true);
+ }
+
+ /**
+ * Return an array of standalone week names.
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
+ * @param {string} [length='long'] - the length of the weekday representation, such as "narrow", "short", "long".
+ * @param {Object} opts - options
+ * @param {string} [opts.locale] - the locale code
+ * @param {string} [opts.numberingSystem=null] - the numbering system
+ * @param {string} [opts.locObj=null] - an existing locale object to use
+ * @example Info.weekdays()[0] //=> 'Monday'
+ * @example Info.weekdays('short')[0] //=> 'Mon'
+ * @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.'
+ * @example Info.weekdays('short', { locale: 'ar' })[0] //=> 'الاثنين'
+ * @return {Array}
+ */
+ static weekdays(length = "long", { locale = null, numberingSystem = null, locObj = null } = {}) {
+ return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length);
+ }
+
+ /**
+ * Return an array of format week names.
+ * Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that
+ * changes the string.
+ * See {@link Info#weekdays}
+ * @param {string} [length='long'] - the length of the month representation, such as "narrow", "short", "long".
+ * @param {Object} opts - options
+ * @param {string} [opts.locale=null] - the locale code
+ * @param {string} [opts.numberingSystem=null] - the numbering system
+ * @param {string} [opts.locObj=null] - an existing locale object to use
+ * @return {Array}
+ */
+ static weekdaysFormat(
+ length = "long",
+ { locale = null, numberingSystem = null, locObj = null } = {}
+ ) {
+ return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true);
+ }
+
+ /**
+ * Return an array of meridiems.
+ * @param {Object} opts - options
+ * @param {string} [opts.locale] - the locale code
+ * @example Info.meridiems() //=> [ 'AM', 'PM' ]
+ * @example Info.meridiems({ locale: 'my' }) //=> [ 'နံနက်', 'ညနေ' ]
+ * @return {Array}
+ */
+ static meridiems({ locale = null } = {}) {
+ return Locale.create(locale).meridiems();
+ }
+
+ /**
+ * Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian.
+ * @param {string} [length='short'] - the length of the era representation, such as "short" or "long".
+ * @param {Object} opts - options
+ * @param {string} [opts.locale] - the locale code
+ * @example Info.eras() //=> [ 'BC', 'AD' ]
+ * @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ]
+ * @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant Jésus-Christ', 'après Jésus-Christ' ]
+ * @return {Array}
+ */
+ static eras(length = "short", { locale = null } = {}) {
+ return Locale.create(locale, null, "gregory").eras(length);
+ }
+
+ /**
+ * Return the set of available features in this environment.
+ * Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case.
+ * Keys:
+ * * `relative`: whether this environment supports relative time formatting
+ * @example Info.features() //=> { relative: false }
+ * @return {Object}
+ */
+ static features() {
+ return { relative: hasRelative() };
+ }
+}
+
+function dayDiff(earlier, later) {
+ const utcDayStart = (dt) => dt.toUTC(0, { keepLocalTime: true }).startOf("day").valueOf(),
+ ms = utcDayStart(later) - utcDayStart(earlier);
+ return Math.floor(Duration.fromMillis(ms).as("days"));
+}
+
+function highOrderDiffs(cursor, later, units) {
+ const differs = [
+ ["years", (a, b) => b.year - a.year],
+ ["quarters", (a, b) => b.quarter - a.quarter + (b.year - a.year) * 4],
+ ["months", (a, b) => b.month - a.month + (b.year - a.year) * 12],
+ [
+ "weeks",
+ (a, b) => {
+ const days = dayDiff(a, b);
+ return (days - (days % 7)) / 7;
+ },
+ ],
+ ["days", dayDiff],
+ ];
+
+ const results = {};
+ const earlier = cursor;
+ let lowestOrder, highWater;
+
+ /* This loop tries to diff using larger units first.
+ If we overshoot, we backtrack and try the next smaller unit.
+ "cursor" starts out at the earlier timestamp and moves closer and closer to "later"
+ as we use smaller and smaller units.
+ highWater keeps track of where we would be if we added one more of the smallest unit,
+ this is used later to potentially convert any difference smaller than the smallest higher order unit
+ into a fraction of that smallest higher order unit
+ */
+ for (const [unit, differ] of differs) {
+ if (units.indexOf(unit) >= 0) {
+ lowestOrder = unit;
+
+ results[unit] = differ(cursor, later);
+ highWater = earlier.plus(results);
+
+ if (highWater > later) {
+ // we overshot the end point, backtrack cursor by 1
+ results[unit]--;
+ cursor = earlier.plus(results);
+
+ // if we are still overshooting now, we need to backtrack again
+ // this happens in certain situations when diffing times in different zones,
+ // because this calculation ignores time zones
+ if (cursor > later) {
+ // keep the "overshot by 1" around as highWater
+ highWater = cursor;
+ // backtrack cursor by 1
+ results[unit]--;
+ cursor = earlier.plus(results);
+ }
+ } else {
+ cursor = highWater;
+ }
+ }
+ }
+
+ return [cursor, results, highWater, lowestOrder];
+}
+
+function diff (earlier, later, units, opts) {
+ let [cursor, results, highWater, lowestOrder] = highOrderDiffs(earlier, later, units);
+
+ const remainingMillis = later - cursor;
+
+ const lowerOrderUnits = units.filter(
+ (u) => ["hours", "minutes", "seconds", "milliseconds"].indexOf(u) >= 0
+ );
+
+ if (lowerOrderUnits.length === 0) {
+ if (highWater < later) {
+ highWater = cursor.plus({ [lowestOrder]: 1 });
+ }
+
+ if (highWater !== cursor) {
+ results[lowestOrder] = (results[lowestOrder] || 0) + remainingMillis / (highWater - cursor);
+ }
+ }
+
+ const duration = Duration.fromObject(results, opts);
+
+ if (lowerOrderUnits.length > 0) {
+ return Duration.fromMillis(remainingMillis, opts)
+ .shiftTo(...lowerOrderUnits)
+ .plus(duration);
+ } else {
+ return duration;
+ }
+}
+
+const numberingSystems = {
+ arab: "[\u0660-\u0669]",
+ arabext: "[\u06F0-\u06F9]",
+ bali: "[\u1B50-\u1B59]",
+ beng: "[\u09E6-\u09EF]",
+ deva: "[\u0966-\u096F]",
+ fullwide: "[\uFF10-\uFF19]",
+ gujr: "[\u0AE6-\u0AEF]",
+ hanidec: "[〇|一|二|三|四|五|六|七|八|九]",
+ khmr: "[\u17E0-\u17E9]",
+ knda: "[\u0CE6-\u0CEF]",
+ laoo: "[\u0ED0-\u0ED9]",
+ limb: "[\u1946-\u194F]",
+ mlym: "[\u0D66-\u0D6F]",
+ mong: "[\u1810-\u1819]",
+ mymr: "[\u1040-\u1049]",
+ orya: "[\u0B66-\u0B6F]",
+ tamldec: "[\u0BE6-\u0BEF]",
+ telu: "[\u0C66-\u0C6F]",
+ thai: "[\u0E50-\u0E59]",
+ tibt: "[\u0F20-\u0F29]",
+ latn: "\\d",
+};
+
+const numberingSystemsUTF16 = {
+ arab: [1632, 1641],
+ arabext: [1776, 1785],
+ bali: [6992, 7001],
+ beng: [2534, 2543],
+ deva: [2406, 2415],
+ fullwide: [65296, 65303],
+ gujr: [2790, 2799],
+ khmr: [6112, 6121],
+ knda: [3302, 3311],
+ laoo: [3792, 3801],
+ limb: [6470, 6479],
+ mlym: [3430, 3439],
+ mong: [6160, 6169],
+ mymr: [4160, 4169],
+ orya: [2918, 2927],
+ tamldec: [3046, 3055],
+ telu: [3174, 3183],
+ thai: [3664, 3673],
+ tibt: [3872, 3881],
+};
+
+const hanidecChars = numberingSystems.hanidec.replace(/[\[|\]]/g, "").split("");
+
+function parseDigits(str) {
+ let value = parseInt(str, 10);
+ if (isNaN(value)) {
+ value = "";
+ for (let i = 0; i < str.length; i++) {
+ const code = str.charCodeAt(i);
+
+ if (str[i].search(numberingSystems.hanidec) !== -1) {
+ value += hanidecChars.indexOf(str[i]);
+ } else {
+ for (const key in numberingSystemsUTF16) {
+ const [min, max] = numberingSystemsUTF16[key];
+ if (code >= min && code <= max) {
+ value += code - min;
+ }
+ }
+ }
+ }
+ return parseInt(value, 10);
+ } else {
+ return value;
+ }
+}
+
+function digitRegex({ numberingSystem }, append = "") {
+ return new RegExp(`${numberingSystems[numberingSystem || "latn"]}${append}`);
+}
+
+const MISSING_FTP = "missing Intl.DateTimeFormat.formatToParts support";
+
+function intUnit(regex, post = (i) => i) {
+ return { regex, deser: ([s]) => post(parseDigits(s)) };
+}
+
+const NBSP = String.fromCharCode(160);
+const spaceOrNBSP = `[ ${NBSP}]`;
+const spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, "g");
+
+function fixListRegex(s) {
+ // make dots optional and also make them literal
+ // make space and non breakable space characters interchangeable
+ return s.replace(/\./g, "\\.?").replace(spaceOrNBSPRegExp, spaceOrNBSP);
+}
+
+function stripInsensitivities(s) {
+ return s
+ .replace(/\./g, "") // ignore dots that were made optional
+ .replace(spaceOrNBSPRegExp, " ") // interchange space and nbsp
+ .toLowerCase();
+}
+
+function oneOf(strings, startIndex) {
+ if (strings === null) {
+ return null;
+ } else {
+ return {
+ regex: RegExp(strings.map(fixListRegex).join("|")),
+ deser: ([s]) =>
+ strings.findIndex((i) => stripInsensitivities(s) === stripInsensitivities(i)) + startIndex,
+ };
+ }
+}
+
+function offset(regex, groups) {
+ return { regex, deser: ([, h, m]) => signedOffset(h, m), groups };
+}
+
+function simple(regex) {
+ return { regex, deser: ([s]) => s };
+}
+
+function escapeToken(value) {
+ return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
+}
+
+/**
+ * @param token
+ * @param {Locale} loc
+ */
+function unitForToken(token, loc) {
+ const one = digitRegex(loc),
+ two = digitRegex(loc, "{2}"),
+ three = digitRegex(loc, "{3}"),
+ four = digitRegex(loc, "{4}"),
+ six = digitRegex(loc, "{6}"),
+ oneOrTwo = digitRegex(loc, "{1,2}"),
+ oneToThree = digitRegex(loc, "{1,3}"),
+ oneToSix = digitRegex(loc, "{1,6}"),
+ oneToNine = digitRegex(loc, "{1,9}"),
+ twoToFour = digitRegex(loc, "{2,4}"),
+ fourToSix = digitRegex(loc, "{4,6}"),
+ literal = (t) => ({ regex: RegExp(escapeToken(t.val)), deser: ([s]) => s, literal: true }),
+ unitate = (t) => {
+ if (token.literal) {
+ return literal(t);
+ }
+ switch (t.val) {
+ // era
+ case "G":
+ return oneOf(loc.eras("short"), 0);
+ case "GG":
+ return oneOf(loc.eras("long"), 0);
+ // years
+ case "y":
+ return intUnit(oneToSix);
+ case "yy":
+ return intUnit(twoToFour, untruncateYear);
+ case "yyyy":
+ return intUnit(four);
+ case "yyyyy":
+ return intUnit(fourToSix);
+ case "yyyyyy":
+ return intUnit(six);
+ // months
+ case "M":
+ return intUnit(oneOrTwo);
+ case "MM":
+ return intUnit(two);
+ case "MMM":
+ return oneOf(loc.months("short", true), 1);
+ case "MMMM":
+ return oneOf(loc.months("long", true), 1);
+ case "L":
+ return intUnit(oneOrTwo);
+ case "LL":
+ return intUnit(two);
+ case "LLL":
+ return oneOf(loc.months("short", false), 1);
+ case "LLLL":
+ return oneOf(loc.months("long", false), 1);
+ // dates
+ case "d":
+ return intUnit(oneOrTwo);
+ case "dd":
+ return intUnit(two);
+ // ordinals
+ case "o":
+ return intUnit(oneToThree);
+ case "ooo":
+ return intUnit(three);
+ // time
+ case "HH":
+ return intUnit(two);
+ case "H":
+ return intUnit(oneOrTwo);
+ case "hh":
+ return intUnit(two);
+ case "h":
+ return intUnit(oneOrTwo);
+ case "mm":
+ return intUnit(two);
+ case "m":
+ return intUnit(oneOrTwo);
+ case "q":
+ return intUnit(oneOrTwo);
+ case "qq":
+ return intUnit(two);
+ case "s":
+ return intUnit(oneOrTwo);
+ case "ss":
+ return intUnit(two);
+ case "S":
+ return intUnit(oneToThree);
+ case "SSS":
+ return intUnit(three);
+ case "u":
+ return simple(oneToNine);
+ case "uu":
+ return simple(oneOrTwo);
+ case "uuu":
+ return intUnit(one);
+ // meridiem
+ case "a":
+ return oneOf(loc.meridiems(), 0);
+ // weekYear (k)
+ case "kkkk":
+ return intUnit(four);
+ case "kk":
+ return intUnit(twoToFour, untruncateYear);
+ // weekNumber (W)
+ case "W":
+ return intUnit(oneOrTwo);
+ case "WW":
+ return intUnit(two);
+ // weekdays
+ case "E":
+ case "c":
+ return intUnit(one);
+ case "EEE":
+ return oneOf(loc.weekdays("short", false), 1);
+ case "EEEE":
+ return oneOf(loc.weekdays("long", false), 1);
+ case "ccc":
+ return oneOf(loc.weekdays("short", true), 1);
+ case "cccc":
+ return oneOf(loc.weekdays("long", true), 1);
+ // offset/zone
+ case "Z":
+ case "ZZ":
+ return offset(new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2);
+ case "ZZZ":
+ return offset(new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2);
+ // we don't support ZZZZ (PST) or ZZZZZ (Pacific Standard Time) in parsing
+ // because we don't have any way to figure out what they are
+ case "z":
+ return simple(/[a-z_+-/]{1,256}?/i);
+ // this special-case "token" represents a place where a macro-token expanded into a white-space literal
+ // in this case we accept any non-newline white-space
+ case " ":
+ return simple(/[^\S\n\r]/);
+ default:
+ return literal(t);
+ }
+ };
+
+ const unit = unitate(token) || {
+ invalidReason: MISSING_FTP,
+ };
+
+ unit.token = token;
+
+ return unit;
+}
+
+const partTypeStyleToTokenVal = {
+ year: {
+ "2-digit": "yy",
+ numeric: "yyyyy",
+ },
+ month: {
+ numeric: "M",
+ "2-digit": "MM",
+ short: "MMM",
+ long: "MMMM",
+ },
+ day: {
+ numeric: "d",
+ "2-digit": "dd",
+ },
+ weekday: {
+ short: "EEE",
+ long: "EEEE",
+ },
+ dayperiod: "a",
+ dayPeriod: "a",
+ hour12: {
+ numeric: "h",
+ "2-digit": "hh",
+ },
+ hour24: {
+ numeric: "H",
+ "2-digit": "HH",
+ },
+ minute: {
+ numeric: "m",
+ "2-digit": "mm",
+ },
+ second: {
+ numeric: "s",
+ "2-digit": "ss",
+ },
+ timeZoneName: {
+ long: "ZZZZZ",
+ short: "ZZZ",
+ },
+};
+
+function tokenForPart(part, formatOpts, resolvedOpts) {
+ const { type, value } = part;
+
+ if (type === "literal") {
+ const isSpace = /^\s+$/.test(value);
+ return {
+ literal: !isSpace,
+ val: isSpace ? " " : value,
+ };
+ }
+
+ const style = formatOpts[type];
+
+ // The user might have explicitly specified hour12 or hourCycle
+ // if so, respect their decision
+ // if not, refer back to the resolvedOpts, which are based on the locale
+ let actualType = type;
+ if (type === "hour") {
+ if (formatOpts.hour12 != null) {
+ actualType = formatOpts.hour12 ? "hour12" : "hour24";
+ } else if (formatOpts.hourCycle != null) {
+ if (formatOpts.hourCycle === "h11" || formatOpts.hourCycle === "h12") {
+ actualType = "hour12";
+ } else {
+ actualType = "hour24";
+ }
+ } else {
+ // tokens only differentiate between 24 hours or not,
+ // so we do not need to check hourCycle here, which is less supported anyways
+ actualType = resolvedOpts.hour12 ? "hour12" : "hour24";
+ }
+ }
+ let val = partTypeStyleToTokenVal[actualType];
+ if (typeof val === "object") {
+ val = val[style];
+ }
+
+ if (val) {
+ return {
+ literal: false,
+ val,
+ };
+ }
+
+ return undefined;
+}
+
+function buildRegex(units) {
+ const re = units.map((u) => u.regex).reduce((f, r) => `${f}(${r.source})`, "");
+ return [`^${re}$`, units];
+}
+
+function match(input, regex, handlers) {
+ const matches = input.match(regex);
+
+ if (matches) {
+ const all = {};
+ let matchIndex = 1;
+ for (const i in handlers) {
+ if (hasOwnProperty(handlers, i)) {
+ const h = handlers[i],
+ groups = h.groups ? h.groups + 1 : 1;
+ if (!h.literal && h.token) {
+ all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups));
+ }
+ matchIndex += groups;
+ }
+ }
+ return [matches, all];
+ } else {
+ return [matches, {}];
+ }
+}
+
+function dateTimeFromMatches(matches) {
+ const toField = (token) => {
+ switch (token) {
+ case "S":
+ return "millisecond";
+ case "s":
+ return "second";
+ case "m":
+ return "minute";
+ case "h":
+ case "H":
+ return "hour";
+ case "d":
+ return "day";
+ case "o":
+ return "ordinal";
+ case "L":
+ case "M":
+ return "month";
+ case "y":
+ return "year";
+ case "E":
+ case "c":
+ return "weekday";
+ case "W":
+ return "weekNumber";
+ case "k":
+ return "weekYear";
+ case "q":
+ return "quarter";
+ default:
+ return null;
+ }
+ };
+
+ let zone = null;
+ let specificOffset;
+ if (!isUndefined(matches.z)) {
+ zone = IANAZone.create(matches.z);
+ }
+
+ if (!isUndefined(matches.Z)) {
+ if (!zone) {
+ zone = new FixedOffsetZone(matches.Z);
+ }
+ specificOffset = matches.Z;
+ }
+
+ if (!isUndefined(matches.q)) {
+ matches.M = (matches.q - 1) * 3 + 1;
+ }
+
+ if (!isUndefined(matches.h)) {
+ if (matches.h < 12 && matches.a === 1) {
+ matches.h += 12;
+ } else if (matches.h === 12 && matches.a === 0) {
+ matches.h = 0;
+ }
+ }
+
+ if (matches.G === 0 && matches.y) {
+ matches.y = -matches.y;
+ }
+
+ if (!isUndefined(matches.u)) {
+ matches.S = parseMillis(matches.u);
+ }
+
+ const vals = Object.keys(matches).reduce((r, k) => {
+ const f = toField(k);
+ if (f) {
+ r[f] = matches[k];
+ }
+
+ return r;
+ }, {});
+
+ return [vals, zone, specificOffset];
+}
+
+let dummyDateTimeCache = null;
+
+function getDummyDateTime() {
+ if (!dummyDateTimeCache) {
+ dummyDateTimeCache = DateTime.fromMillis(1555555555555);
+ }
+
+ return dummyDateTimeCache;
+}
+
+function maybeExpandMacroToken(token, locale) {
+ if (token.literal) {
+ return token;
+ }
+
+ const formatOpts = Formatter.macroTokenToFormatOpts(token.val);
+ const tokens = formatOptsToTokens(formatOpts, locale);
+
+ if (tokens == null || tokens.includes(undefined)) {
+ return token;
+ }
+
+ return tokens;
+}
+
+function expandMacroTokens(tokens, locale) {
+ return Array.prototype.concat(...tokens.map((t) => maybeExpandMacroToken(t, locale)));
+}
+
+/**
+ * @private
+ */
+
+function explainFromTokens(locale, input, format) {
+ const tokens = expandMacroTokens(Formatter.parseFormat(format), locale),
+ units = tokens.map((t) => unitForToken(t, locale)),
+ disqualifyingUnit = units.find((t) => t.invalidReason);
+
+ if (disqualifyingUnit) {
+ return { input, tokens, invalidReason: disqualifyingUnit.invalidReason };
+ } else {
+ const [regexString, handlers] = buildRegex(units),
+ regex = RegExp(regexString, "i"),
+ [rawMatches, matches] = match(input, regex, handlers),
+ [result, zone, specificOffset] = matches
+ ? dateTimeFromMatches(matches)
+ : [null, null, undefined];
+ if (hasOwnProperty(matches, "a") && hasOwnProperty(matches, "H")) {
+ throw new ConflictingSpecificationError(
+ "Can't include meridiem when specifying 24-hour format"
+ );
+ }
+ return { input, tokens, regex, rawMatches, matches, result, zone, specificOffset };
+ }
+}
+
+function parseFromTokens(locale, input, format) {
+ const { result, zone, specificOffset, invalidReason } = explainFromTokens(locale, input, format);
+ return [result, zone, specificOffset, invalidReason];
+}
+
+function formatOptsToTokens(formatOpts, locale) {
+ if (!formatOpts) {
+ return null;
+ }
+
+ const formatter = Formatter.create(locale, formatOpts);
+ const df = formatter.dtFormatter(getDummyDateTime());
+ const parts = df.formatToParts();
+ const resolvedOpts = df.resolvedOptions();
+ return parts.map((p) => tokenForPart(p, formatOpts, resolvedOpts));
+}
+
+const nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
+ leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
+
+function unitOutOfRange(unit, value) {
+ return new Invalid(
+ "unit out of range",
+ `you specified ${value} (of type ${typeof value}) as a ${unit}, which is invalid`
+ );
+}
+
+function dayOfWeek(year, month, day) {
+ const d = new Date(Date.UTC(year, month - 1, day));
+
+ if (year < 100 && year >= 0) {
+ d.setUTCFullYear(d.getUTCFullYear() - 1900);
+ }
+
+ const js = d.getUTCDay();
+
+ return js === 0 ? 7 : js;
+}
+
+function computeOrdinal(year, month, day) {
+ return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1];
+}
+
+function uncomputeOrdinal(year, ordinal) {
+ const table = isLeapYear(year) ? leapLadder : nonLeapLadder,
+ month0 = table.findIndex((i) => i < ordinal),
+ day = ordinal - table[month0];
+ return { month: month0 + 1, day };
+}
+
+/**
+ * @private
+ */
+
+function gregorianToWeek(gregObj) {
+ const { year, month, day } = gregObj,
+ ordinal = computeOrdinal(year, month, day),
+ weekday = dayOfWeek(year, month, day);
+
+ let weekNumber = Math.floor((ordinal - weekday + 10) / 7),
+ weekYear;
+
+ if (weekNumber < 1) {
+ weekYear = year - 1;
+ weekNumber = weeksInWeekYear(weekYear);
+ } else if (weekNumber > weeksInWeekYear(year)) {
+ weekYear = year + 1;
+ weekNumber = 1;
+ } else {
+ weekYear = year;
+ }
+
+ return { weekYear, weekNumber, weekday, ...timeObject(gregObj) };
+}
+
+function weekToGregorian(weekData) {
+ const { weekYear, weekNumber, weekday } = weekData,
+ weekdayOfJan4 = dayOfWeek(weekYear, 1, 4),
+ yearInDays = daysInYear(weekYear);
+
+ let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 3,
+ year;
+
+ if (ordinal < 1) {
+ year = weekYear - 1;
+ ordinal += daysInYear(year);
+ } else if (ordinal > yearInDays) {
+ year = weekYear + 1;
+ ordinal -= daysInYear(weekYear);
+ } else {
+ year = weekYear;
+ }
+
+ const { month, day } = uncomputeOrdinal(year, ordinal);
+ return { year, month, day, ...timeObject(weekData) };
+}
+
+function gregorianToOrdinal(gregData) {
+ const { year, month, day } = gregData;
+ const ordinal = computeOrdinal(year, month, day);
+ return { year, ordinal, ...timeObject(gregData) };
+}
+
+function ordinalToGregorian(ordinalData) {
+ const { year, ordinal } = ordinalData;
+ const { month, day } = uncomputeOrdinal(year, ordinal);
+ return { year, month, day, ...timeObject(ordinalData) };
+}
+
+function hasInvalidWeekData(obj) {
+ const validYear = isInteger(obj.weekYear),
+ validWeek = integerBetween(obj.weekNumber, 1, weeksInWeekYear(obj.weekYear)),
+ validWeekday = integerBetween(obj.weekday, 1, 7);
+
+ if (!validYear) {
+ return unitOutOfRange("weekYear", obj.weekYear);
+ } else if (!validWeek) {
+ return unitOutOfRange("week", obj.week);
+ } else if (!validWeekday) {
+ return unitOutOfRange("weekday", obj.weekday);
+ } else return false;
+}
+
+function hasInvalidOrdinalData(obj) {
+ const validYear = isInteger(obj.year),
+ validOrdinal = integerBetween(obj.ordinal, 1, daysInYear(obj.year));
+
+ if (!validYear) {
+ return unitOutOfRange("year", obj.year);
+ } else if (!validOrdinal) {
+ return unitOutOfRange("ordinal", obj.ordinal);
+ } else return false;
+}
+
+function hasInvalidGregorianData(obj) {
+ const validYear = isInteger(obj.year),
+ validMonth = integerBetween(obj.month, 1, 12),
+ validDay = integerBetween(obj.day, 1, daysInMonth(obj.year, obj.month));
+
+ if (!validYear) {
+ return unitOutOfRange("year", obj.year);
+ } else if (!validMonth) {
+ return unitOutOfRange("month", obj.month);
+ } else if (!validDay) {
+ return unitOutOfRange("day", obj.day);
+ } else return false;
+}
+
+function hasInvalidTimeData(obj) {
+ const { hour, minute, second, millisecond } = obj;
+ const validHour =
+ integerBetween(hour, 0, 23) ||
+ (hour === 24 && minute === 0 && second === 0 && millisecond === 0),
+ validMinute = integerBetween(minute, 0, 59),
+ validSecond = integerBetween(second, 0, 59),
+ validMillisecond = integerBetween(millisecond, 0, 999);
+
+ if (!validHour) {
+ return unitOutOfRange("hour", hour);
+ } else if (!validMinute) {
+ return unitOutOfRange("minute", minute);
+ } else if (!validSecond) {
+ return unitOutOfRange("second", second);
+ } else if (!validMillisecond) {
+ return unitOutOfRange("millisecond", millisecond);
+ } else return false;
+}
+
+const INVALID = "Invalid DateTime";
+const MAX_DATE = 8.64e15;
+
+function unsupportedZone(zone) {
+ return new Invalid("unsupported zone", `the zone "${zone.name}" is not supported`);
+}
+
+// we cache week data on the DT object and this intermediates the cache
+function possiblyCachedWeekData(dt) {
+ if (dt.weekData === null) {
+ dt.weekData = gregorianToWeek(dt.c);
+ }
+ return dt.weekData;
+}
+
+// clone really means, "make a new object with these modifications". all "setters" really use this
+// to create a new object while only changing some of the properties
+function clone(inst, alts) {
+ const current = {
+ ts: inst.ts,
+ zone: inst.zone,
+ c: inst.c,
+ o: inst.o,
+ loc: inst.loc,
+ invalid: inst.invalid,
+ };
+ return new DateTime({ ...current, ...alts, old: current });
+}
+
+// find the right offset a given local time. The o input is our guess, which determines which
+// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)
+function fixOffset(localTS, o, tz) {
+ // Our UTC time is just a guess because our offset is just a guess
+ let utcGuess = localTS - o * 60 * 1000;
+
+ // Test whether the zone matches the offset for this ts
+ const o2 = tz.offset(utcGuess);
+
+ // If so, offset didn't change and we're done
+ if (o === o2) {
+ return [utcGuess, o];
+ }
+
+ // If not, change the ts by the difference in the offset
+ utcGuess -= (o2 - o) * 60 * 1000;
+
+ // If that gives us the local time we want, we're done
+ const o3 = tz.offset(utcGuess);
+ if (o2 === o3) {
+ return [utcGuess, o2];
+ }
+
+ // If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time
+ return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)];
+}
+
+// convert an epoch timestamp into a calendar object with the given offset
+function tsToObj(ts, offset) {
+ ts += offset * 60 * 1000;
+
+ const d = new Date(ts);
+
+ return {
+ year: d.getUTCFullYear(),
+ month: d.getUTCMonth() + 1,
+ day: d.getUTCDate(),
+ hour: d.getUTCHours(),
+ minute: d.getUTCMinutes(),
+ second: d.getUTCSeconds(),
+ millisecond: d.getUTCMilliseconds(),
+ };
+}
+
+// convert a calendar object to a epoch timestamp
+function objToTS(obj, offset, zone) {
+ return fixOffset(objToLocalTS(obj), offset, zone);
+}
+
+// create a new DT instance by adding a duration, adjusting for DSTs
+function adjustTime(inst, dur) {
+ const oPre = inst.o,
+ year = inst.c.year + Math.trunc(dur.years),
+ month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3,
+ c = {
+ ...inst.c,
+ year,
+ month,
+ day:
+ Math.min(inst.c.day, daysInMonth(year, month)) +
+ Math.trunc(dur.days) +
+ Math.trunc(dur.weeks) * 7,
+ },
+ millisToAdd = Duration.fromObject({
+ years: dur.years - Math.trunc(dur.years),
+ quarters: dur.quarters - Math.trunc(dur.quarters),
+ months: dur.months - Math.trunc(dur.months),
+ weeks: dur.weeks - Math.trunc(dur.weeks),
+ days: dur.days - Math.trunc(dur.days),
+ hours: dur.hours,
+ minutes: dur.minutes,
+ seconds: dur.seconds,
+ milliseconds: dur.milliseconds,
+ }).as("milliseconds"),
+ localTS = objToLocalTS(c);
+
+ let [ts, o] = fixOffset(localTS, oPre, inst.zone);
+
+ if (millisToAdd !== 0) {
+ ts += millisToAdd;
+ // that could have changed the offset by going over a DST, but we want to keep the ts the same
+ o = inst.zone.offset(ts);
+ }
+
+ return { ts, o };
+}
+
+// helper useful in turning the results of parsing into real dates
+// by handling the zone options
+function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) {
+ const { setZone, zone } = opts;
+ if ((parsed && Object.keys(parsed).length !== 0) || parsedZone) {
+ const interpretationZone = parsedZone || zone,
+ inst = DateTime.fromObject(parsed, {
+ ...opts,
+ zone: interpretationZone,
+ specificOffset,
+ });
+ return setZone ? inst : inst.setZone(zone);
+ } else {
+ return DateTime.invalid(
+ new Invalid("unparsable", `the input "${text}" can't be parsed as ${format}`)
+ );
+ }
+}
+
+// if you want to output a technical format (e.g. RFC 2822), this helper
+// helps handle the details
+function toTechFormat(dt, format, allowZ = true) {
+ return dt.isValid
+ ? Formatter.create(Locale.create("en-US"), {
+ allowZ,
+ forceSimple: true,
+ }).formatDateTimeFromString(dt, format)
+ : null;
+}
+
+function toISODate(o, extended) {
+ const longFormat = o.c.year > 9999 || o.c.year < 0;
+ let c = "";
+ if (longFormat && o.c.year >= 0) c += "+";
+ c += padStart(o.c.year, longFormat ? 6 : 4);
+
+ if (extended) {
+ c += "-";
+ c += padStart(o.c.month);
+ c += "-";
+ c += padStart(o.c.day);
+ } else {
+ c += padStart(o.c.month);
+ c += padStart(o.c.day);
+ }
+ return c;
+}
+
+function toISOTime(
+ o,
+ extended,
+ suppressSeconds,
+ suppressMilliseconds,
+ includeOffset,
+ extendedZone
+) {
+ let c = padStart(o.c.hour);
+ if (extended) {
+ c += ":";
+ c += padStart(o.c.minute);
+ if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) {
+ c += ":";
+ }
+ } else {
+ c += padStart(o.c.minute);
+ }
+
+ if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) {
+ c += padStart(o.c.second);
+
+ if (o.c.millisecond !== 0 || !suppressMilliseconds) {
+ c += ".";
+ c += padStart(o.c.millisecond, 3);
+ }
+ }
+
+ if (includeOffset) {
+ if (o.isOffsetFixed && o.offset === 0 && !extendedZone) {
+ c += "Z";
+ } else if (o.o < 0) {
+ c += "-";
+ c += padStart(Math.trunc(-o.o / 60));
+ c += ":";
+ c += padStart(Math.trunc(-o.o % 60));
+ } else {
+ c += "+";
+ c += padStart(Math.trunc(o.o / 60));
+ c += ":";
+ c += padStart(Math.trunc(o.o % 60));
+ }
+ }
+
+ if (extendedZone) {
+ c += "[" + o.zone.ianaName + "]";
+ }
+ return c;
+}
+
+// defaults for unspecified units in the supported calendars
+const defaultUnitValues = {
+ month: 1,
+ day: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ },
+ defaultWeekUnitValues = {
+ weekNumber: 1,
+ weekday: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ },
+ defaultOrdinalUnitValues = {
+ ordinal: 1,
+ hour: 0,
+ minute: 0,
+ second: 0,
+ millisecond: 0,
+ };
+
+// Units in the supported calendars, sorted by bigness
+const orderedUnits = ["year", "month", "day", "hour", "minute", "second", "millisecond"],
+ orderedWeekUnits = [
+ "weekYear",
+ "weekNumber",
+ "weekday",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ ],
+ orderedOrdinalUnits = ["year", "ordinal", "hour", "minute", "second", "millisecond"];
+
+// standardize case and plurality in units
+function normalizeUnit(unit) {
+ const normalized = {
+ year: "year",
+ years: "year",
+ month: "month",
+ months: "month",
+ day: "day",
+ days: "day",
+ hour: "hour",
+ hours: "hour",
+ minute: "minute",
+ minutes: "minute",
+ quarter: "quarter",
+ quarters: "quarter",
+ second: "second",
+ seconds: "second",
+ millisecond: "millisecond",
+ milliseconds: "millisecond",
+ weekday: "weekday",
+ weekdays: "weekday",
+ weeknumber: "weekNumber",
+ weeksnumber: "weekNumber",
+ weeknumbers: "weekNumber",
+ weekyear: "weekYear",
+ weekyears: "weekYear",
+ ordinal: "ordinal",
+ }[unit.toLowerCase()];
+
+ if (!normalized) throw new InvalidUnitError(unit);
+
+ return normalized;
+}
+
+// this is a dumbed down version of fromObject() that runs about 60% faster
+// but doesn't do any validation, makes a bunch of assumptions about what units
+// are present, and so on.
+function quickDT(obj, opts) {
+ const zone = normalizeZone(opts.zone, Settings.defaultZone),
+ loc = Locale.fromObject(opts),
+ tsNow = Settings.now();
+
+ let ts, o;
+
+ // assume we have the higher-order units
+ if (!isUndefined(obj.year)) {
+ for (const u of orderedUnits) {
+ if (isUndefined(obj[u])) {
+ obj[u] = defaultUnitValues[u];
+ }
+ }
+
+ const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj);
+ if (invalid) {
+ return DateTime.invalid(invalid);
+ }
+
+ const offsetProvis = zone.offset(tsNow);
+ [ts, o] = objToTS(obj, offsetProvis, zone);
+ } else {
+ ts = tsNow;
+ }
+
+ return new DateTime({ ts, zone, loc, o });
+}
+
+function diffRelative(start, end, opts) {
+ const round = isUndefined(opts.round) ? true : opts.round,
+ format = (c, unit) => {
+ c = roundTo(c, round || opts.calendary ? 0 : 2, true);
+ const formatter = end.loc.clone(opts).relFormatter(opts);
+ return formatter.format(c, unit);
+ },
+ differ = (unit) => {
+ if (opts.calendary) {
+ if (!end.hasSame(start, unit)) {
+ return end.startOf(unit).diff(start.startOf(unit), unit).get(unit);
+ } else return 0;
+ } else {
+ return end.diff(start, unit).get(unit);
+ }
+ };
+
+ if (opts.unit) {
+ return format(differ(opts.unit), opts.unit);
+ }
+
+ for (const unit of opts.units) {
+ const count = differ(unit);
+ if (Math.abs(count) >= 1) {
+ return format(count, unit);
+ }
+ }
+ return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]);
+}
+
+function lastOpts(argList) {
+ let opts = {},
+ args;
+ if (argList.length > 0 && typeof argList[argList.length - 1] === "object") {
+ opts = argList[argList.length - 1];
+ args = Array.from(argList).slice(0, argList.length - 1);
+ } else {
+ args = Array.from(argList);
+ }
+ return [opts, args];
+}
+
+/**
+ * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
+ *
+ * A DateTime comprises of:
+ * * A timestamp. Each DateTime instance refers to a specific millisecond of the Unix epoch.
+ * * A time zone. Each instance is considered in the context of a specific zone (by default the local system's zone).
+ * * Configuration properties that effect how output strings are formatted, such as `locale`, `numberingSystem`, and `outputCalendar`.
+ *
+ * Here is a brief overview of the most commonly used functionality it provides:
+ *
+ * * **Creation**: To create a DateTime from its components, use one of its factory class methods: {@link DateTime.local}, {@link DateTime.utc}, and (most flexibly) {@link DateTime.fromObject}. To create one from a standard string format, use {@link DateTime.fromISO}, {@link DateTime.fromHTTP}, and {@link DateTime.fromRFC2822}. To create one from a custom string format, use {@link DateTime.fromFormat}. To create one from a native JS date, use {@link DateTime.fromJSDate}.
+ * * **Gregorian calendar and time**: To examine the Gregorian properties of a DateTime individually (i.e as opposed to collectively through {@link DateTime#toObject}), use the {@link DateTime#year}, {@link DateTime#month},
+ * {@link DateTime#day}, {@link DateTime#hour}, {@link DateTime#minute}, {@link DateTime#second}, {@link DateTime#millisecond} accessors.
+ * * **Week calendar**: For ISO week calendar attributes, see the {@link DateTime#weekYear}, {@link DateTime#weekNumber}, and {@link DateTime#weekday} accessors.
+ * * **Configuration** See the {@link DateTime#locale} and {@link DateTime#numberingSystem} accessors.
+ * * **Transformation**: To transform the DateTime into other DateTimes, use {@link DateTime#set}, {@link DateTime#reconfigure}, {@link DateTime#setZone}, {@link DateTime#setLocale}, {@link DateTime.plus}, {@link DateTime#minus}, {@link DateTime#endOf}, {@link DateTime#startOf}, {@link DateTime#toUTC}, and {@link DateTime#toLocal}.
+ * * **Output**: To convert the DateTime to other representations, use the {@link DateTime#toRelative}, {@link DateTime#toRelativeCalendar}, {@link DateTime#toJSON}, {@link DateTime#toISO}, {@link DateTime#toHTTP}, {@link DateTime#toObject}, {@link DateTime#toRFC2822}, {@link DateTime#toString}, {@link DateTime#toLocaleString}, {@link DateTime#toFormat}, {@link DateTime#toMillis} and {@link DateTime#toJSDate}.
+ *
+ * There's plenty others documented below. In addition, for more information on subtler topics like internationalization, time zones, alternative calendars, validity, and so on, see the external documentation.
+ */
+class DateTime {
+ /**
+ * @access private
+ */
+ constructor(config) {
+ const zone = config.zone || Settings.defaultZone;
+
+ let invalid =
+ config.invalid ||
+ (Number.isNaN(config.ts) ? new Invalid("invalid input") : null) ||
+ (!zone.isValid ? unsupportedZone(zone) : null);
+ /**
+ * @access private
+ */
+ this.ts = isUndefined(config.ts) ? Settings.now() : config.ts;
+
+ let c = null,
+ o = null;
+ if (!invalid) {
+ const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);
+
+ if (unchanged) {
+ [c, o] = [config.old.c, config.old.o];
+ } else {
+ const ot = zone.offset(this.ts);
+ c = tsToObj(this.ts, ot);
+ invalid = Number.isNaN(c.year) ? new Invalid("invalid input") : null;
+ c = invalid ? null : c;
+ o = invalid ? null : ot;
+ }
+ }
+
+ /**
+ * @access private
+ */
+ this._zone = zone;
+ /**
+ * @access private
+ */
+ this.loc = config.loc || Locale.create();
+ /**
+ * @access private
+ */
+ this.invalid = invalid;
+ /**
+ * @access private
+ */
+ this.weekData = null;
+ /**
+ * @access private
+ */
+ this.c = c;
+ /**
+ * @access private
+ */
+ this.o = o;
+ /**
+ * @access private
+ */
+ this.isLuxonDateTime = true;
+ }
+
+ // CONSTRUCT
+
+ /**
+ * Create a DateTime for the current instant, in the system's time zone.
+ *
+ * Use Settings to override these default values if needed.
+ * @example DateTime.now().toISO() //~> now in the ISO format
+ * @return {DateTime}
+ */
+ static now() {
+ return new DateTime({});
+ }
+
+ /**
+ * Create a local DateTime
+ * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used
+ * @param {number} [month=1] - The month, 1-indexed
+ * @param {number} [day=1] - The day of the month, 1-indexed
+ * @param {number} [hour=0] - The hour of the day, in 24-hour time
+ * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59
+ * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59
+ * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999
+ * @example DateTime.local() //~> now
+ * @example DateTime.local({ zone: "America/New_York" }) //~> now, in US east coast time
+ * @example DateTime.local(2017) //~> 2017-01-01T00:00:00
+ * @example DateTime.local(2017, 3) //~> 2017-03-01T00:00:00
+ * @example DateTime.local(2017, 3, 12, { locale: "fr" }) //~> 2017-03-12T00:00:00, with a French locale
+ * @example DateTime.local(2017, 3, 12, 5) //~> 2017-03-12T05:00:00
+ * @example DateTime.local(2017, 3, 12, 5, { zone: "utc" }) //~> 2017-03-12T05:00:00, in UTC
+ * @example DateTime.local(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00
+ * @example DateTime.local(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10
+ * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765) //~> 2017-03-12T05:45:10.765
+ * @return {DateTime}
+ */
+ static local() {
+ const [opts, args] = lastOpts(arguments),
+ [year, month, day, hour, minute, second, millisecond] = args;
+ return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);
+ }
+
+ /**
+ * Create a DateTime in UTC
+ * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used
+ * @param {number} [month=1] - The month, 1-indexed
+ * @param {number} [day=1] - The day of the month
+ * @param {number} [hour=0] - The hour of the day, in 24-hour time
+ * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59
+ * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59
+ * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999
+ * @param {Object} options - configuration options for the DateTime
+ * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
+ * @param {string} [options.outputCalendar] - the output calendar to set on the resulting DateTime instance
+ * @param {string} [options.numberingSystem] - the numbering system to set on the resulting DateTime instance
+ * @example DateTime.utc() //~> now
+ * @example DateTime.utc(2017) //~> 2017-01-01T00:00:00Z
+ * @example DateTime.utc(2017, 3) //~> 2017-03-01T00:00:00Z
+ * @example DateTime.utc(2017, 3, 12) //~> 2017-03-12T00:00:00Z
+ * @example DateTime.utc(2017, 3, 12, 5) //~> 2017-03-12T05:00:00Z
+ * @example DateTime.utc(2017, 3, 12, 5, 45) //~> 2017-03-12T05:45:00Z
+ * @example DateTime.utc(2017, 3, 12, 5, 45, { locale: "fr" }) //~> 2017-03-12T05:45:00Z with a French locale
+ * @example DateTime.utc(2017, 3, 12, 5, 45, 10) //~> 2017-03-12T05:45:10Z
+ * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765, { locale: "fr" }) //~> 2017-03-12T05:45:10.765Z with a French locale
+ * @return {DateTime}
+ */
+ static utc() {
+ const [opts, args] = lastOpts(arguments),
+ [year, month, day, hour, minute, second, millisecond] = args;
+
+ opts.zone = FixedOffsetZone.utcInstance;
+ return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);
+ }
+
+ /**
+ * Create a DateTime from a JavaScript Date object. Uses the default zone.
+ * @param {Date} date - a JavaScript Date object
+ * @param {Object} options - configuration options for the DateTime
+ * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
+ * @return {DateTime}
+ */
+ static fromJSDate(date, options = {}) {
+ const ts = isDate(date) ? date.valueOf() : NaN;
+ if (Number.isNaN(ts)) {
+ return DateTime.invalid("invalid input");
+ }
+
+ const zoneToUse = normalizeZone(options.zone, Settings.defaultZone);
+ if (!zoneToUse.isValid) {
+ return DateTime.invalid(unsupportedZone(zoneToUse));
+ }
+
+ return new DateTime({
+ ts: ts,
+ zone: zoneToUse,
+ loc: Locale.fromObject(options),
+ });
+ }
+
+ /**
+ * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
+ * @param {number} milliseconds - a number of milliseconds since 1970 UTC
+ * @param {Object} options - configuration options for the DateTime
+ * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
+ * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
+ * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance
+ * @return {DateTime}
+ */
+ static fromMillis(milliseconds, options = {}) {
+ if (!isNumber(milliseconds)) {
+ throw new InvalidArgumentError(
+ `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`
+ );
+ } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {
+ // this isn't perfect because because we can still end up out of range because of additional shifting, but it's a start
+ return DateTime.invalid("Timestamp out of range");
+ } else {
+ return new DateTime({
+ ts: milliseconds,
+ zone: normalizeZone(options.zone, Settings.defaultZone),
+ loc: Locale.fromObject(options),
+ });
+ }
+ }
+
+ /**
+ * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.
+ * @param {number} seconds - a number of seconds since 1970 UTC
+ * @param {Object} options - configuration options for the DateTime
+ * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into
+ * @param {string} [options.locale] - a locale to set on the resulting DateTime instance
+ * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance
+ * @return {DateTime}
+ */
+ static fromSeconds(seconds, options = {}) {
+ if (!isNumber(seconds)) {
+ throw new InvalidArgumentError("fromSeconds requires a numerical input");
+ } else {
+ return new DateTime({
+ ts: seconds * 1000,
+ zone: normalizeZone(options.zone, Settings.defaultZone),
+ loc: Locale.fromObject(options),
+ });
+ }
+ }
+
+ /**
+ * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults.
+ * @param {Object} obj - the object to create the DateTime from
+ * @param {number} obj.year - a year, such as 1987
+ * @param {number} obj.month - a month, 1-12
+ * @param {number} obj.day - a day of the month, 1-31, depending on the month
+ * @param {number} obj.ordinal - day of the year, 1-365 or 366
+ * @param {number} obj.weekYear - an ISO week year
+ * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year
+ * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday
+ * @param {number} obj.hour - hour of the day, 0-23
+ * @param {number} obj.minute - minute of the hour, 0-59
+ * @param {number} obj.second - second of the minute, 0-59
+ * @param {number} obj.millisecond - millisecond of the second, 0-999
+ * @param {Object} opts - options for creating this DateTime
+ * @param {string|Zone} [opts.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone()
+ * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
+ * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
+ * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25'
+ * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01'
+ * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06
+ * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'utc' }),
+ * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' })
+ * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' })
+ * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13'
+ * @return {DateTime}
+ */
+ static fromObject(obj, opts = {}) {
+ obj = obj || {};
+ const zoneToUse = normalizeZone(opts.zone, Settings.defaultZone);
+ if (!zoneToUse.isValid) {
+ return DateTime.invalid(unsupportedZone(zoneToUse));
+ }
+
+ const tsNow = Settings.now(),
+ offsetProvis = !isUndefined(opts.specificOffset)
+ ? opts.specificOffset
+ : zoneToUse.offset(tsNow),
+ normalized = normalizeObject(obj, normalizeUnit),
+ containsOrdinal = !isUndefined(normalized.ordinal),
+ containsGregorYear = !isUndefined(normalized.year),
+ containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
+ containsGregor = containsGregorYear || containsGregorMD,
+ definiteWeekDef = normalized.weekYear || normalized.weekNumber,
+ loc = Locale.fromObject(opts);
+
+ // cases:
+ // just a weekday -> this week's instance of that weekday, no worries
+ // (gregorian data or ordinal) + (weekYear or weekNumber) -> error
+ // (gregorian month or day) + ordinal -> error
+ // otherwise just use weeks or ordinals or gregorian, depending on what's specified
+
+ if ((containsGregor || containsOrdinal) && definiteWeekDef) {
+ throw new ConflictingSpecificationError(
+ "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
+ );
+ }
+
+ if (containsGregorMD && containsOrdinal) {
+ throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
+ }
+
+ const useWeekData = definiteWeekDef || (normalized.weekday && !containsGregor);
+
+ // configure ourselves to deal with gregorian dates or week stuff
+ let units,
+ defaultValues,
+ objNow = tsToObj(tsNow, offsetProvis);
+ if (useWeekData) {
+ units = orderedWeekUnits;
+ defaultValues = defaultWeekUnitValues;
+ objNow = gregorianToWeek(objNow);
+ } else if (containsOrdinal) {
+ units = orderedOrdinalUnits;
+ defaultValues = defaultOrdinalUnitValues;
+ objNow = gregorianToOrdinal(objNow);
+ } else {
+ units = orderedUnits;
+ defaultValues = defaultUnitValues;
+ }
+
+ // set default values for missing stuff
+ let foundFirst = false;
+ for (const u of units) {
+ const v = normalized[u];
+ if (!isUndefined(v)) {
+ foundFirst = true;
+ } else if (foundFirst) {
+ normalized[u] = defaultValues[u];
+ } else {
+ normalized[u] = objNow[u];
+ }
+ }
+
+ // make sure the values we have are in range
+ const higherOrderInvalid = useWeekData
+ ? hasInvalidWeekData(normalized)
+ : containsOrdinal
+ ? hasInvalidOrdinalData(normalized)
+ : hasInvalidGregorianData(normalized),
+ invalid = higherOrderInvalid || hasInvalidTimeData(normalized);
+
+ if (invalid) {
+ return DateTime.invalid(invalid);
+ }
+
+ // compute the actual time
+ const gregorian = useWeekData
+ ? weekToGregorian(normalized)
+ : containsOrdinal
+ ? ordinalToGregorian(normalized)
+ : normalized,
+ [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse),
+ inst = new DateTime({
+ ts: tsFinal,
+ zone: zoneToUse,
+ o: offsetFinal,
+ loc,
+ });
+
+ // gregorian data + weekday serves only to validate
+ if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) {
+ return DateTime.invalid(
+ "mismatched weekday",
+ `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`
+ );
+ }
+
+ return inst;
+ }
+
+ /**
+ * Create a DateTime from an ISO 8601 string
+ * @param {string} text - the ISO string
+ * @param {Object} opts - options to affect the creation
+ * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone
+ * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one
+ * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
+ * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance
+ * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance
+ * @example DateTime.fromISO('2016-05-25T09:08:34.123')
+ * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00')
+ * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true})
+ * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'})
+ * @example DateTime.fromISO('2016-W05-4')
+ * @return {DateTime}
+ */
+ static fromISO(text, opts = {}) {
+ const [vals, parsedZone] = parseISODate(text);
+ return parseDataToDateTime(vals, parsedZone, opts, "ISO 8601", text);
+ }
+
+ /**
+ * Create a DateTime from an RFC 2822 string
+ * @param {string} text - the RFC 2822 string
+ * @param {Object} opts - options to affect the creation
+ * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.
+ * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one
+ * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
+ * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
+ * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT')
+ * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600')
+ * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z')
+ * @return {DateTime}
+ */
+ static fromRFC2822(text, opts = {}) {
+ const [vals, parsedZone] = parseRFC2822Date(text);
+ return parseDataToDateTime(vals, parsedZone, opts, "RFC 2822", text);
+ }
+
+ /**
+ * Create a DateTime from an HTTP header date
+ * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
+ * @param {string} text - the HTTP header date
+ * @param {Object} opts - options to affect the creation
+ * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.
+ * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods.
+ * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance
+ * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance
+ * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT')
+ * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT')
+ * @example DateTime.fromHTTP('Sun Nov 6 08:49:37 1994')
+ * @return {DateTime}
+ */
+ static fromHTTP(text, opts = {}) {
+ const [vals, parsedZone] = parseHTTPDate(text);
+ return parseDataToDateTime(vals, parsedZone, opts, "HTTP", opts);
+ }
+
+ /**
+ * Create a DateTime from an input string and format string.
+ * Defaults to en-US if no locale has been specified, regardless of the system's locale. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/parsing?id=table-of-tokens).
+ * @param {string} text - the string to parse
+ * @param {string} fmt - the format the string is expected to be in (see the link below for the formats)
+ * @param {Object} opts - options to affect the creation
+ * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone
+ * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one
+ * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale
+ * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system
+ * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @return {DateTime}
+ */
+ static fromFormat(text, fmt, opts = {}) {
+ if (isUndefined(text) || isUndefined(fmt)) {
+ throw new InvalidArgumentError("fromFormat requires an input string and a format");
+ }
+
+ const { locale = null, numberingSystem = null } = opts,
+ localeToUse = Locale.fromOpts({
+ locale,
+ numberingSystem,
+ defaultToEN: true,
+ }),
+ [vals, parsedZone, specificOffset, invalid] = parseFromTokens(localeToUse, text, fmt);
+ if (invalid) {
+ return DateTime.invalid(invalid);
+ } else {
+ return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text, specificOffset);
+ }
+ }
+
+ /**
+ * @deprecated use fromFormat instead
+ */
+ static fromString(text, fmt, opts = {}) {
+ return DateTime.fromFormat(text, fmt, opts);
+ }
+
+ /**
+ * Create a DateTime from a SQL date, time, or datetime
+ * Defaults to en-US if no locale has been specified, regardless of the system's locale
+ * @param {string} text - the string to parse
+ * @param {Object} opts - options to affect the creation
+ * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone
+ * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one
+ * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale
+ * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system
+ * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance
+ * @example DateTime.fromSQL('2017-05-15')
+ * @example DateTime.fromSQL('2017-05-15 09:12:34')
+ * @example DateTime.fromSQL('2017-05-15 09:12:34.342')
+ * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00')
+ * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles')
+ * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true })
+ * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' })
+ * @example DateTime.fromSQL('09:12:34.342')
+ * @return {DateTime}
+ */
+ static fromSQL(text, opts = {}) {
+ const [vals, parsedZone] = parseSQL(text);
+ return parseDataToDateTime(vals, parsedZone, opts, "SQL", text);
+ }
+
+ /**
+ * Create an invalid DateTime.
+ * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent.
+ * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
+ * @return {DateTime}
+ */
+ static invalid(reason, explanation = null) {
+ if (!reason) {
+ throw new InvalidArgumentError("need to specify a reason the DateTime is invalid");
+ }
+
+ const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);
+
+ if (Settings.throwOnInvalid) {
+ throw new InvalidDateTimeError(invalid);
+ } else {
+ return new DateTime({ invalid });
+ }
+ }
+
+ /**
+ * Check if an object is an instance of DateTime. Works across context boundaries
+ * @param {object} o
+ * @return {boolean}
+ */
+ static isDateTime(o) {
+ return (o && o.isLuxonDateTime) || false;
+ }
+
+ /**
+ * Produce the format string for a set of options
+ * @param formatOpts
+ * @param localeOpts
+ * @returns {string}
+ */
+ static parseFormatForOpts(formatOpts, localeOpts = {}) {
+ const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts));
+ return !tokenList ? null : tokenList.map((t) => (t ? t.val : null)).join("");
+ }
+
+ /**
+ * Produce the the fully expanded format token for the locale
+ * Does NOT quote characters, so quoted tokens will not round trip correctly
+ * @param fmt
+ * @param localeOpts
+ * @returns {string}
+ */
+ static expandFormat(fmt, localeOpts = {}) {
+ const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts));
+ return expanded.map((t) => t.val).join("");
+ }
+
+ // INFO
+
+ /**
+ * Get the value of unit.
+ * @param {string} unit - a unit such as 'minute' or 'day'
+ * @example DateTime.local(2017, 7, 4).get('month'); //=> 7
+ * @example DateTime.local(2017, 7, 4).get('day'); //=> 4
+ * @return {number}
+ */
+ get(unit) {
+ return this[unit];
+ }
+
+ /**
+ * Returns whether the DateTime is valid. Invalid DateTimes occur when:
+ * * The DateTime was created from invalid calendar information, such as the 13th month or February 30
+ * * The DateTime was created by an operation on another invalid date
+ * @type {boolean}
+ */
+ get isValid() {
+ return this.invalid === null;
+ }
+
+ /**
+ * Returns an error code if this DateTime is invalid, or null if the DateTime is valid
+ * @type {string}
+ */
+ get invalidReason() {
+ return this.invalid ? this.invalid.reason : null;
+ }
+
+ /**
+ * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid
+ * @type {string}
+ */
+ get invalidExplanation() {
+ return this.invalid ? this.invalid.explanation : null;
+ }
+
+ /**
+ * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime
+ *
+ * @type {string}
+ */
+ get locale() {
+ return this.isValid ? this.loc.locale : null;
+ }
+
+ /**
+ * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime
+ *
+ * @type {string}
+ */
+ get numberingSystem() {
+ return this.isValid ? this.loc.numberingSystem : null;
+ }
+
+ /**
+ * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime
+ *
+ * @type {string}
+ */
+ get outputCalendar() {
+ return this.isValid ? this.loc.outputCalendar : null;
+ }
+
+ /**
+ * Get the time zone associated with this DateTime.
+ * @type {Zone}
+ */
+ get zone() {
+ return this._zone;
+ }
+
+ /**
+ * Get the name of the time zone.
+ * @type {string}
+ */
+ get zoneName() {
+ return this.isValid ? this.zone.name : null;
+ }
+
+ /**
+ * Get the year
+ * @example DateTime.local(2017, 5, 25).year //=> 2017
+ * @type {number}
+ */
+ get year() {
+ return this.isValid ? this.c.year : NaN;
+ }
+
+ /**
+ * Get the quarter
+ * @example DateTime.local(2017, 5, 25).quarter //=> 2
+ * @type {number}
+ */
+ get quarter() {
+ return this.isValid ? Math.ceil(this.c.month / 3) : NaN;
+ }
+
+ /**
+ * Get the month (1-12).
+ * @example DateTime.local(2017, 5, 25).month //=> 5
+ * @type {number}
+ */
+ get month() {
+ return this.isValid ? this.c.month : NaN;
+ }
+
+ /**
+ * Get the day of the month (1-30ish).
+ * @example DateTime.local(2017, 5, 25).day //=> 25
+ * @type {number}
+ */
+ get day() {
+ return this.isValid ? this.c.day : NaN;
+ }
+
+ /**
+ * Get the hour of the day (0-23).
+ * @example DateTime.local(2017, 5, 25, 9).hour //=> 9
+ * @type {number}
+ */
+ get hour() {
+ return this.isValid ? this.c.hour : NaN;
+ }
+
+ /**
+ * Get the minute of the hour (0-59).
+ * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30
+ * @type {number}
+ */
+ get minute() {
+ return this.isValid ? this.c.minute : NaN;
+ }
+
+ /**
+ * Get the second of the minute (0-59).
+ * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52
+ * @type {number}
+ */
+ get second() {
+ return this.isValid ? this.c.second : NaN;
+ }
+
+ /**
+ * Get the millisecond of the second (0-999).
+ * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654
+ * @type {number}
+ */
+ get millisecond() {
+ return this.isValid ? this.c.millisecond : NaN;
+ }
+
+ /**
+ * Get the week year
+ * @see https://en.wikipedia.org/wiki/ISO_week_date
+ * @example DateTime.local(2014, 12, 31).weekYear //=> 2015
+ * @type {number}
+ */
+ get weekYear() {
+ return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN;
+ }
+
+ /**
+ * Get the week number of the week year (1-52ish).
+ * @see https://en.wikipedia.org/wiki/ISO_week_date
+ * @example DateTime.local(2017, 5, 25).weekNumber //=> 21
+ * @type {number}
+ */
+ get weekNumber() {
+ return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN;
+ }
+
+ /**
+ * Get the day of the week.
+ * 1 is Monday and 7 is Sunday
+ * @see https://en.wikipedia.org/wiki/ISO_week_date
+ * @example DateTime.local(2014, 11, 31).weekday //=> 4
+ * @type {number}
+ */
+ get weekday() {
+ return this.isValid ? possiblyCachedWeekData(this).weekday : NaN;
+ }
+
+ /**
+ * Get the ordinal (meaning the day of the year)
+ * @example DateTime.local(2017, 5, 25).ordinal //=> 145
+ * @type {number|DateTime}
+ */
+ get ordinal() {
+ return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN;
+ }
+
+ /**
+ * Get the human readable short month name, such as 'Oct'.
+ * Defaults to the system's locale if no locale has been specified
+ * @example DateTime.local(2017, 10, 30).monthShort //=> Oct
+ * @type {string}
+ */
+ get monthShort() {
+ return this.isValid ? Info.months("short", { locObj: this.loc })[this.month - 1] : null;
+ }
+
+ /**
+ * Get the human readable long month name, such as 'October'.
+ * Defaults to the system's locale if no locale has been specified
+ * @example DateTime.local(2017, 10, 30).monthLong //=> October
+ * @type {string}
+ */
+ get monthLong() {
+ return this.isValid ? Info.months("long", { locObj: this.loc })[this.month - 1] : null;
+ }
+
+ /**
+ * Get the human readable short weekday, such as 'Mon'.
+ * Defaults to the system's locale if no locale has been specified
+ * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon
+ * @type {string}
+ */
+ get weekdayShort() {
+ return this.isValid ? Info.weekdays("short", { locObj: this.loc })[this.weekday - 1] : null;
+ }
+
+ /**
+ * Get the human readable long weekday, such as 'Monday'.
+ * Defaults to the system's locale if no locale has been specified
+ * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday
+ * @type {string}
+ */
+ get weekdayLong() {
+ return this.isValid ? Info.weekdays("long", { locObj: this.loc })[this.weekday - 1] : null;
+ }
+
+ /**
+ * Get the UTC offset of this DateTime in minutes
+ * @example DateTime.now().offset //=> -240
+ * @example DateTime.utc().offset //=> 0
+ * @type {number}
+ */
+ get offset() {
+ return this.isValid ? +this.o : NaN;
+ }
+
+ /**
+ * Get the short human name for the zone's current offset, for example "EST" or "EDT".
+ * Defaults to the system's locale if no locale has been specified
+ * @type {string}
+ */
+ get offsetNameShort() {
+ if (this.isValid) {
+ return this.zone.offsetName(this.ts, {
+ format: "short",
+ locale: this.locale,
+ });
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the long human name for the zone's current offset, for example "Eastern Standard Time" or "Eastern Daylight Time".
+ * Defaults to the system's locale if no locale has been specified
+ * @type {string}
+ */
+ get offsetNameLong() {
+ if (this.isValid) {
+ return this.zone.offsetName(this.ts, {
+ format: "long",
+ locale: this.locale,
+ });
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get whether this zone's offset ever changes, as in a DST.
+ * @type {boolean}
+ */
+ get isOffsetFixed() {
+ return this.isValid ? this.zone.isUniversal : null;
+ }
+
+ /**
+ * Get whether the DateTime is in a DST.
+ * @type {boolean}
+ */
+ get isInDST() {
+ if (this.isOffsetFixed) {
+ return false;
+ } else {
+ return (
+ this.offset > this.set({ month: 1, day: 1 }).offset ||
+ this.offset > this.set({ month: 5 }).offset
+ );
+ }
+ }
+
+ /**
+ * Get those DateTimes which have the same local time as this DateTime, but a different offset from UTC
+ * in this DateTime's zone. During DST changes local time can be ambiguous, for example
+ * `2023-10-29T02:30:00` in `Europe/Berlin` can have offset `+01:00` or `+02:00`.
+ * This method will return both possible DateTimes if this DateTime's local time is ambiguous.
+ * @returns {DateTime[]}
+ */
+ getPossibleOffsets() {
+ if (!this.isValid || this.isOffsetFixed) {
+ return [this];
+ }
+ const dayMs = 86400000;
+ const minuteMs = 60000;
+ const localTS = objToLocalTS(this.c);
+ const oEarlier = this.zone.offset(localTS - dayMs);
+ const oLater = this.zone.offset(localTS + dayMs);
+
+ const o1 = this.zone.offset(localTS - oEarlier * minuteMs);
+ const o2 = this.zone.offset(localTS - oLater * minuteMs);
+ if (o1 === o2) {
+ return [this];
+ }
+ const ts1 = localTS - o1 * minuteMs;
+ const ts2 = localTS - o2 * minuteMs;
+ const c1 = tsToObj(ts1, o1);
+ const c2 = tsToObj(ts2, o2);
+ if (
+ c1.hour === c2.hour &&
+ c1.minute === c2.minute &&
+ c1.second === c2.second &&
+ c1.millisecond === c2.millisecond
+ ) {
+ return [clone(this, { ts: ts1 }), clone(this, { ts: ts2 })];
+ }
+ return [this];
+ }
+
+ /**
+ * Returns true if this DateTime is in a leap year, false otherwise
+ * @example DateTime.local(2016).isInLeapYear //=> true
+ * @example DateTime.local(2013).isInLeapYear //=> false
+ * @type {boolean}
+ */
+ get isInLeapYear() {
+ return isLeapYear(this.year);
+ }
+
+ /**
+ * Returns the number of days in this DateTime's month
+ * @example DateTime.local(2016, 2).daysInMonth //=> 29
+ * @example DateTime.local(2016, 3).daysInMonth //=> 31
+ * @type {number}
+ */
+ get daysInMonth() {
+ return daysInMonth(this.year, this.month);
+ }
+
+ /**
+ * Returns the number of days in this DateTime's year
+ * @example DateTime.local(2016).daysInYear //=> 366
+ * @example DateTime.local(2013).daysInYear //=> 365
+ * @type {number}
+ */
+ get daysInYear() {
+ return this.isValid ? daysInYear(this.year) : NaN;
+ }
+
+ /**
+ * Returns the number of weeks in this DateTime's year
+ * @see https://en.wikipedia.org/wiki/ISO_week_date
+ * @example DateTime.local(2004).weeksInWeekYear //=> 53
+ * @example DateTime.local(2013).weeksInWeekYear //=> 52
+ * @type {number}
+ */
+ get weeksInWeekYear() {
+ return this.isValid ? weeksInWeekYear(this.weekYear) : NaN;
+ }
+
+ /**
+ * Returns the resolved Intl options for this DateTime.
+ * This is useful in understanding the behavior of formatting methods
+ * @param {Object} opts - the same options as toLocaleString
+ * @return {Object}
+ */
+ resolvedLocaleOptions(opts = {}) {
+ const { locale, numberingSystem, calendar } = Formatter.create(
+ this.loc.clone(opts),
+ opts
+ ).resolvedOptions(this);
+ return { locale, numberingSystem, outputCalendar: calendar };
+ }
+
+ // TRANSFORM
+
+ /**
+ * "Set" the DateTime's zone to UTC. Returns a newly-constructed DateTime.
+ *
+ * Equivalent to {@link DateTime#setZone}('utc')
+ * @param {number} [offset=0] - optionally, an offset from UTC in minutes
+ * @param {Object} [opts={}] - options to pass to `setZone()`
+ * @return {DateTime}
+ */
+ toUTC(offset = 0, opts = {}) {
+ return this.setZone(FixedOffsetZone.instance(offset), opts);
+ }
+
+ /**
+ * "Set" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime.
+ *
+ * Equivalent to `setZone('local')`
+ * @return {DateTime}
+ */
+ toLocal() {
+ return this.setZone(Settings.defaultZone);
+ }
+
+ /**
+ * "Set" the DateTime's zone to specified zone. Returns a newly-constructed DateTime.
+ *
+ * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link DateTime#plus}. You may wish to use {@link DateTime#toLocal} and {@link DateTime#toUTC} which provide simple convenience wrappers for commonly used zones.
+ * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link DateTime#Zone} class.
+ * @param {Object} opts - options
+ * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this.
+ * @return {DateTime}
+ */
+ setZone(zone, { keepLocalTime = false, keepCalendarTime = false } = {}) {
+ zone = normalizeZone(zone, Settings.defaultZone);
+ if (zone.equals(this.zone)) {
+ return this;
+ } else if (!zone.isValid) {
+ return DateTime.invalid(unsupportedZone(zone));
+ } else {
+ let newTS = this.ts;
+ if (keepLocalTime || keepCalendarTime) {
+ const offsetGuess = zone.offset(this.ts);
+ const asObj = this.toObject();
+ [newTS] = objToTS(asObj, offsetGuess, zone);
+ }
+ return clone(this, { ts: newTS, zone });
+ }
+ }
+
+ /**
+ * "Set" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime.
+ * @param {Object} properties - the properties to set
+ * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' })
+ * @return {DateTime}
+ */
+ reconfigure({ locale, numberingSystem, outputCalendar } = {}) {
+ const loc = this.loc.clone({ locale, numberingSystem, outputCalendar });
+ return clone(this, { loc });
+ }
+
+ /**
+ * "Set" the locale. Returns a newly-constructed DateTime.
+ * Just a convenient alias for reconfigure({ locale })
+ * @example DateTime.local(2017, 5, 25).setLocale('en-GB')
+ * @return {DateTime}
+ */
+ setLocale(locale) {
+ return this.reconfigure({ locale });
+ }
+
+ /**
+ * "Set" the values of specified units. Returns a newly-constructed DateTime.
+ * You can only set units with this method; for "setting" metadata, see {@link DateTime#reconfigure} and {@link DateTime#setZone}.
+ * @param {Object} values - a mapping of units to numbers
+ * @example dt.set({ year: 2017 })
+ * @example dt.set({ hour: 8, minute: 30 })
+ * @example dt.set({ weekday: 5 })
+ * @example dt.set({ year: 2005, ordinal: 234 })
+ * @return {DateTime}
+ */
+ set(values) {
+ if (!this.isValid) return this;
+
+ const normalized = normalizeObject(values, normalizeUnit),
+ settingWeekStuff =
+ !isUndefined(normalized.weekYear) ||
+ !isUndefined(normalized.weekNumber) ||
+ !isUndefined(normalized.weekday),
+ containsOrdinal = !isUndefined(normalized.ordinal),
+ containsGregorYear = !isUndefined(normalized.year),
+ containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),
+ containsGregor = containsGregorYear || containsGregorMD,
+ definiteWeekDef = normalized.weekYear || normalized.weekNumber;
+
+ if ((containsGregor || containsOrdinal) && definiteWeekDef) {
+ throw new ConflictingSpecificationError(
+ "Can't mix weekYear/weekNumber units with year/month/day or ordinals"
+ );
+ }
+
+ if (containsGregorMD && containsOrdinal) {
+ throw new ConflictingSpecificationError("Can't mix ordinal dates with month/day");
+ }
+
+ let mixed;
+ if (settingWeekStuff) {
+ mixed = weekToGregorian({ ...gregorianToWeek(this.c), ...normalized });
+ } else if (!isUndefined(normalized.ordinal)) {
+ mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized });
+ } else {
+ mixed = { ...this.toObject(), ...normalized };
+
+ // if we didn't set the day but we ended up on an overflow date,
+ // use the last day of the right month
+ if (isUndefined(normalized.day)) {
+ mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
+ }
+ }
+
+ const [ts, o] = objToTS(mixed, this.o, this.zone);
+ return clone(this, { ts, o });
+ }
+
+ /**
+ * Add a period of time to this DateTime and return the resulting DateTime
+ *
+ * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between.
+ * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
+ * @example DateTime.now().plus(123) //~> in 123 milliseconds
+ * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes
+ * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow
+ * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday
+ * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min
+ * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min
+ * @return {DateTime}
+ */
+ plus(duration) {
+ if (!this.isValid) return this;
+ const dur = Duration.fromDurationLike(duration);
+ return clone(this, adjustTime(this, dur));
+ }
+
+ /**
+ * Subtract a period of time to this DateTime and return the resulting DateTime
+ * See {@link DateTime#plus}
+ * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()
+ @return {DateTime}
+ */
+ minus(duration) {
+ if (!this.isValid) return this;
+ const dur = Duration.fromDurationLike(duration).negate();
+ return clone(this, adjustTime(this, dur));
+ }
+
+ /**
+ * "Set" this DateTime to the beginning of a unit of time.
+ * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
+ * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01'
+ * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01'
+ * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays
+ * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00'
+ * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00'
+ * @return {DateTime}
+ */
+ startOf(unit) {
+ if (!this.isValid) return this;
+ const o = {},
+ normalizedUnit = Duration.normalizeUnit(unit);
+ switch (normalizedUnit) {
+ case "years":
+ o.month = 1;
+ // falls through
+ case "quarters":
+ case "months":
+ o.day = 1;
+ // falls through
+ case "weeks":
+ case "days":
+ o.hour = 0;
+ // falls through
+ case "hours":
+ o.minute = 0;
+ // falls through
+ case "minutes":
+ o.second = 0;
+ // falls through
+ case "seconds":
+ o.millisecond = 0;
+ break;
+ // no default, invalid units throw in normalizeUnit()
+ }
+
+ if (normalizedUnit === "weeks") {
+ o.weekday = 1;
+ }
+
+ if (normalizedUnit === "quarters") {
+ const q = Math.ceil(this.month / 3);
+ o.month = (q - 1) * 3 + 1;
+ }
+
+ return this.set(o);
+ }
+
+ /**
+ * "Set" this DateTime to the end (meaning the last millisecond) of a unit of time
+ * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.
+ * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00'
+ * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00'
+ * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays
+ * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00'
+ * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00'
+ * @return {DateTime}
+ */
+ endOf(unit) {
+ return this.isValid
+ ? this.plus({ [unit]: 1 })
+ .startOf(unit)
+ .minus(1)
+ : this;
+ }
+
+ // OUTPUT
+
+ /**
+ * Returns a string representation of this DateTime formatted according to the specified format string.
+ * **You may not want this.** See {@link DateTime#toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/formatting?id=table-of-tokens).
+ * Defaults to en-US if no locale has been specified, regardless of the system's locale.
+ * @param {string} fmt - the format string
+ * @param {Object} opts - opts to override the configuration options on this DateTime
+ * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22'
+ * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22'
+ * @example DateTime.now().toFormat('yyyy LLL dd', { locale: "fr" }) //=> '2017 avr. 22'
+ * @example DateTime.now().toFormat("HH 'hours and' mm 'minutes'") //=> '20 hours and 55 minutes'
+ * @return {string}
+ */
+ toFormat(fmt, opts = {}) {
+ return this.isValid
+ ? Formatter.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt)
+ : INVALID;
+ }
+
+ /**
+ * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`.
+ * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation
+ * of the DateTime in the assigned locale.
+ * Defaults to the system's locale if no locale has been specified
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
+ * @param formatOpts {Object} - Intl.DateTimeFormat constructor options and configuration options
+ * @param {Object} opts - opts to override the configuration options on this DateTime
+ * @example DateTime.now().toLocaleString(); //=> 4/20/2017
+ * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017'
+ * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017'
+ * @example DateTime.now().toLocaleString(DateTime.DATE_FULL, { locale: 'fr' }); //=> '28 août 2022'
+ * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM'
+ * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM'
+ * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20'
+ * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM'
+ * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }); //=> '11:32'
+ * @return {string}
+ */
+ toLocaleString(formatOpts = DATE_SHORT, opts = {}) {
+ return this.isValid
+ ? Formatter.create(this.loc.clone(opts), formatOpts).formatDateTime(this)
+ : INVALID;
+ }
+
+ /**
+ * Returns an array of format "parts", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output.
+ * Defaults to the system's locale if no locale has been specified
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
+ * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`.
+ * @example DateTime.now().toLocaleParts(); //=> [
+ * //=> { type: 'day', value: '25' },
+ * //=> { type: 'literal', value: '/' },
+ * //=> { type: 'month', value: '05' },
+ * //=> { type: 'literal', value: '/' },
+ * //=> { type: 'year', value: '1982' }
+ * //=> ]
+ */
+ toLocaleParts(opts = {}) {
+ return this.isValid
+ ? Formatter.create(this.loc.clone(opts), opts).formatDateTimeParts(this)
+ : [];
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this DateTime
+ * @param {Object} opts - options
+ * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
+ * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
+ * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
+ * @param {boolean} [opts.extendedZone=false] - add the time zone format extension
+ * @param {string} [opts.format='extended'] - choose between the basic and extended format
+ * @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'
+ * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'
+ * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'
+ * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'
+ * @return {string}
+ */
+ toISO({
+ format = "extended",
+ suppressSeconds = false,
+ suppressMilliseconds = false,
+ includeOffset = true,
+ extendedZone = false,
+ } = {}) {
+ if (!this.isValid) {
+ return null;
+ }
+
+ const ext = format === "extended";
+
+ let c = toISODate(this, ext);
+ c += "T";
+ c += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone);
+ return c;
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this DateTime's date component
+ * @param {Object} opts - options
+ * @param {string} [opts.format='extended'] - choose between the basic and extended format
+ * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'
+ * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'
+ * @return {string}
+ */
+ toISODate({ format = "extended" } = {}) {
+ if (!this.isValid) {
+ return null;
+ }
+
+ return toISODate(this, format === "extended");
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this DateTime's week date
+ * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2'
+ * @return {string}
+ */
+ toISOWeekDate() {
+ return toTechFormat(this, "kkkk-'W'WW-c");
+ }
+
+ /**
+ * Returns an ISO 8601-compliant string representation of this DateTime's time component
+ * @param {Object} opts - options
+ * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0
+ * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0
+ * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
+ * @param {boolean} [opts.extendedZone=true] - add the time zone format extension
+ * @param {boolean} [opts.includePrefix=false] - include the `T` prefix
+ * @param {string} [opts.format='extended'] - choose between the basic and extended format
+ * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'
+ * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'
+ * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'
+ * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'
+ * @return {string}
+ */
+ toISOTime({
+ suppressMilliseconds = false,
+ suppressSeconds = false,
+ includeOffset = true,
+ includePrefix = false,
+ extendedZone = false,
+ format = "extended",
+ } = {}) {
+ if (!this.isValid) {
+ return null;
+ }
+
+ let c = includePrefix ? "T" : "";
+ return (
+ c +
+ toISOTime(
+ this,
+ format === "extended",
+ suppressSeconds,
+ suppressMilliseconds,
+ includeOffset,
+ extendedZone
+ )
+ );
+ }
+
+ /**
+ * Returns an RFC 2822-compatible string representation of this DateTime
+ * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000'
+ * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400'
+ * @return {string}
+ */
+ toRFC2822() {
+ return toTechFormat(this, "EEE, dd LLL yyyy HH:mm:ss ZZZ", false);
+ }
+
+ /**
+ * Returns a string representation of this DateTime appropriate for use in HTTP headers. The output is always expressed in GMT.
+ * Specifically, the string conforms to RFC 1123.
+ * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
+ * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT'
+ * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT'
+ * @return {string}
+ */
+ toHTTP() {
+ return toTechFormat(this.toUTC(), "EEE, dd LLL yyyy HH:mm:ss 'GMT'");
+ }
+
+ /**
+ * Returns a string representation of this DateTime appropriate for use in SQL Date
+ * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'
+ * @return {string}
+ */
+ toSQLDate() {
+ if (!this.isValid) {
+ return null;
+ }
+ return toISODate(this, true);
+ }
+
+ /**
+ * Returns a string representation of this DateTime appropriate for use in SQL Time
+ * @param {Object} opts - options
+ * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.
+ * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
+ * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00'
+ * @example DateTime.utc().toSQL() //=> '05:15:16.345'
+ * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00'
+ * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345'
+ * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York'
+ * @return {string}
+ */
+ toSQLTime({ includeOffset = true, includeZone = false, includeOffsetSpace = true } = {}) {
+ let fmt = "HH:mm:ss.SSS";
+
+ if (includeZone || includeOffset) {
+ if (includeOffsetSpace) {
+ fmt += " ";
+ }
+ if (includeZone) {
+ fmt += "z";
+ } else if (includeOffset) {
+ fmt += "ZZ";
+ }
+ }
+
+ return toTechFormat(this, fmt, true);
+ }
+
+ /**
+ * Returns a string representation of this DateTime appropriate for use in SQL DateTime
+ * @param {Object} opts - options
+ * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.
+ * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
+ * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00'
+ * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z'
+ * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00'
+ * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000'
+ * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York'
+ * @return {string}
+ */
+ toSQL(opts = {}) {
+ if (!this.isValid) {
+ return null;
+ }
+
+ return `${this.toSQLDate()} ${this.toSQLTime(opts)}`;
+ }
+
+ /**
+ * Returns a string representation of this DateTime appropriate for debugging
+ * @return {string}
+ */
+ toString() {
+ return this.isValid ? this.toISO() : INVALID;
+ }
+
+ /**
+ * Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime#toMillis}
+ * @return {number}
+ */
+ valueOf() {
+ return this.toMillis();
+ }
+
+ /**
+ * Returns the epoch milliseconds of this DateTime.
+ * @return {number}
+ */
+ toMillis() {
+ return this.isValid ? this.ts : NaN;
+ }
+
+ /**
+ * Returns the epoch seconds of this DateTime.
+ * @return {number}
+ */
+ toSeconds() {
+ return this.isValid ? this.ts / 1000 : NaN;
+ }
+
+ /**
+ * Returns the epoch seconds (as a whole number) of this DateTime.
+ * @return {number}
+ */
+ toUnixInteger() {
+ return this.isValid ? Math.floor(this.ts / 1000) : NaN;
+ }
+
+ /**
+ * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON.
+ * @return {string}
+ */
+ toJSON() {
+ return this.toISO();
+ }
+
+ /**
+ * Returns a BSON serializable equivalent to this DateTime.
+ * @return {Date}
+ */
+ toBSON() {
+ return this.toJSDate();
+ }
+
+ /**
+ * Returns a JavaScript object with this DateTime's year, month, day, and so on.
+ * @param opts - options for generating the object
+ * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output
+ * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 }
+ * @return {Object}
+ */
+ toObject(opts = {}) {
+ if (!this.isValid) return {};
+
+ const base = { ...this.c };
+
+ if (opts.includeConfig) {
+ base.outputCalendar = this.outputCalendar;
+ base.numberingSystem = this.loc.numberingSystem;
+ base.locale = this.loc.locale;
+ }
+ return base;
+ }
+
+ /**
+ * Returns a JavaScript Date equivalent to this DateTime.
+ * @return {Date}
+ */
+ toJSDate() {
+ return new Date(this.isValid ? this.ts : NaN);
+ }
+
+ // COMPARE
+
+ /**
+ * Return the difference between two DateTimes as a Duration.
+ * @param {DateTime} otherDateTime - the DateTime to compare this one to
+ * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration.
+ * @param {Object} opts - options that affect the creation of the Duration
+ * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
+ * @example
+ * var i1 = DateTime.fromISO('1982-05-25T09:45'),
+ * i2 = DateTime.fromISO('1983-10-14T10:30');
+ * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 }
+ * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 }
+ * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 }
+ * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 }
+ * @return {Duration}
+ */
+ diff(otherDateTime, unit = "milliseconds", opts = {}) {
+ if (!this.isValid || !otherDateTime.isValid) {
+ return Duration.invalid("created by diffing an invalid DateTime");
+ }
+
+ const durOpts = { locale: this.locale, numberingSystem: this.numberingSystem, ...opts };
+
+ const units = maybeArray(unit).map(Duration.normalizeUnit),
+ otherIsLater = otherDateTime.valueOf() > this.valueOf(),
+ earlier = otherIsLater ? this : otherDateTime,
+ later = otherIsLater ? otherDateTime : this,
+ diffed = diff(earlier, later, units, durOpts);
+
+ return otherIsLater ? diffed.negate() : diffed;
+ }
+
+ /**
+ * Return the difference between this DateTime and right now.
+ * See {@link DateTime#diff}
+ * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration
+ * @param {Object} opts - options that affect the creation of the Duration
+ * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
+ * @return {Duration}
+ */
+ diffNow(unit = "milliseconds", opts = {}) {
+ return this.diff(DateTime.now(), unit, opts);
+ }
+
+ /**
+ * Return an Interval spanning between this DateTime and another DateTime
+ * @param {DateTime} otherDateTime - the other end point of the Interval
+ * @return {Interval}
+ */
+ until(otherDateTime) {
+ return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;
+ }
+
+ /**
+ * Return whether this DateTime is in the same unit of time as another DateTime.
+ * Higher-order units must also be identical for this function to return `true`.
+ * Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link DateTime#setZone} to convert one of the dates if needed.
+ * @param {DateTime} otherDateTime - the other DateTime
+ * @param {string} unit - the unit of time to check sameness on
+ * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day
+ * @return {boolean}
+ */
+ hasSame(otherDateTime, unit) {
+ if (!this.isValid) return false;
+
+ const inputMs = otherDateTime.valueOf();
+ const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true });
+ return adjustedToZone.startOf(unit) <= inputMs && inputMs <= adjustedToZone.endOf(unit);
+ }
+
+ /**
+ * Equality check
+ * Two DateTimes are equal if and only if they represent the same millisecond, have the same zone and location, and are both valid.
+ * To compare just the millisecond values, use `+dt1 === +dt2`.
+ * @param {DateTime} other - the other DateTime
+ * @return {boolean}
+ */
+ equals(other) {
+ return (
+ this.isValid &&
+ other.isValid &&
+ this.valueOf() === other.valueOf() &&
+ this.zone.equals(other.zone) &&
+ this.loc.equals(other.loc)
+ );
+ }
+
+ /**
+ * Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your
+ * platform supports Intl.RelativeTimeFormat. Rounds down by default.
+ * @param {Object} options - options that affect the output
+ * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
+ * @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow"
+ * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds"
+ * @param {boolean} [options.round=true] - whether to round the numbers in the output.
+ * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.
+ * @param {string} options.locale - override the locale of this DateTime
+ * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
+ * @example DateTime.now().plus({ days: 1 }).toRelative() //=> "in 1 day"
+ * @example DateTime.now().setLocale("es").toRelative({ days: 1 }) //=> "dentro de 1 día"
+ * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: "fr" }) //=> "dans 23 heures"
+ * @example DateTime.now().minus({ days: 2 }).toRelative() //=> "2 days ago"
+ * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: "hours" }) //=> "48 hours ago"
+ * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> "1.5 days ago"
+ */
+ toRelative(options = {}) {
+ if (!this.isValid) return null;
+ const base = options.base || DateTime.fromObject({}, { zone: this.zone }),
+ padding = options.padding ? (this < base ? -options.padding : options.padding) : 0;
+ let units = ["years", "months", "days", "hours", "minutes", "seconds"];
+ let unit = options.unit;
+ if (Array.isArray(options.unit)) {
+ units = options.unit;
+ unit = undefined;
+ }
+ return diffRelative(base, this.plus(padding), {
+ ...options,
+ numeric: "always",
+ units,
+ unit,
+ });
+ }
+
+ /**
+ * Returns a string representation of this date relative to today, such as "yesterday" or "next month".
+ * Only internationalizes on platforms that supports Intl.RelativeTimeFormat.
+ * @param {Object} options - options that affect the output
+ * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
+ * @param {string} options.locale - override the locale of this DateTime
+ * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of "years", "quarters", "months", "weeks", or "days"
+ * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
+ * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> "tomorrow"
+ * @example DateTime.now().setLocale("es").plus({ days: 1 }).toRelative() //=> ""mañana"
+ * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: "fr" }) //=> "demain"
+ * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> "2 days ago"
+ */
+ toRelativeCalendar(options = {}) {
+ if (!this.isValid) return null;
+
+ return diffRelative(options.base || DateTime.fromObject({}, { zone: this.zone }), this, {
+ ...options,
+ numeric: "auto",
+ units: ["years", "months", "days"],
+ calendary: true,
+ });
+ }
+
+ /**
+ * Return the min of several date times
+ * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum
+ * @return {DateTime} the min DateTime, or undefined if called with no argument
+ */
+ static min(...dateTimes) {
+ if (!dateTimes.every(DateTime.isDateTime)) {
+ throw new InvalidArgumentError("min requires all arguments be DateTimes");
+ }
+ return bestBy(dateTimes, (i) => i.valueOf(), Math.min);
+ }
+
+ /**
+ * Return the max of several date times
+ * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum
+ * @return {DateTime} the max DateTime, or undefined if called with no argument
+ */
+ static max(...dateTimes) {
+ if (!dateTimes.every(DateTime.isDateTime)) {
+ throw new InvalidArgumentError("max requires all arguments be DateTimes");
+ }
+ return bestBy(dateTimes, (i) => i.valueOf(), Math.max);
+ }
+
+ // MISC
+
+ /**
+ * Explain how a string would be parsed by fromFormat()
+ * @param {string} text - the string to parse
+ * @param {string} fmt - the format the string is expected to be in (see description)
+ * @param {Object} options - options taken by fromFormat()
+ * @return {Object}
+ */
+ static fromFormatExplain(text, fmt, options = {}) {
+ const { locale = null, numberingSystem = null } = options,
+ localeToUse = Locale.fromOpts({
+ locale,
+ numberingSystem,
+ defaultToEN: true,
+ });
+ return explainFromTokens(localeToUse, text, fmt);
+ }
+
+ /**
+ * @deprecated use fromFormatExplain instead
+ */
+ static fromStringExplain(text, fmt, options = {}) {
+ return DateTime.fromFormatExplain(text, fmt, options);
+ }
+
+ // FORMAT PRESETS
+
+ /**
+ * {@link DateTime#toLocaleString} format like 10/14/1983
+ * @type {Object}
+ */
+ static get DATE_SHORT() {
+ return DATE_SHORT;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Oct 14, 1983'
+ * @type {Object}
+ */
+ static get DATE_MED() {
+ return DATE_MED;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Fri, Oct 14, 1983'
+ * @type {Object}
+ */
+ static get DATE_MED_WITH_WEEKDAY() {
+ return DATE_MED_WITH_WEEKDAY;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'October 14, 1983'
+ * @type {Object}
+ */
+ static get DATE_FULL() {
+ return DATE_FULL;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Tuesday, October 14, 1983'
+ * @type {Object}
+ */
+ static get DATE_HUGE() {
+ return DATE_HUGE;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get TIME_SIMPLE() {
+ return TIME_SIMPLE;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get TIME_WITH_SECONDS() {
+ return TIME_WITH_SECONDS;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get TIME_WITH_SHORT_OFFSET() {
+ return TIME_WITH_SHORT_OFFSET;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get TIME_WITH_LONG_OFFSET() {
+ return TIME_WITH_LONG_OFFSET;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30', always 24-hour.
+ * @type {Object}
+ */
+ static get TIME_24_SIMPLE() {
+ return TIME_24_SIMPLE;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30:23', always 24-hour.
+ * @type {Object}
+ */
+ static get TIME_24_WITH_SECONDS() {
+ return TIME_24_WITH_SECONDS;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30:23 EDT', always 24-hour.
+ * @type {Object}
+ */
+ static get TIME_24_WITH_SHORT_OFFSET() {
+ return TIME_24_WITH_SHORT_OFFSET;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour.
+ * @type {Object}
+ */
+ static get TIME_24_WITH_LONG_OFFSET() {
+ return TIME_24_WITH_LONG_OFFSET;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_SHORT() {
+ return DATETIME_SHORT;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_SHORT_WITH_SECONDS() {
+ return DATETIME_SHORT_WITH_SECONDS;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_MED() {
+ return DATETIME_MED;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_MED_WITH_SECONDS() {
+ return DATETIME_MED_WITH_SECONDS;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_MED_WITH_WEEKDAY() {
+ return DATETIME_MED_WITH_WEEKDAY;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_FULL() {
+ return DATETIME_FULL;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_FULL_WITH_SECONDS() {
+ return DATETIME_FULL_WITH_SECONDS;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_HUGE() {
+ return DATETIME_HUGE;
+ }
+
+ /**
+ * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is.
+ * @type {Object}
+ */
+ static get DATETIME_HUGE_WITH_SECONDS() {
+ return DATETIME_HUGE_WITH_SECONDS;
+ }
+}
+
+/**
+ * @private
+ */
+function friendlyDateTime(dateTimeish) {
+ if (DateTime.isDateTime(dateTimeish)) {
+ return dateTimeish;
+ } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) {
+ return DateTime.fromJSDate(dateTimeish);
+ } else if (dateTimeish && typeof dateTimeish === "object") {
+ return DateTime.fromObject(dateTimeish);
+ } else {
+ throw new InvalidArgumentError(
+ `Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}`
+ );
+ }
+}
+
+const VERSION = "3.4.3";
+
+var Luxon = /*#__PURE__*/Object.freeze({
+ __proto__: null,
+ VERSION: VERSION,
+ DateTime: DateTime,
+ Duration: Duration,
+ Interval: Interval,
+ Info: Info,
+ Zone: Zone,
+ FixedOffsetZone: FixedOffsetZone,
+ IANAZone: IANAZone,
+ InvalidZone: InvalidZone,
+ SystemZone: SystemZone,
+ Settings: Settings
+});
+
+////////////////////
+// Query Settings //
+////////////////////
+const DEFAULT_QUERY_SETTINGS = {
+ renderNullAs: "\\-",
+ taskCompletionTracking: false,
+ taskCompletionUseEmojiShorthand: false,
+ taskCompletionText: "completion",
+ taskCompletionDateFormat: "yyyy-MM-dd",
+ recursiveSubTaskCompletion: false,
+ warnOnEmptyResult: true,
+ refreshEnabled: true,
+ refreshInterval: 2500,
+ defaultDateFormat: "MMMM dd, yyyy",
+ defaultDateTimeFormat: "h:mm a - MMMM dd, yyyy",
+ maxRecursiveRenderDepth: 4,
+ tableIdColumnName: "File",
+ tableGroupColumnName: "Group",
+ showResultCount: true,
+};
+const DEFAULT_EXPORT_SETTINGS = {
+ allowHtml: true,
+};
+/** Default settings for dataview on install. */
+const DEFAULT_SETTINGS = {
+ ...DEFAULT_QUERY_SETTINGS,
+ ...DEFAULT_EXPORT_SETTINGS,
+ ...{
+ inlineQueryPrefix: "=",
+ inlineJsQueryPrefix: "$=",
+ inlineQueriesInCodeblocks: true,
+ enableInlineDataview: true,
+ enableDataviewJs: false,
+ enableInlineDataviewJs: false,
+ prettyRenderInlineFields: true,
+ prettyRenderInlineFieldsInLivePreview: true,
+ dataviewJsKeyword: "dataviewjs",
+ },
+};
+
+/** Functional return type for error handling. */
+class Success {
+ value;
+ successful;
+ constructor(value) {
+ this.value = value;
+ this.successful = true;
+ }
+ map(f) {
+ return new Success(f(this.value));
+ }
+ flatMap(f) {
+ return f(this.value);
+ }
+ mapErr(f) {
+ return this;
+ }
+ bimap(succ, _fail) {
+ return this.map(succ);
+ }
+ orElse(_value) {
+ return this.value;
+ }
+ cast() {
+ return this;
+ }
+ orElseThrow(_message) {
+ return this.value;
+ }
+}
+/** Functional return type for error handling. */
+class Failure {
+ error;
+ successful;
+ constructor(error) {
+ this.error = error;
+ this.successful = false;
+ }
+ map(_f) {
+ return this;
+ }
+ flatMap(_f) {
+ return this;
+ }
+ mapErr(f) {
+ return new Failure(f(this.error));
+ }
+ bimap(_succ, fail) {
+ return this.mapErr(fail);
+ }
+ orElse(value) {
+ return value;
+ }
+ cast() {
+ return this;
+ }
+ orElseThrow(message) {
+ if (message)
+ throw new Error(message(this.error));
+ else
+ throw new Error("" + this.error);
+ }
+}
+/** Monadic 'Result' type which encapsulates whether a procedure succeeded or failed, as well as it's return value. */
+var Result;
+(function (Result) {
+ /** Construct a new success result wrapping the given value. */
+ function success(value) {
+ return new Success(value);
+ }
+ Result.success = success;
+ /** Construct a new failure value wrapping the given error. */
+ function failure(error) {
+ return new Failure(error);
+ }
+ Result.failure = failure;
+ /** Join two results with a bi-function and return a new result. */
+ function flatMap2(first, second, f) {
+ if (first.successful) {
+ if (second.successful)
+ return f(first.value, second.value);
+ else
+ return failure(second.error);
+ }
+ else {
+ return failure(first.error);
+ }
+ }
+ Result.flatMap2 = flatMap2;
+ /** Join two results with a bi-function and return a new result. */
+ function map2(first, second, f) {
+ return flatMap2(first, second, (a, b) => success(f(a, b)));
+ }
+ Result.map2 = map2;
+})(Result || (Result = {}));
+
+var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+function getDefaultExportFromCjs (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+}
+
+var parsimmon_umd_min = {exports: {}};
+
+parsimmon_umd_min.exports;
+
+(function (module, exports) {
+ !function(n,t){module.exports=t();}("undefined"!=typeof self?self:commonjsGlobal,function(){return function(n){var t={};function r(e){if(t[e])return t[e].exports;var u=t[e]={i:e,l:!1,exports:{}};return n[e].call(u.exports,u,u.exports,r),u.l=!0,u.exports}return r.m=n,r.c=t,r.d=function(n,t,e){r.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:e});},r.r=function(n){Object.defineProperty(n,"__esModule",{value:!0});},r.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},r.p="",r(r.s=0)}([function(n,t,r){function e(n){if(!(this instanceof e))return new e(n);this._=n;}var u=e.prototype;function o(n,t){for(var r=0;r>7),buf:function(n){var t=i(function(n,t,r,e){return n.concat(r===e.length-1?Buffer.from([t,0]).readUInt16BE(0):e.readUInt16BE(r))},[],n);return Buffer.from(a(function(n){return (n<<1&65535)>>8},t))}(r.buf)};}),r}function c(){return "undefined"!=typeof Buffer}function s(){if(!c())throw new Error("Buffer global does not exist; please use webpack if you need to parse Buffers in the browser.")}function l(n){s();var t=i(function(n,t){return n+t},0,n);if(t%8!=0)throw new Error("The bits ["+n.join(", ")+"] add up to "+t+" which is not an even number of bytes; the total should be divisible by 8");var r,u=t/8,o=(r=function(n){return n>48},i(function(n,t){return n||(r(t)?t:n)},null,n));if(o)throw new Error(o+" bit range requested exceeds 48 bit (6 byte) Number max.");return new e(function(t,r){var e=u+r;return e>t.length?x(r,u.toString()+" bytes"):b(e,i(function(n,t){var r=f(t,n.buf);return {coll:n.coll.concat(r.v),buf:r.buf}},{coll:[],buf:t.slice(r,e)},n).coll)})}function h(n,t){return new e(function(r,e){return s(),e+t>r.length?x(e,t+" bytes for "+n):b(e+t,r.slice(e,e+t))})}function p(n,t){if("number"!=typeof(r=t)||Math.floor(r)!==r||t<0||t>6)throw new Error(n+" requires integer length in range [0, 6].");var r;}function d(n){return p("uintBE",n),h("uintBE("+n+")",n).map(function(t){return t.readUIntBE(0,n)})}function v(n){return p("uintLE",n),h("uintLE("+n+")",n).map(function(t){return t.readUIntLE(0,n)})}function g(n){return p("intBE",n),h("intBE("+n+")",n).map(function(t){return t.readIntBE(0,n)})}function m(n){return p("intLE",n),h("intLE("+n+")",n).map(function(t){return t.readIntLE(0,n)})}function y(n){return n instanceof e}function E(n){return "[object Array]"==={}.toString.call(n)}function w(n){return c()&&Buffer.isBuffer(n)}function b(n,t){return {status:!0,index:n,value:t,furthest:-1,expected:[]}}function x(n,t){return E(t)||(t=[t]),{status:!1,index:-1,value:null,furthest:n,expected:t}}function B(n,t){if(!t)return n;if(n.furthest>t.furthest)return n;var r=n.furthest===t.furthest?function(n,t){if(function(){if(void 0!==e._supportsSet)return e._supportsSet;var n="undefined"!=typeof Set;return e._supportsSet=n,n}()&&Array.from){for(var r=new Set(n),u=0;u=0;){if(i in r){e=r[i].line,0===o&&(o=r[i].lineStart);break}("\n"===n.charAt(i)||"\r"===n.charAt(i)&&"\n"!==n.charAt(i+1))&&(u++,0===o&&(o=i+1)),i--;}var a=e+u,f=t-o;return r[t]={line:a,lineStart:o},{offset:t,line:a+1,column:f+1}}function _(n){if(!y(n))throw new Error("not a parser: "+n)}function L(n,t){return "string"==typeof n?n.charAt(t):n[t]}function O(n){if("number"!=typeof n)throw new Error("not a number: "+n)}function k(n){if("function"!=typeof n)throw new Error("not a function: "+n)}function P(n){if("string"!=typeof n)throw new Error("not a string: "+n)}var q=2,A=3,I=8,F=5*I,M=4*I,z=" ";function R(n,t){return new Array(t+1).join(n)}function U(n,t,r){var e=t-n.length;return e<=0?n:R(r,e)+n}function W(n,t,r,e){return {from:n-t>0?n-t:0,to:n+r>e?e:n+r}}function D(n,t){var r,e,u,o,f,c=t.index,s=c.offset,l=1;if(s===n.length)return "Got the end of the input";if(w(n)){var h=s-s%I,p=s-h,d=W(h,F,M+I,n.length),v=a(function(n){return a(function(n){return U(n.toString(16),2,"0")},n)},function(n,t){var r=n.length,e=[],u=0;if(r<=t)return [n.slice()];for(var o=0;o=4&&(r+=1),l=2,u=a(function(n){return n.length<=4?n.join(" "):n.slice(0,4).join(" ")+" "+n.slice(4).join(" ")},v),(f=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(f=2);}else {var g=n.split(/\r\n|[\n\r\u2028\u2029]/);r=c.column-1,e=c.line-1,o=W(e,q,A,g.length),u=g.slice(o.from,o.to),f=o.to.toString().length;}var m=e-o.from;return w(n)&&(f=(8*(o.to>0?o.to-1:o.to)).toString(16).length)<2&&(f=2),i(function(t,e,u){var i,a=u===m,c=a?"> ":z;return i=w(n)?U((8*(o.from+u)).toString(16),f,"0"):U((o.from+u+1).toString(),f," "),[].concat(t,[c+i+" | "+e],a?[z+R(" ",f)+" | "+U("",r," ")+R("^",l)]:[])},[],u).join("\n")}function N(n,t){return ["\n","-- PARSING FAILED "+R("-",50),"\n\n",D(n,t),"\n\n",(r=t.expected,1===r.length?"Expected:\n\n"+r[0]:"Expected one of the following: \n\n"+r.join(", ")),"\n"].join("");var r;}function G(n){return void 0!==n.flags?n.flags:[n.global?"g":"",n.ignoreCase?"i":"",n.multiline?"m":"",n.unicode?"u":"",n.sticky?"y":""].join("")}function C(){for(var n=[].slice.call(arguments),t=n.length,r=0;r=2?O(t):t=0;var r=function(n){return RegExp("^(?:"+n.source+")",G(n))}(n),u=""+n;return e(function(n,e){var o=r.exec(n.slice(e));if(o){if(0<=t&&t<=o.length){var i=o[0],a=o[t];return b(e+i.length,a)}return x(e,"valid match group (0 to "+o.length+") in "+u)}return x(e,u)})}function X(n){return e(function(t,r){return b(r,n)})}function Y(n){return e(function(t,r){return x(r,n)})}function Z(n){if(y(n))return e(function(t,r){var e=n._(t,r);return e.index=r,e.value="",e});if("string"==typeof n)return Z(K(n));if(n instanceof RegExp)return Z(Q(n));throw new Error("not a string, regexp, or parser: "+n)}function $(n){return _(n),e(function(t,r){var e=n._(t,r),u=t.slice(r,e.index);return e.status?x(r,'not "'+u+'"'):b(r,null)})}function nn(n){return k(n),e(function(t,r){var e=L(t,r);return r=n.length?x(t,"any character/byte"):b(t+1,L(n,t))}),on=e(function(n,t){return b(n.length,n.slice(t))}),an=e(function(n,t){return t=0}).desc(t)},e.optWhitespace=hn,e.Parser=e,e.range=function(n,t){return nn(function(r){return n<=r&&r<=t}).desc(n+"-"+t)},e.regex=Q,e.regexp=Q,e.sepBy=V,e.sepBy1=H,e.seq=C,e.seqMap=J,e.seqObj=function(){for(var n,t={},r=0,u=(n=arguments,Array.prototype.slice.call(n)),o=u.length,i=0;i255)throw new Error("Value specified to byte constructor ("+n+"=0x"+n.toString(16)+") is larger in value than a single byte.");var t=(n>15?"0x":"0x0")+n.toString(16);return e(function(r,e){var u=L(r,e);return u===n?b(e+1,u):x(e,t)})},buffer:function(n){return h("buffer",n).map(function(n){return Buffer.from(n)})},encodedString:function(n,t){return h("string",t).map(function(t){return t.toString(n)})},uintBE:d,uint8BE:d(1),uint16BE:d(2),uint32BE:d(4),uintLE:v,uint8LE:v(1),uint16LE:v(2),uint32LE:v(4),intBE:g,int8BE:g(1),int16BE:g(2),int32BE:g(4),intLE:m,int8LE:m(1),int16LE:m(2),int32LE:m(4),floatBE:h("floatBE",4).map(function(n){return n.readFloatBE(0)}),floatLE:h("floatLE",4).map(function(n){return n.readFloatLE(0)}),doubleBE:h("doubleBE",8).map(function(n){return n.readDoubleBE(0)}),doubleLE:h("doubleLE",8).map(function(n){return n.readDoubleLE(0)})},n.exports=e;}])});
+} (parsimmon_umd_min, parsimmon_umd_min.exports));
+
+var parsimmon_umd_minExports = parsimmon_umd_min.exports;
+
+var emojiRegex = () => {
+ // https://mths.be/emoji
+ return /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26F9(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC08\uDC26](?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC2\uDECE-\uDEDB\uDEE0-\uDEE8]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;
+};
+
+var removeMarkdown = function(md, options) {
+ options = options || {};
+ options.listUnicodeChar = options.hasOwnProperty('listUnicodeChar') ? options.listUnicodeChar : false;
+ options.stripListLeaders = options.hasOwnProperty('stripListLeaders') ? options.stripListLeaders : true;
+ options.gfm = options.hasOwnProperty('gfm') ? options.gfm : true;
+ options.useImgAltText = options.hasOwnProperty('useImgAltText') ? options.useImgAltText : true;
+ options.abbr = options.hasOwnProperty('abbr') ? options.abbr : false;
+ options.replaceLinksWithURL = options.hasOwnProperty('replaceLinksWithURL') ? options.replaceLinksWithURL : false;
+ options.htmlTagsToSkip = options.hasOwnProperty('htmlTagsToSkip') ? options.htmlTagsToSkip : [];
+ options.throwError = options.hasOwnProperty('throwError') ? options.throwError : false;
+
+ var output = md || '';
+
+ // Remove horizontal rules (stripListHeaders conflict with this rule, which is why it has been moved to the top)
+ output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*/gm, '');
+
+ try {
+ if (options.stripListLeaders) {
+ if (options.listUnicodeChar)
+ output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, options.listUnicodeChar + ' $1');
+ else
+ output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, '$1');
+ }
+ if (options.gfm) {
+ output = output
+ // Header
+ .replace(/\n={2,}/g, '\n')
+ // Fenced codeblocks
+ .replace(/~{3}.*\n/g, '')
+ // Strikethrough
+ .replace(/~~/g, '')
+ // Fenced codeblocks
+ .replace(/`{3}.*\n/g, '');
+ }
+ if (options.abbr) {
+ // Remove abbreviations
+ output = output.replace(/\*\[.*\]:.*\n/, '');
+ }
+ output = output
+ // Remove HTML tags
+ .replace(/<[^>]*>/g, '');
+
+ var htmlReplaceRegex = new RegExp('<[^>]*>', 'g');
+ if (options.htmlTagsToSkip.length > 0) {
+ // Using negative lookahead. Eg. (?!sup|sub) will not match 'sup' and 'sub' tags.
+ var joinedHtmlTagsToSkip = '(?!' + options.htmlTagsToSkip.join("|") + ')';
+
+ // Adding the lookahead literal with the default regex for html. Eg./<(?!sup|sub)[^>]*>/ig
+ htmlReplaceRegex = new RegExp(
+ '<' +
+ joinedHtmlTagsToSkip +
+ '[^>]*>',
+ 'ig'
+ );
+ }
+
+ output = output
+ // Remove HTML tags
+ .replace(htmlReplaceRegex, '')
+ // Remove setext-style headers
+ .replace(/^[=\-]{2,}\s*$/g, '')
+ // Remove footnotes?
+ .replace(/\[\^.+?\](\: .*?$)?/g, '')
+ .replace(/\s{0,2}\[.*?\]: .*?$/g, '')
+ // Remove images
+ .replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? '$1' : '')
+ // Remove inline links
+ .replace(/\[([^\]]*?)\][\[\(].*?[\]\)]/g, options.replaceLinksWithURL ? '$2' : '$1')
+ // Remove blockquotes
+ .replace(/^(\n)?\s{0,3}>\s?/gm, '$1')
+ // .replace(/(^|\n)\s{0,3}>\s?/g, '\n\n')
+ // Remove reference-style links?
+ .replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
+ // Remove atx-style headers
+ .replace(/^(\n)?\s{0,}#{1,6}\s*( (.+))? +#+$|^(\n)?\s{0,}#{1,6}\s*( (.+))?$/gm, '$1$3$4$6')
+ // Remove * emphasis
+ .replace(/([\*]+)(\S)(.*?\S)??\1/g, '$2$3')
+ // Remove _ emphasis. Unlike *, _ emphasis gets rendered only if
+ // 1. Either there is a whitespace character before opening _ and after closing _.
+ // 2. Or _ is at the start/end of the string.
+ .replace(/(^|\W)([_]+)(\S)(.*?\S)??\2($|\W)/g, '$1$3$4$5')
+ // Remove code blocks
+ .replace(/(`{3,})(.*?)\1/gm, '$2')
+ // Remove inline code
+ .replace(/`(.+?)`/g, '$1')
+ // // Replace two or more newlines with exactly two? Not entirely sure this belongs here...
+ // .replace(/\n{2,}/g, '\n\n')
+ // // Remove newlines in a paragraph
+ // .replace(/(\S+)\n\s*(\S+)/g, '$1 $2')
+ // Replace strike through
+ .replace(/~(.*?)~/g, '$1');
+ } catch(e) {
+ if (options.throwError) throw e;
+
+ console.error("remove-markdown encountered error: %s", e);
+ return md;
+ }
+ return output;
+};
+
+var removeMd = /*@__PURE__*/getDefaultExportFromCjs(removeMarkdown);
+
+/** Normalize a duration to all of the proper units. */
+function normalizeDuration(dur) {
+ if (dur === undefined || dur === null)
+ return dur;
+ return dur.shiftToAll().normalize();
+}
+/** Strip the time components of a date time object. */
+function stripTime(dt) {
+ if (dt === null || dt === undefined)
+ return dt;
+ return DateTime.fromObject({
+ year: dt.year,
+ month: dt.month,
+ day: dt.day,
+ });
+}
+/** Get the folder containing the given path (i.e., like computing 'path/..'). */
+function getParentFolder(path) {
+ return path.split("/").slice(0, -1).join("/");
+}
+/** Get the "title" for a file, by stripping other parts of the path as well as the extension. */
+function getFileTitle(path) {
+ if (path.includes("/"))
+ path = path.substring(path.lastIndexOf("/") + 1);
+ if (path.endsWith(".md"))
+ path = path.substring(0, path.length - 3);
+ return path;
+}
+/** Get the extension of a file from the file path. */
+function getExtension(path) {
+ if (!path.includes("."))
+ return "";
+ return path.substring(path.lastIndexOf(".") + 1);
+}
+/** Parse all subtags out of the given tag. I.e., #hello/i/am would yield [#hello/i/am, #hello/i, #hello]. */
+function extractSubtags(tag) {
+ let result = [tag];
+ while (tag.includes("/")) {
+ tag = tag.substring(0, tag.lastIndexOf("/"));
+ result.push(tag);
+ }
+ return result;
+}
+/** Try calling the given function; on failure, return the error message. */
+function tryOrPropagate(func) {
+ try {
+ return func();
+ }
+ catch (error) {
+ return Result.failure("" + error + "\n\n" + error.stack);
+ }
+}
+/** Try asynchronously calling the given function; on failure, return the error message. */
+async function asyncTryOrPropagate(func) {
+ try {
+ return await func();
+ }
+ catch (error) {
+ return Result.failure("" + error + "\n\n" + error.stack);
+ }
+}
+/**
+ * Escape regex characters in a string.
+ * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions.
+ */
+function escapeRegex(str) {
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+/** A parsimmon parser which canonicalizes variable names while properly respecting emoji. */
+const VAR_NAME_CANONICALIZER = parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regex(new RegExp(emojiRegex(), "")), parsimmon_umd_minExports.regex(/[0-9\p{Letter}_-]+/u).map(str => str.toLocaleLowerCase()), parsimmon_umd_minExports.whitespace.map(_ => "-"), parsimmon_umd_minExports.any.map(_ => ""))
+ .many()
+ .map(result => result.join(""));
+/** Convert an arbitrary variable name into something JS/query friendly. */
+function canonicalizeVarName(name) {
+ return VAR_NAME_CANONICALIZER.tryParse(name);
+}
+const HEADER_CANONICALIZER = parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regex(new RegExp(emojiRegex(), "")), parsimmon_umd_minExports.regex(/[0-9\p{Letter}_-]+/u), parsimmon_umd_minExports.whitespace.map(_ => " "), parsimmon_umd_minExports.any.map(_ => " "))
+ .many()
+ .map(result => {
+ return result.join("").split(/\s+/).join(" ").trim();
+});
+/**
+ * Normalizes the text in a header to be something that is actually linkable to. This mimics
+ * how Obsidian does it's normalization, collapsing repeated spaces and stripping out control characters.
+ */
+function normalizeHeaderForLink(header) {
+ return HEADER_CANONICALIZER.tryParse(header);
+}
+/** Render a DateTime in a minimal format to save space. */
+function renderMinimalDate(time, settings, locale) {
+ // If there is no relevant time specified, fall back to just rendering the date.
+ if (time.second == 0 && time.minute == 0 && time.hour == 0) {
+ return time.toLocal().toFormat(settings.defaultDateFormat, { locale });
+ }
+ return time.toLocal().toFormat(settings.defaultDateTimeFormat, { locale });
+}
+/** Render a duration in a minimal format to save space. */
+function renderMinimalDuration(dur) {
+ dur = normalizeDuration(dur);
+ // toHuman outputs zero quantities e.g. "0 seconds"
+ dur = Duration.fromObject(Object.fromEntries(Object.entries(dur.toObject()).filter(([, quantity]) => quantity != 0)));
+ return dur.toHuman();
+}
+/** Determine if two sets are equal in contents. */
+function setsEqual(first, second) {
+ if (first.size != second.size)
+ return false;
+ for (let elem of first)
+ if (!second.has(elem))
+ return false;
+ return true;
+}
+/** Normalize a markdown string. Removes all markdown tags and obsidian links. */
+function normalizeMarkdown(str) {
+ // [[test]] -> test
+ let interim = str.replace(/\[\[([^\|]*?)\]\]/g, "$1");
+ // [[test|test]] -> test
+ interim = interim.replace(/\[\[.*?\|(.*?)\]\]/, "$1");
+ // remove markdown tags
+ interim = removeMd(interim);
+ return interim;
+}
+
+var Values;
+(function (Values) {
+ /** Convert an arbitrary value into a reasonable, Markdown-friendly string if possible. */
+ function toString(field, setting = DEFAULT_QUERY_SETTINGS, recursive = false) {
+ let wrapped = wrapValue(field);
+ if (!wrapped)
+ return setting.renderNullAs;
+ switch (wrapped.type) {
+ case "null":
+ return setting.renderNullAs;
+ case "string":
+ return wrapped.value;
+ case "number":
+ case "boolean":
+ return "" + wrapped.value;
+ case "html":
+ return wrapped.value.outerHTML;
+ case "widget":
+ return wrapped.value.markdown();
+ case "link":
+ return wrapped.value.markdown();
+ case "function":
+ return "";
+ case "array":
+ let result = "";
+ if (recursive)
+ result += "[";
+ result += wrapped.value.map(f => toString(f, setting, true)).join(", ");
+ if (recursive)
+ result += "]";
+ return result;
+ case "object":
+ return ("{ " +
+ Object.entries(wrapped.value)
+ .map(e => e[0] + ": " + toString(e[1], setting, true))
+ .join(", ") +
+ " }");
+ case "date":
+ if (wrapped.value.second == 0 && wrapped.value.hour == 0 && wrapped.value.minute == 0) {
+ return wrapped.value.toFormat(setting.defaultDateFormat);
+ }
+ return wrapped.value.toFormat(setting.defaultDateTimeFormat);
+ case "duration":
+ return renderMinimalDuration(wrapped.value);
+ }
+ }
+ Values.toString = toString;
+ /** Wrap a literal value so you can switch on it easily. */
+ function wrapValue(val) {
+ if (isNull(val))
+ return { type: "null", value: val };
+ else if (isNumber(val))
+ return { type: "number", value: val };
+ else if (isString(val))
+ return { type: "string", value: val };
+ else if (isBoolean(val))
+ return { type: "boolean", value: val };
+ else if (isDuration(val))
+ return { type: "duration", value: val };
+ else if (isDate(val))
+ return { type: "date", value: val };
+ else if (isWidget(val))
+ return { type: "widget", value: val };
+ else if (isArray(val))
+ return { type: "array", value: val };
+ else if (isLink(val))
+ return { type: "link", value: val };
+ else if (isFunction(val))
+ return { type: "function", value: val };
+ else if (isHtml(val))
+ return { type: "html", value: val };
+ else if (isObject(val))
+ return { type: "object", value: val };
+ else
+ return undefined;
+ }
+ Values.wrapValue = wrapValue;
+ /** Recursively map complex objects at the leaves. */
+ function mapLeaves(val, func) {
+ if (isObject(val)) {
+ let result = {};
+ for (let [key, value] of Object.entries(val))
+ result[key] = mapLeaves(value, func);
+ return result;
+ }
+ else if (isArray(val)) {
+ let result = [];
+ for (let value of val)
+ result.push(mapLeaves(value, func));
+ return result;
+ }
+ else {
+ return func(val);
+ }
+ }
+ Values.mapLeaves = mapLeaves;
+ /** Compare two arbitrary JavaScript values. Produces a total ordering over ANY possible dataview value. */
+ function compareValue(val1, val2, linkNormalizer) {
+ // Handle undefined/nulls first.
+ if (val1 === undefined)
+ val1 = null;
+ if (val2 === undefined)
+ val2 = null;
+ if (val1 === null && val2 === null)
+ return 0;
+ else if (val1 === null)
+ return -1;
+ else if (val2 === null)
+ return 1;
+ // A non-null value now which we can wrap & compare on.
+ let wrap1 = wrapValue(val1);
+ let wrap2 = wrapValue(val2);
+ if (wrap1 === undefined && wrap2 === undefined)
+ return 0;
+ else if (wrap1 === undefined)
+ return -1;
+ else if (wrap2 === undefined)
+ return 1;
+ // Short-circuit on different types or on reference equality.
+ if (wrap1.type != wrap2.type)
+ return wrap1.type.localeCompare(wrap2.type);
+ if (wrap1.value === wrap2.value)
+ return 0;
+ switch (wrap1.type) {
+ case "string":
+ return wrap1.value.localeCompare(wrap2.value);
+ case "number":
+ if (wrap1.value < wrap2.value)
+ return -1;
+ else if (wrap1.value == wrap2.value)
+ return 0;
+ return 1;
+ case "null":
+ return 0;
+ case "boolean":
+ if (wrap1.value == wrap2.value)
+ return 0;
+ else
+ return wrap1.value ? 1 : -1;
+ case "link":
+ let link1 = wrap1.value;
+ let link2 = wrap2.value;
+ let normalize = linkNormalizer ?? ((x) => x);
+ // We can't compare by file name or display, since that would break link equality. Compare by path.
+ let pathCompare = normalize(link1.path).localeCompare(normalize(link2.path));
+ if (pathCompare != 0)
+ return pathCompare;
+ // Then compare by type.
+ let typeCompare = link1.type.localeCompare(link2.type);
+ if (typeCompare != 0)
+ return typeCompare;
+ // Then compare by subpath existence.
+ if (link1.subpath && !link2.subpath)
+ return 1;
+ if (!link1.subpath && link2.subpath)
+ return -1;
+ if (!link1.subpath && !link2.subpath)
+ return 0;
+ // Since both have a subpath, compare by subpath.
+ return (link1.subpath ?? "").localeCompare(link2.subpath ?? "");
+ case "date":
+ return wrap1.value < wrap2.value
+ ? -1
+ : wrap1.value.equals(wrap2.value)
+ ? 0
+ : 1;
+ case "duration":
+ return wrap1.value < wrap2.value
+ ? -1
+ : wrap1.value.equals(wrap2.value)
+ ? 0
+ : 1;
+ case "array":
+ let f1 = wrap1.value;
+ let f2 = wrap2.value;
+ for (let index = 0; index < Math.min(f1.length, f2.length); index++) {
+ let comp = compareValue(f1[index], f2[index]);
+ if (comp != 0)
+ return comp;
+ }
+ return f1.length - f2.length;
+ case "object":
+ let o1 = wrap1.value;
+ let o2 = wrap2.value;
+ let k1 = Array.from(Object.keys(o1));
+ let k2 = Array.from(Object.keys(o2));
+ k1.sort();
+ k2.sort();
+ let keyCompare = compareValue(k1, k2);
+ if (keyCompare != 0)
+ return keyCompare;
+ for (let key of k1) {
+ let comp = compareValue(o1[key], o2[key]);
+ if (comp != 0)
+ return comp;
+ }
+ return 0;
+ case "widget":
+ case "html":
+ case "function":
+ return 0;
+ }
+ }
+ Values.compareValue = compareValue;
+ /** Find the corresponding Dataview type for an arbitrary value. */
+ function typeOf(val) {
+ return wrapValue(val)?.type;
+ }
+ Values.typeOf = typeOf;
+ /** Determine if the given value is "truthy" (i.e., is non-null and has data in it). */
+ function isTruthy(field) {
+ let wrapped = wrapValue(field);
+ if (!wrapped)
+ return false;
+ switch (wrapped.type) {
+ case "number":
+ return wrapped.value != 0;
+ case "string":
+ return wrapped.value.length > 0;
+ case "boolean":
+ return wrapped.value;
+ case "link":
+ return !!wrapped.value.path;
+ case "date":
+ return wrapped.value.toMillis() != 0;
+ case "duration":
+ return wrapped.value.as("seconds") != 0;
+ case "object":
+ return Object.keys(wrapped.value).length > 0;
+ case "array":
+ return wrapped.value.length > 0;
+ case "null":
+ return false;
+ case "html":
+ case "widget":
+ case "function":
+ return true;
+ }
+ }
+ Values.isTruthy = isTruthy;
+ /** Deep copy a field. */
+ function deepCopy(field) {
+ if (field === null || field === undefined)
+ return field;
+ if (Values.isArray(field)) {
+ return [].concat(field.map(v => deepCopy(v)));
+ }
+ else if (Values.isObject(field)) {
+ let result = {};
+ for (let [key, value] of Object.entries(field))
+ result[key] = deepCopy(value);
+ return result;
+ }
+ else {
+ return field;
+ }
+ }
+ Values.deepCopy = deepCopy;
+ function isString(val) {
+ return typeof val == "string";
+ }
+ Values.isString = isString;
+ function isNumber(val) {
+ return typeof val == "number";
+ }
+ Values.isNumber = isNumber;
+ function isDate(val) {
+ return val instanceof DateTime;
+ }
+ Values.isDate = isDate;
+ function isDuration(val) {
+ return val instanceof Duration;
+ }
+ Values.isDuration = isDuration;
+ function isNull(val) {
+ return val === null || val === undefined;
+ }
+ Values.isNull = isNull;
+ function isArray(val) {
+ return Array.isArray(val);
+ }
+ Values.isArray = isArray;
+ function isBoolean(val) {
+ return typeof val === "boolean";
+ }
+ Values.isBoolean = isBoolean;
+ function isLink(val) {
+ return val instanceof Link;
+ }
+ Values.isLink = isLink;
+ function isWidget(val) {
+ return val instanceof Widget;
+ }
+ Values.isWidget = isWidget;
+ function isHtml(val) {
+ if (typeof HTMLElement !== "undefined") {
+ return val instanceof HTMLElement;
+ }
+ else {
+ return false;
+ }
+ }
+ Values.isHtml = isHtml;
+ /** Checks if the given value is an object (and not any other dataview-recognized object-like type). */
+ function isObject(val) {
+ return (typeof val == "object" &&
+ !isHtml(val) &&
+ !isWidget(val) &&
+ !isArray(val) &&
+ !isDuration(val) &&
+ !isDate(val) &&
+ !isLink(val) &&
+ val !== undefined &&
+ !isNull(val));
+ }
+ Values.isObject = isObject;
+ function isFunction(val) {
+ return typeof val == "function";
+ }
+ Values.isFunction = isFunction;
+})(Values || (Values = {}));
+///////////////
+// Groupings //
+///////////////
+var Groupings;
+(function (Groupings) {
+ /** Determines if the given group entry is a standalone value, or a grouping of sub-entries. */
+ function isElementGroup(entry) {
+ return Values.isObject(entry) && Object.keys(entry).length == 2 && "key" in entry && "rows" in entry;
+ }
+ Groupings.isElementGroup = isElementGroup;
+ /** Determines if the given array is a grouping array. */
+ function isGrouping(entry) {
+ for (let element of entry)
+ if (!isElementGroup(element))
+ return false;
+ return true;
+ }
+ Groupings.isGrouping = isGrouping;
+ /** Count the total number of elements in a recursive grouping. */
+ function count(elements) {
+ if (isGrouping(elements)) {
+ let result = 0;
+ for (let subgroup of elements)
+ result += count(subgroup.rows);
+ return result;
+ }
+ else {
+ return elements.length;
+ }
+ }
+ Groupings.count = count;
+})(Groupings || (Groupings = {}));
+//////////
+// LINK //
+//////////
+/** The Obsidian 'link', used for uniquely describing a file, header, or block. */
+class Link {
+ /** The file path this link points to. */
+ path;
+ /** The display name associated with the link. */
+ display;
+ /** The block ID or header this link points to within a file, if relevant. */
+ subpath;
+ /** Is this link an embedded link (!)? */
+ embed;
+ /** The type of this link, which determines what 'subpath' refers to, if anything. */
+ type;
+ /** Create a link to a specific file. */
+ static file(path, embed = false, display) {
+ return new Link({
+ path,
+ embed,
+ display,
+ subpath: undefined,
+ type: "file",
+ });
+ }
+ static infer(linkpath, embed = false, display) {
+ if (linkpath.includes("#^")) {
+ let split = linkpath.split("#^");
+ return Link.block(split[0], split[1], embed, display);
+ }
+ else if (linkpath.includes("#")) {
+ let split = linkpath.split("#");
+ return Link.header(split[0], split[1], embed, display);
+ }
+ else
+ return Link.file(linkpath, embed, display);
+ }
+ /** Create a link to a specific file and header in that file. */
+ static header(path, header, embed, display) {
+ // Headers need to be normalized to alpha-numeric & with extra spacing removed.
+ return new Link({
+ path,
+ embed,
+ display,
+ subpath: normalizeHeaderForLink(header),
+ type: "header",
+ });
+ }
+ /** Create a link to a specific file and block in that file. */
+ static block(path, blockId, embed, display) {
+ return new Link({
+ path,
+ embed,
+ display,
+ subpath: blockId,
+ type: "block",
+ });
+ }
+ static fromObject(object) {
+ return new Link(object);
+ }
+ constructor(fields) {
+ Object.assign(this, fields);
+ }
+ /** Checks for link equality (i.e., that the links are pointing to the same exact location). */
+ equals(other) {
+ if (other == undefined || other == null)
+ return false;
+ return this.path == other.path && this.type == other.type && this.subpath == other.subpath;
+ }
+ /** Convert this link to it's markdown representation. */
+ toString() {
+ return this.markdown();
+ }
+ /** Convert this link to a raw object which is serialization-friendly. */
+ toObject() {
+ return { path: this.path, type: this.type, subpath: this.subpath, display: this.display, embed: this.embed };
+ }
+ /** Update this link with a new path. */
+ //@ts-ignore; error appeared after updating Obsidian to 0.15.4; it also updated other packages but didn't say which
+ withPath(path) {
+ return new Link(Object.assign({}, this, { path }));
+ }
+ /** Return a new link which points to the same location but with a new display value. */
+ withDisplay(display) {
+ return new Link(Object.assign({}, this, { display }));
+ }
+ /** Convert a file link into a link to a specific header. */
+ withHeader(header) {
+ return Link.header(this.path, header, this.embed, this.display);
+ }
+ /** Convert any link into a link to its file. */
+ toFile() {
+ return Link.file(this.path, this.embed, this.display);
+ }
+ /** Convert this link into an embedded link. */
+ toEmbed() {
+ if (this.embed) {
+ return this;
+ }
+ else {
+ let link = new Link(this);
+ link.embed = true;
+ return link;
+ }
+ }
+ /** Convert this link into a non-embedded link. */
+ fromEmbed() {
+ if (!this.embed) {
+ return this;
+ }
+ else {
+ let link = new Link(this);
+ link.embed = false;
+ return link;
+ }
+ }
+ /** Convert this link to markdown so it can be rendered. */
+ markdown() {
+ let result = (this.embed ? "!" : "") + "[[" + this.obsidianLink();
+ if (this.display) {
+ result += "|" + this.display;
+ }
+ else {
+ result += "|" + getFileTitle(this.path);
+ if (this.type == "header" || this.type == "block")
+ result += " > " + this.subpath;
+ }
+ result += "]]";
+ return result;
+ }
+ /** Convert the inner part of the link to something that Obsidian can open / understand. */
+ obsidianLink() {
+ const escaped = this.path.replaceAll("|", "\\|");
+ if (this.type == "header")
+ return escaped + "#" + this.subpath?.replaceAll("|", "\\|");
+ if (this.type == "block")
+ return escaped + "#^" + this.subpath?.replaceAll("|", "\\|");
+ else
+ return escaped;
+ }
+ /** The stripped name of the file this link points to. */
+ fileName() {
+ return getFileTitle(this.path).replace(".md", "");
+ }
+}
+/////////////////
+// WIDGET BASE //
+/////////////////
+/**
+ * A trivial base class which just defines the '$widget' identifier type. Subtypes of
+ * widget are responsible for adding whatever metadata is relevant. If you want your widget
+ * to have rendering functionality (which you probably do), you should extend `RenderWidget`.
+ */
+class Widget {
+ $widget;
+ constructor($widget) {
+ this.$widget = $widget;
+ }
+}
+/** A trivial widget which renders a (key, value) pair, and allows accessing the key and value. */
+class ListPairWidget extends Widget {
+ key;
+ value;
+ constructor(key, value) {
+ super("dataview:list-pair");
+ this.key = key;
+ this.value = value;
+ }
+ markdown() {
+ return `${Values.toString(this.key)}: ${Values.toString(this.value)}`;
+ }
+}
+/** A simple widget which renders an external link. */
+class ExternalLinkWidget extends Widget {
+ url;
+ display;
+ constructor(url, display) {
+ super("dataview:external-link");
+ this.url = url;
+ this.display = display;
+ }
+ markdown() {
+ return `[${this.display ?? this.url}](${this.url})`;
+ }
+}
+var Widgets;
+(function (Widgets) {
+ /** Create a list pair widget matching the given key and value. */
+ function listPair(key, value) {
+ return new ListPairWidget(key, value);
+ }
+ Widgets.listPair = listPair;
+ /** Create an external link widget which renders an external Obsidian link. */
+ function externalLink(url, display) {
+ return new ExternalLinkWidget(url, display);
+ }
+ Widgets.externalLink = externalLink;
+ /** Checks if the given widget is a list pair widget. */
+ function isListPair(widget) {
+ return widget.$widget === "dataview:list-pair";
+ }
+ Widgets.isListPair = isListPair;
+ function isExternalLink(widget) {
+ return widget.$widget === "dataview:external-link";
+ }
+ Widgets.isExternalLink = isExternalLink;
+ /** Determines if the given widget is any kind of built-in widget with special rendering handling. */
+ function isBuiltin(widget) {
+ return isListPair(widget) || isExternalLink(widget);
+ }
+ Widgets.isBuiltin = isBuiltin;
+})(Widgets || (Widgets = {}));
+
+/** Implementation of DataArray, minus the dynamic variable access, which is implemented via proxy. */
+class DataArrayImpl {
+ values;
+ settings;
+ defaultComparator;
+ static ARRAY_FUNCTIONS = new Set([
+ "where",
+ "filter",
+ "map",
+ "flatMap",
+ "mutate",
+ "slice",
+ "concat",
+ "indexOf",
+ "limit",
+ "find",
+ "findIndex",
+ "includes",
+ "join",
+ "sort",
+ "sortInPlace",
+ "groupBy",
+ "groupIn",
+ "distinct",
+ "every",
+ "some",
+ "none",
+ "first",
+ "last",
+ "to",
+ "into",
+ "lwrap",
+ "expand",
+ "forEach",
+ "length",
+ "values",
+ "array",
+ "defaultComparator",
+ "toString",
+ "settings",
+ "sum",
+ "avg",
+ "min",
+ "max",
+ ]);
+ static ARRAY_PROXY = {
+ get: function (target, prop, receiver) {
+ if (typeof prop === "symbol")
+ return target[prop];
+ else if (typeof prop === "number")
+ return target.values[prop];
+ else if (prop === "constructor")
+ return target.values.constructor;
+ else if (!isNaN(parseInt(prop)))
+ return target.values[parseInt(prop)];
+ else if (DataArrayImpl.ARRAY_FUNCTIONS.has(prop.toString()))
+ return target[prop.toString()];
+ return target.to(prop);
+ },
+ };
+ static wrap(arr, settings, defaultComparator = Values.compareValue) {
+ return new Proxy(new DataArrayImpl(arr, settings, defaultComparator), DataArrayImpl.ARRAY_PROXY);
+ }
+ length;
+ constructor(values, settings, defaultComparator = Values.compareValue) {
+ this.values = values;
+ this.settings = settings;
+ this.defaultComparator = defaultComparator;
+ this.length = values.length;
+ }
+ lwrap(values) {
+ return DataArrayImpl.wrap(values, this.settings, this.defaultComparator);
+ }
+ where(predicate) {
+ return this.lwrap(this.values.filter(predicate));
+ }
+ filter(predicate) {
+ return this.where(predicate);
+ }
+ map(f) {
+ return this.lwrap(this.values.map(f));
+ }
+ flatMap(f) {
+ let result = [];
+ for (let index = 0; index < this.length; index++) {
+ let value = f(this.values[index], index, this.values);
+ if (!value || value.length == 0)
+ continue;
+ for (let r of value)
+ result.push(r);
+ }
+ return this.lwrap(result);
+ }
+ mutate(f) {
+ for (let index = 0; index < this.values.length; index++) {
+ f(this.values[index], index, this.values);
+ }
+ return this;
+ }
+ limit(count) {
+ return this.lwrap(this.values.slice(0, count));
+ }
+ slice(start, end) {
+ return this.lwrap(this.values.slice(start, end));
+ }
+ concat(other) {
+ return this.lwrap(this.values.concat(other.values));
+ }
+ /** Return the first index of the given (optionally starting the search) */
+ indexOf(element, fromIndex) {
+ return this.findIndex(e => this.defaultComparator(e, element) == 0, fromIndex);
+ }
+ /** Return the first element that satisfies the given predicate. */
+ find(pred) {
+ let index = this.findIndex(pred);
+ if (index == -1)
+ return undefined;
+ else
+ return this.values[index];
+ }
+ findIndex(pred, fromIndex) {
+ for (let index = fromIndex ?? 0; index < this.length; index++) {
+ if (pred(this.values[index], index, this.values))
+ return index;
+ }
+ return -1;
+ }
+ includes(element) {
+ return this.indexOf(element, 0) != -1;
+ }
+ join(sep) {
+ return this.map(s => Values.toString(s, this.settings))
+ .array()
+ .join(sep ?? ", ");
+ }
+ sort(key, direction, comparator) {
+ if (this.values.length == 0)
+ return this;
+ let realComparator = comparator ?? this.defaultComparator;
+ let realKey = key ?? ((l) => l);
+ // Associate each entry with it's index for the key function, and then do a normal sort.
+ let copy = [].concat(this.array()).map((elem, index) => {
+ return { index: index, value: elem };
+ });
+ copy.sort((a, b) => {
+ let aKey = realKey(a.value, a.index, this.values);
+ let bKey = realKey(b.value, b.index, this.values);
+ return direction === "desc" ? -realComparator(aKey, bKey) : realComparator(aKey, bKey);
+ });
+ return this.lwrap(copy.map(e => e.value));
+ }
+ sortInPlace(key, direction, comparator) {
+ if (this.values.length == 0)
+ return this;
+ let realComparator = comparator ?? this.defaultComparator;
+ let realKey = key ?? ((l) => l);
+ this.values.sort((a, b) => {
+ let aKey = realKey(a);
+ let bKey = realKey(b);
+ return direction == "desc" ? -realComparator(aKey, bKey) : realComparator(aKey, bKey);
+ });
+ return this;
+ }
+ groupBy(key, comparator) {
+ if (this.values.length == 0)
+ return this.lwrap([]);
+ // JavaScript sucks and we can't make hash maps over arbitrary types (only strings/ints), so
+ // we do a poor man algorithm where we SORT, followed by grouping.
+ let intermediate = this.sort(key, "asc", comparator);
+ comparator = comparator ?? this.defaultComparator;
+ let result = [];
+ let currentRow = [intermediate[0]];
+ let current = key(intermediate[0], 0, intermediate.values);
+ for (let index = 1; index < intermediate.length; index++) {
+ let newKey = key(intermediate[index], index, intermediate.values);
+ if (comparator(current, newKey) != 0) {
+ result.push({ key: current, rows: this.lwrap(currentRow) });
+ current = newKey;
+ currentRow = [intermediate[index]];
+ }
+ else {
+ currentRow.push(intermediate[index]);
+ }
+ }
+ result.push({ key: current, rows: this.lwrap(currentRow) });
+ return this.lwrap(result);
+ }
+ groupIn(key, comparator) {
+ if (Groupings.isGrouping(this.values)) {
+ return this.map(v => {
+ return {
+ key: v.key,
+ rows: DataArray.wrap(v.rows, this.settings).groupIn(key, comparator),
+ };
+ });
+ }
+ else {
+ return this.groupBy(key, comparator);
+ }
+ }
+ distinct(key, comparator) {
+ if (this.values.length == 0)
+ return this;
+ let realKey = key ?? (x => x);
+ // For similar reasons to groupBy, do a sort and take the first element of each block.
+ let intermediate = this.map((x, index) => {
+ return { key: realKey(x, index, this.values), value: x };
+ }).sort(x => x.key, "asc", comparator);
+ comparator = comparator ?? this.defaultComparator;
+ let result = [intermediate[0].value];
+ for (let index = 1; index < intermediate.length; index++) {
+ if (comparator(intermediate[index - 1].key, intermediate[index].key) != 0) {
+ result.push(intermediate[index].value);
+ }
+ }
+ return this.lwrap(result);
+ }
+ every(f) {
+ return this.values.every(f);
+ }
+ some(f) {
+ return this.values.some(f);
+ }
+ none(f) {
+ return this.values.every((v, i, a) => !f(v, i, a));
+ }
+ first() {
+ return this.values.length > 0 ? this.values[0] : undefined;
+ }
+ last() {
+ return this.values.length > 0 ? this.values[this.values.length - 1] : undefined;
+ }
+ to(key) {
+ let result = [];
+ for (let child of this.values) {
+ let value = child[key];
+ if (value === undefined || value === null)
+ continue;
+ if (Array.isArray(value) || DataArray.isDataArray(value))
+ value.forEach(v => result.push(v));
+ else
+ result.push(value);
+ }
+ return this.lwrap(result);
+ }
+ into(key) {
+ let result = [];
+ for (let child of this.values) {
+ let value = child[key];
+ if (value === undefined || value === null)
+ continue;
+ result.push(value);
+ }
+ return this.lwrap(result);
+ }
+ expand(key) {
+ let result = [];
+ let queue = [].concat(this.values);
+ while (queue.length > 0) {
+ let next = queue.pop();
+ let value = next[key];
+ if (value === undefined || value === null)
+ continue;
+ if (Array.isArray(value))
+ value.forEach(v => queue.push(v));
+ else if (value instanceof DataArrayImpl)
+ value.forEach(v => queue.push(v));
+ else
+ queue.push(value);
+ result.push(next);
+ }
+ return this.lwrap(result);
+ }
+ forEach(f) {
+ for (let index = 0; index < this.values.length; index++) {
+ f(this.values[index], index, this.values);
+ }
+ }
+ sum() {
+ return this.values.reduce((a, b) => a + b, 0);
+ }
+ avg() {
+ return this.sum() / this.values.length;
+ }
+ min() {
+ return Math.min(...this.values);
+ }
+ max() {
+ return Math.max(...this.values);
+ }
+ array() {
+ return [].concat(this.values);
+ }
+ [Symbol.iterator]() {
+ return this.values[Symbol.iterator]();
+ }
+ toString() {
+ return "[" + this.values.join(", ") + "]";
+ }
+}
+/** Provides utility functions for generating data arrays. */
+var DataArray;
+(function (DataArray) {
+ /** Create a new Dataview data array. */
+ function wrap(raw, settings) {
+ if (isDataArray(raw))
+ return raw;
+ return DataArrayImpl.wrap(raw, settings);
+ }
+ DataArray.wrap = wrap;
+ /** Create a new DataArray from an iterable object. */
+ function from(raw, settings) {
+ if (isDataArray(raw))
+ return raw;
+ let data = [];
+ for (let elem of raw)
+ data.push(elem);
+ return DataArrayImpl.wrap(data, settings);
+ }
+ DataArray.from = from;
+ /** Return true if the given object is a data array. */
+ function isDataArray(obj) {
+ return obj instanceof DataArrayImpl;
+ }
+ DataArray.isDataArray = isDataArray;
+})(DataArray || (DataArray = {}));
+// A scary looking polyfill, sure, but it fixes up data array/array interop for us.
+const oldArrayIsArray = Array.isArray;
+Array.isArray = (arg) => {
+ return oldArrayIsArray(arg) || DataArray.isDataArray(arg);
+};
+
+/** Test-environment-friendly function which fetches the current system locale. */
+function currentLocale() {
+ if (typeof window === "undefined")
+ return "en-US";
+ return window.navigator.language;
+}
+
+/** Render simple fields compactly, removing wrapping content like paragraph and span. */
+async function renderCompactMarkdown(app, markdown, container, sourcePath, component, isInlineFieldLivePreview = false) {
+ // check if the call is from the CM6 view plugin defined in src/ui/views/inline-field-live-preview.ts
+ if (isInlineFieldLivePreview) {
+ await renderCompactMarkdownForInlineFieldLivePreview(app, markdown, container, sourcePath, component);
+ }
+ else {
+ let subcontainer = container.createSpan();
+ await obsidian.MarkdownRenderer.render(app, markdown, subcontainer, sourcePath, component);
+ let paragraph = subcontainer.querySelector(":scope > p");
+ if (subcontainer.children.length == 1 && paragraph) {
+ while (paragraph.firstChild) {
+ subcontainer.appendChild(paragraph.firstChild);
+ }
+ subcontainer.removeChild(paragraph);
+ }
+ }
+}
+async function renderCompactMarkdownForInlineFieldLivePreview(app, markdown, container, sourcePath, component) {
+ const tmpContainer = createSpan();
+ await obsidian.MarkdownRenderer.render(app, markdown, tmpContainer, sourcePath, component);
+ let paragraph = tmpContainer.querySelector(":scope > p");
+ if (tmpContainer.childNodes.length == 1 && paragraph) {
+ container.appendChild(paragraph.childNodes.item(paragraph.childNodes.length - 1));
+ }
+ else {
+ container.replaceChildren(...tmpContainer.childNodes);
+ }
+ tmpContainer.remove();
+}
+/** Render a pre block with an error in it; returns the element to allow for dynamic updating. */
+function renderErrorPre(container, error) {
+ let pre = container.createEl("pre", { cls: ["dataview", "dataview-error"] });
+ pre.appendText(error);
+ return pre;
+}
+/** Render a static codeblock. */
+function renderCodeBlock(container, source, language) {
+ let code = container.createEl("code", { cls: ["dataview"] });
+ if (language)
+ code.classList.add("language-" + language);
+ code.appendText(source);
+ return code;
+}
+/** Prettily render a value into a container with the given settings. */
+async function renderValue(app, field, container, originFile, component, settings, expandList = false, context = "root", depth = 0, isInlineFieldLivePreview = false) {
+ // Prevent infinite recursion.
+ if (depth > settings.maxRecursiveRenderDepth) {
+ container.appendText("...");
+ return;
+ }
+ if (Values.isNull(field)) {
+ await renderCompactMarkdown(app, settings.renderNullAs, container, originFile, component, isInlineFieldLivePreview);
+ }
+ else if (Values.isDate(field)) {
+ container.appendText(renderMinimalDate(field, settings, currentLocale()));
+ }
+ else if (Values.isDuration(field)) {
+ container.appendText(renderMinimalDuration(field));
+ }
+ else if (Values.isString(field) || Values.isBoolean(field) || Values.isNumber(field)) {
+ await renderCompactMarkdown(app, "" + field, container, originFile, component, isInlineFieldLivePreview);
+ }
+ else if (Values.isLink(field)) {
+ await renderCompactMarkdown(app, field.markdown(), container, originFile, component, isInlineFieldLivePreview);
+ }
+ else if (Values.isHtml(field)) {
+ container.appendChild(field);
+ }
+ else if (Values.isWidget(field)) {
+ if (Widgets.isListPair(field)) {
+ await renderValue(app, field.key, container, originFile, component, settings, expandList, context, depth, isInlineFieldLivePreview);
+ container.appendText(": ");
+ await renderValue(app, field.value, container, originFile, component, settings, expandList, context, depth, isInlineFieldLivePreview);
+ }
+ else if (Widgets.isExternalLink(field)) {
+ let elem = document.createElement("a");
+ elem.textContent = field.display ?? field.url;
+ elem.rel = "noopener";
+ elem.target = "_blank";
+ elem.classList.add("external-link");
+ elem.href = field.url;
+ container.appendChild(elem);
+ }
+ else {
+ container.appendText(``);
+ }
+ }
+ else if (Values.isFunction(field)) {
+ container.appendText("");
+ }
+ else if (Values.isArray(field) || DataArray.isDataArray(field)) {
+ if (expandList) {
+ let list = container.createEl("ul", {
+ cls: [
+ "dataview",
+ "dataview-ul",
+ context == "list" ? "dataview-result-list-ul" : "dataview-result-list-root-ul",
+ ],
+ });
+ for (let child of field) {
+ let li = list.createEl("li", { cls: "dataview-result-list-li" });
+ await renderValue(app, child, li, originFile, component, settings, expandList, "list", depth + 1, isInlineFieldLivePreview);
+ }
+ }
+ else {
+ if (field.length == 0) {
+ container.appendText("");
+ return;
+ }
+ let span = container.createEl("span", { cls: ["dataview", "dataview-result-list-span"] });
+ let first = true;
+ for (let val of field) {
+ if (first)
+ first = false;
+ else
+ span.appendText(", ");
+ await renderValue(app, val, span, originFile, component, settings, expandList, "list", depth + 1, isInlineFieldLivePreview);
+ }
+ }
+ }
+ else if (Values.isObject(field)) {
+ // Don't render classes in case they have recursive references; spoopy.
+ if (field?.constructor?.name && field?.constructor?.name != "Object") {
+ container.appendText(`<${field.constructor.name}>`);
+ return;
+ }
+ if (expandList) {
+ let list = container.createEl("ul", { cls: ["dataview", "dataview-ul", "dataview-result-object-ul"] });
+ for (let [key, value] of Object.entries(field)) {
+ let li = list.createEl("li", { cls: ["dataview", "dataview-li", "dataview-result-object-li"] });
+ li.appendText(key + ": ");
+ await renderValue(app, value, li, originFile, component, settings, expandList, "list", depth + 1, isInlineFieldLivePreview);
+ }
+ }
+ else {
+ if (Object.keys(field).length == 0) {
+ container.appendText("");
+ return;
+ }
+ let span = container.createEl("span", { cls: ["dataview", "dataview-result-object-span"] });
+ let first = true;
+ for (let [key, value] of Object.entries(field)) {
+ if (first)
+ first = false;
+ else
+ span.appendText(", ");
+ span.appendText(key + ": ");
+ await renderValue(app, value, span, originFile, component, settings, expandList, "list", depth + 1, isInlineFieldLivePreview);
+ }
+ }
+ }
+ else {
+ container.appendText("Unrecognized: " + JSON.stringify(field));
+ }
+}
+
+var papaparse_min = {exports: {}};
+
+/* @license
+Papa Parse
+v5.4.1
+https://github.com/mholt/PapaParse
+License: MIT
+*/
+papaparse_min.exports;
+
+(function (module, exports) {
+ !function(e,t){module.exports=t();}(commonjsGlobal,function s(){var f="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==f?f:{};var n=!f.document&&!!f.postMessage,o=f.IS_PAPA_WORKER||!1,a={},u=0,b={parse:function(e,t){var r=(t=t||{}).dynamicTyping||!1;J(r)&&(t.dynamicTypingFunction=r,r={});if(t.dynamicTyping=r,t.transform=!!J(t.transform)&&t.transform,t.worker&&b.WORKERS_SUPPORTED){var i=function(){if(!b.WORKERS_SUPPORTED)return !1;var e=(r=f.URL||f.webkitURL||null,i=s.toString(),b.BLOB_URL||(b.BLOB_URL=r.createObjectURL(new Blob(["var global = (function() { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } return {}; })(); global.IS_PAPA_WORKER=true; ","(",i,")();"],{type:"text/javascript"})))),t=new f.Worker(e);var r,i;return t.onmessage=_,t.id=u++,a[t.id]=t}();return i.userStep=t.step,i.userChunk=t.chunk,i.userComplete=t.complete,i.userError=t.error,t.step=J(t.step),t.chunk=J(t.chunk),t.complete=J(t.complete),t.error=J(t.error),delete t.worker,void i.postMessage({input:e,config:t,workerId:i.id})}var n=null;b.NODE_STREAM_INPUT,"string"==typeof e?(e=function(e){if(65279===e.charCodeAt(0))return e.slice(1);return e}(e),n=t.download?new l(t):new p(t)):!0===e.readable&&J(e.read)&&J(e.on)?n=new g(t):(f.File&&e instanceof File||e instanceof Object)&&(n=new c(t));return n.stream(e)},unparse:function(e,t){var n=!1,_=!0,m=",",y="\r\n",s='"',a=s+s,r=!1,i=null,o=!1;!function(){if("object"!=typeof t)return;"string"!=typeof t.delimiter||b.BAD_DELIMITERS.filter(function(e){return -1!==t.delimiter.indexOf(e)}).length||(m=t.delimiter);("boolean"==typeof t.quotes||"function"==typeof t.quotes||Array.isArray(t.quotes))&&(n=t.quotes);"boolean"!=typeof t.skipEmptyLines&&"string"!=typeof t.skipEmptyLines||(r=t.skipEmptyLines);"string"==typeof t.newline&&(y=t.newline);"string"==typeof t.quoteChar&&(s=t.quoteChar);"boolean"==typeof t.header&&(_=t.header);if(Array.isArray(t.columns)){if(0===t.columns.length)throw new Error("Option columns is empty");i=t.columns;}void 0!==t.escapeChar&&(a=t.escapeChar+s);("boolean"==typeof t.escapeFormulae||t.escapeFormulae instanceof RegExp)&&(o=t.escapeFormulae instanceof RegExp?t.escapeFormulae:/^[=+\-@\t\r].*$/);}();var u=new RegExp(Q(s),"g");"string"==typeof e&&(e=JSON.parse(e));if(Array.isArray(e)){if(!e.length||Array.isArray(e[0]))return h(null,e,r);if("object"==typeof e[0])return h(i||Object.keys(e[0]),e,r)}else if("object"==typeof e)return "string"==typeof e.data&&(e.data=JSON.parse(e.data)),Array.isArray(e.data)&&(e.fields||(e.fields=e.meta&&e.meta.fields||i),e.fields||(e.fields=Array.isArray(e.data[0])?e.fields:"object"==typeof e.data[0]?Object.keys(e.data[0]):[]),Array.isArray(e.data[0])||"object"==typeof e.data[0]||(e.data=[e.data])),h(e.fields||[],e.data||[],r);throw new Error("Unable to serialize unrecognized input");function h(e,t,r){var i="";"string"==typeof e&&(e=JSON.parse(e)),"string"==typeof t&&(t=JSON.parse(t));var n=Array.isArray(e)&&0=this._config.preview;if(o)f.postMessage({results:n,workerId:b.WORKER_ID,finished:a});else if(J(this._config.chunk)&&!t){if(this._config.chunk(n,this._handle),this._handle.paused()||this._handle.aborted())return void(this._halted=!0);n=void 0,this._completeResults=void 0;}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(n.data),this._completeResults.errors=this._completeResults.errors.concat(n.errors),this._completeResults.meta=n.meta),this._completed||!a||!J(this._config.complete)||n&&n.meta.aborted||(this._config.complete(this._completeResults,this._input),this._completed=!0),a||n&&n.meta.paused||this._nextChunk(),n}this._halted=!0;},this._sendError=function(e){J(this._config.error)?this._config.error(e):o&&this._config.error&&f.postMessage({workerId:b.WORKER_ID,error:e,finished:!1});};}function l(e){var i;(e=e||{}).chunkSize||(e.chunkSize=b.RemoteChunkSize),h.call(this,e),this._nextChunk=n?function(){this._readChunk(),this._chunkLoaded();}:function(){this._readChunk();},this.stream=function(e){this._input=e,this._nextChunk();},this._readChunk=function(){if(this._finished)this._chunkLoaded();else {if(i=new XMLHttpRequest,this._config.withCredentials&&(i.withCredentials=this._config.withCredentials),n||(i.onload=v(this._chunkLoaded,this),i.onerror=v(this._chunkError,this)),i.open(this._config.downloadRequestBody?"POST":"GET",this._input,!n),this._config.downloadRequestHeaders){var e=this._config.downloadRequestHeaders;for(var t in e)i.setRequestHeader(t,e[t]);}if(this._config.chunkSize){var r=this._start+this._config.chunkSize-1;i.setRequestHeader("Range","bytes="+this._start+"-"+r);}try{i.send(this._config.downloadRequestBody);}catch(e){this._chunkError(e.message);}n&&0===i.status&&this._chunkError();}},this._chunkLoaded=function(){4===i.readyState&&(i.status<200||400<=i.status?this._chunkError():(this._start+=this._config.chunkSize?this._config.chunkSize:i.responseText.length,this._finished=!this._config.chunkSize||this._start>=function(e){var t=e.getResponseHeader("Content-Range");if(null===t)return -1;return parseInt(t.substring(t.lastIndexOf("/")+1))}(i),this.parseChunk(i.responseText)));},this._chunkError=function(e){var t=i.statusText||e;this._sendError(new Error(t));};}function c(e){var i,n;(e=e||{}).chunkSize||(e.chunkSize=b.LocalChunkSize),h.call(this,e);var s="undefined"!=typeof FileReader;this.stream=function(e){this._input=e,n=e.slice||e.webkitSlice||e.mozSlice,s?((i=new FileReader).onload=v(this._chunkLoaded,this),i.onerror=v(this._chunkError,this)):i=new FileReaderSync,this._nextChunk();},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(e.target.result);},this._chunkError=function(){this._sendError(i.error);};}function p(e){var r;h.call(this,e=e||{}),this.stream=function(e){return r=e,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var e,t=this._config.chunkSize;return t?(e=r.substring(0,t),r=r.substring(t)):(e=r,r=""),this._finished=!r,this.parseChunk(e)}};}function g(e){h.call(this,e=e||{});var t=[],r=!0,i=!1;this.pause=function(){h.prototype.pause.apply(this,arguments),this._input.pause();},this.resume=function(){h.prototype.resume.apply(this,arguments),this._input.resume();},this.stream=function(e){this._input=e,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError);},this._checkIsFinished=function(){i&&1===t.length&&(this._finished=!0);},this._nextChunk=function(){this._checkIsFinished(),t.length?this.parseChunk(t.shift()):r=!0;},this._streamData=v(function(e){try{t.push("string"==typeof e?e:e.toString(this._config.encoding)),r&&(r=!1,this._checkIsFinished(),this.parseChunk(t.shift()));}catch(e){this._streamError(e);}},this),this._streamError=v(function(e){this._streamCleanUp(),this._sendError(e);},this),this._streamEnd=v(function(){this._streamCleanUp(),i=!0,this._streamData("");},this),this._streamCleanUp=v(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError);},this);}function r(m){var a,o,u,i=Math.pow(2,53),n=-i,s=/^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/,h=/^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/,t=this,r=0,f=0,d=!1,e=!1,l=[],c={data:[],errors:[],meta:{}};if(J(m.step)){var p=m.step;m.step=function(e){if(c=e,_())g();else {if(g(),0===c.data.length)return;r+=e.data.length,m.preview&&r>m.preview?o.abort():(c.data=c.data[0],p(c,t));}};}function y(e){return "greedy"===m.skipEmptyLines?""===e.join("").trim():1===e.length&&0===e[0].length}function g(){return c&&u&&(k("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+b.DefaultDelimiter+"'"),u=!1),m.skipEmptyLines&&(c.data=c.data.filter(function(e){return !y(e)})),_()&&function(){if(!c)return;function e(e,t){J(m.transformHeader)&&(e=m.transformHeader(e,t)),l.push(e);}if(Array.isArray(c.data[0])){for(var t=0;_()&&t=l.length?"__parsed_extra":l[r]),m.transform&&(s=m.transform(s,n)),s=v(n,s),"__parsed_extra"===n?(i[n]=i[n]||[],i[n].push(s)):i[n]=s;}return m.header&&(r>l.length?k("FieldMismatch","TooManyFields","Too many fields: expected "+l.length+" fields but parsed "+r,f+t):r=i.length/2?"\r\n":"\r"}(e,i)),u=!1,m.delimiter)J(m.delimiter)&&(m.delimiter=m.delimiter(e),c.meta.delimiter=m.delimiter);else {var n=function(e,t,r,i,n){var s,a,o,u;n=n||[",","\t","|",";",b.RECORD_SEP,b.UNIT_SEP];for(var h=0;h=N)return L(!0)}else for(S=W,W++;;){if(-1===(S=i.indexOf(z,S+1)))return r||h.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:u.length,index:W}),T();if(S===n-1)return T(i.substring(W,S).replace(C,z));if(z!==K||i[S+1]!==K){if(z===K||0===S||i[S-1]!==K){-1!==w&&w=N)return L(!0);break}h.push({type:"Quotes",code:"InvalidQuotes",message:"Trailing quote on quoted field is malformed",row:u.length,index:W}),S++;}}else S++;}return T();function I(e){u.push(e),d=W;}function A(e){var t=0;if(-1!==e){var r=i.substring(S+1,e);r&&""===r.trim()&&(t=r.length);}return t}function T(e){return r||(void 0===e&&(e=i.substring(W)),f.push(e),W=n,I(f),o&&F()),L()}function D(e){W=e,I(f),f=[],R=i.indexOf(P,W);}function L(e){return {data:u,errors:h,meta:{delimiter:M,linebreak:P,aborted:H,truncated:!!e,cursor:d+(t||0)}}}function F(){q(L()),u=[],h=[];}},this.abort=function(){H=!0;},this.getCharIndex=function(){return W};}function _(e){var t=e.data,r=a[t.workerId],i=!1;if(t.error)r.userError(t.error,t.file);else if(t.results&&t.results.data){var n={abort:function(){i=!0,m(t.workerId,{data:[],errors:[],meta:{aborted:!0}});},pause:y,resume:y};if(J(r.userStep)){for(var s=0;s" || op == ">=" || op == "!=" || op == "=";
+ }
+ Fields.isCompareOp = isCompareOp;
+ Fields.NULL = Fields.literal(null);
+})(Fields || (Fields = {}));
+
+/** AST implementation for queries over data sources. */
+/** Utility functions for creating and manipulating sources. */
+var Sources;
+(function (Sources) {
+ /** Create a source which searches from a tag. */
+ function tag(tag) {
+ return { type: "tag", tag };
+ }
+ Sources.tag = tag;
+ /** Create a source which fetches from a CSV file. */
+ function csv(path) {
+ return { type: "csv", path };
+ }
+ Sources.csv = csv;
+ /** Create a source which searches for files under a folder prefix. */
+ function folder(prefix) {
+ return { type: "folder", folder: prefix };
+ }
+ Sources.folder = folder;
+ /** Create a source which searches for files which link to/from a given file. */
+ function link(file, incoming) {
+ return { type: "link", file, direction: incoming ? "incoming" : "outgoing" };
+ }
+ Sources.link = link;
+ /** Create a source which joins two sources by a logical operator (and/or). */
+ function binaryOp(left, op, right) {
+ return { type: "binaryop", left, op, right };
+ }
+ Sources.binaryOp = binaryOp;
+ /** Create a source which takes the intersection of two sources. */
+ function and(left, right) {
+ return { type: "binaryop", left, op: "&", right };
+ }
+ Sources.and = and;
+ /** Create a source which takes the union of two sources. */
+ function or(left, right) {
+ return { type: "binaryop", left, op: "|", right };
+ }
+ Sources.or = or;
+ /** Create a source which negates the underlying source. */
+ function negate(child) {
+ return { type: "negate", child };
+ }
+ Sources.negate = negate;
+ function empty() {
+ return { type: "empty" };
+ }
+ Sources.empty = empty;
+})(Sources || (Sources = {}));
+
+/** Emoji regex without any additional flags. */
+const EMOJI_REGEX = new RegExp(emojiRegex(), "");
+/** Provides a lookup table for unit durations of the given type. */
+const DURATION_TYPES = {
+ year: Duration.fromObject({ years: 1 }),
+ years: Duration.fromObject({ years: 1 }),
+ yr: Duration.fromObject({ years: 1 }),
+ yrs: Duration.fromObject({ years: 1 }),
+ month: Duration.fromObject({ months: 1 }),
+ months: Duration.fromObject({ months: 1 }),
+ mo: Duration.fromObject({ months: 1 }),
+ mos: Duration.fromObject({ months: 1 }),
+ week: Duration.fromObject({ weeks: 1 }),
+ weeks: Duration.fromObject({ weeks: 1 }),
+ wk: Duration.fromObject({ weeks: 1 }),
+ wks: Duration.fromObject({ weeks: 1 }),
+ w: Duration.fromObject({ weeks: 1 }),
+ day: Duration.fromObject({ days: 1 }),
+ days: Duration.fromObject({ days: 1 }),
+ d: Duration.fromObject({ days: 1 }),
+ hour: Duration.fromObject({ hours: 1 }),
+ hours: Duration.fromObject({ hours: 1 }),
+ hr: Duration.fromObject({ hours: 1 }),
+ hrs: Duration.fromObject({ hours: 1 }),
+ h: Duration.fromObject({ hours: 1 }),
+ minute: Duration.fromObject({ minutes: 1 }),
+ minutes: Duration.fromObject({ minutes: 1 }),
+ min: Duration.fromObject({ minutes: 1 }),
+ mins: Duration.fromObject({ minutes: 1 }),
+ m: Duration.fromObject({ minutes: 1 }),
+ second: Duration.fromObject({ seconds: 1 }),
+ seconds: Duration.fromObject({ seconds: 1 }),
+ sec: Duration.fromObject({ seconds: 1 }),
+ secs: Duration.fromObject({ seconds: 1 }),
+ s: Duration.fromObject({ seconds: 1 }),
+};
+/** Shorthand for common dates (relative to right now). */
+const DATE_SHORTHANDS = {
+ now: () => DateTime.local(),
+ today: () => DateTime.local().startOf("day"),
+ yesterday: () => DateTime.local()
+ .startOf("day")
+ .minus(Duration.fromObject({ days: 1 })),
+ tomorrow: () => DateTime.local()
+ .startOf("day")
+ .plus(Duration.fromObject({ days: 1 })),
+ sow: () => DateTime.local().startOf("week"),
+ "start-of-week": () => DateTime.local().startOf("week"),
+ eow: () => DateTime.local().endOf("week"),
+ "end-of-week": () => DateTime.local().endOf("week"),
+ soy: () => DateTime.local().startOf("year"),
+ "start-of-year": () => DateTime.local().startOf("year"),
+ eoy: () => DateTime.local().endOf("year"),
+ "end-of-year": () => DateTime.local().endOf("year"),
+ som: () => DateTime.local().startOf("month"),
+ "start-of-month": () => DateTime.local().startOf("month"),
+ eom: () => DateTime.local().endOf("month"),
+ "end-of-month": () => DateTime.local().endOf("month"),
+};
+/**
+ * Keywords which cannot be used as variables directly. Use `row.` if it is a variable you have defined and want
+ * to access.
+ */
+const KEYWORDS = ["FROM", "WHERE", "LIMIT", "GROUP", "FLATTEN"];
+///////////////
+// Utilities //
+///////////////
+/** Split on unescaped pipes in an inner link. */
+function splitOnUnescapedPipe(link) {
+ let pipe = -1;
+ while ((pipe = link.indexOf("|", pipe + 1)) >= 0) {
+ if (pipe > 0 && link[pipe - 1] == "\\")
+ continue;
+ return [link.substring(0, pipe).replace(/\\\|/g, "|"), link.substring(pipe + 1)];
+ }
+ return [link.replace(/\\\|/g, "|"), undefined];
+}
+/** Attempt to parse the inside of a link to pull out display name, subpath, etc. */
+function parseInnerLink(rawlink) {
+ let [link, display] = splitOnUnescapedPipe(rawlink);
+ return Link.infer(link, false, display);
+}
+/** Create a left-associative binary parser which parses the given sub-element and separator. Handles whitespace. */
+function createBinaryParser(child, sep, combine) {
+ return parsimmon_umd_minExports.seqMap(child, parsimmon_umd_minExports.seq(parsimmon_umd_minExports.optWhitespace, sep, parsimmon_umd_minExports.optWhitespace, child).many(), (first, rest) => {
+ if (rest.length == 0)
+ return first;
+ let node = combine(first, rest[0][1], rest[0][3]);
+ for (let index = 1; index < rest.length; index++) {
+ node = combine(node, rest[index][1], rest[index][3]);
+ }
+ return node;
+ });
+}
+function chainOpt(base, ...funcs) {
+ return parsimmon_umd_minExports.custom((success, failure) => {
+ return (input, i) => {
+ let result = base._(input, i);
+ if (!result.status)
+ return result;
+ for (let func of funcs) {
+ let next = func(result.value)._(input, result.index);
+ if (!next.status)
+ return result;
+ result = next;
+ }
+ return result;
+ };
+ });
+}
+const EXPRESSION = parsimmon_umd_minExports.createLanguage({
+ // A floating point number; the decimal point is optional.
+ number: q => parsimmon_umd_minExports.regexp(/-?[0-9]+(\.[0-9]+)?/)
+ .map(str => Number.parseFloat(str))
+ .desc("number"),
+ // A quote-surrounded string which supports escape characters ('\').
+ string: q => parsimmon_umd_minExports.string('"')
+ .then(parsimmon_umd_minExports.alt(q.escapeCharacter, parsimmon_umd_minExports.noneOf('"\\'))
+ .atLeast(0)
+ .map(chars => chars.join("")))
+ .skip(parsimmon_umd_minExports.string('"'))
+ .desc("string"),
+ escapeCharacter: _ => parsimmon_umd_minExports.string("\\")
+ .then(parsimmon_umd_minExports.any)
+ .map(escaped => {
+ // If we are escaping a backslash or a quote, pass in on in escaped form
+ if (escaped === '"')
+ return '"';
+ if (escaped === "\\")
+ return "\\";
+ else
+ return "\\" + escaped;
+ }),
+ // A boolean true/false value.
+ bool: _ => parsimmon_umd_minExports.regexp(/true|false|True|False/)
+ .map(str => str.toLowerCase() == "true")
+ .desc("boolean ('true' or 'false')"),
+ // A tag of the form '#stuff/hello-there'.
+ tag: _ => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("#"), parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/[^\u2000-\u206F\u2E00-\u2E7F'!"#$%&()*+,.:;<=>?@^`{|}~\[\]\\\s]/).desc("text")).many(), (start, rest) => start + rest.join("")).desc("tag ('#hello/stuff')"),
+ // A variable identifier, which is alphanumeric and must start with a letter or... emoji.
+ identifier: _ => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/\p{Letter}/u), parsimmon_umd_minExports.regexp(EMOJI_REGEX).desc("text")), parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(/[0-9\p{Letter}_-]/u), parsimmon_umd_minExports.regexp(EMOJI_REGEX).desc("text")).many(), (first, rest) => first + rest.join("")).desc("variable identifier"),
+ // An Obsidian link of the form [[]].
+ link: _ => parsimmon_umd_minExports.regexp(/\[\[([^\[\]]*?)\]\]/u, 1)
+ .map(linkInner => parseInnerLink(linkInner))
+ .desc("file link"),
+ // An embeddable link which can start with '!'. This overlaps with the normal negation operator, so it is only
+ // provided for metadata parsing.
+ embedLink: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("!").atMost(1), q.link, (p, l) => {
+ if (p.length > 0)
+ l.embed = true;
+ return l;
+ }).desc("file link"),
+ // Binary plus or minus operator.
+ binaryPlusMinus: _ => parsimmon_umd_minExports.regexp(/\+|-/)
+ .map(str => str)
+ .desc("'+' or '-'"),
+ // Binary times or divide operator.
+ binaryMulDiv: _ => parsimmon_umd_minExports.regexp(/\*|\/|%/)
+ .map(str => str)
+ .desc("'*' or '/' or '%'"),
+ // Binary comparison operator.
+ binaryCompareOp: _ => parsimmon_umd_minExports.regexp(/>=|<=|!=|>|<|=/)
+ .map(str => str)
+ .desc("'>=' or '<=' or '!=' or '=' or '>' or '<'"),
+ // Binary boolean combination operator.
+ binaryBooleanOp: _ => parsimmon_umd_minExports.regexp(/and|or|&|\|/i)
+ .map(str => {
+ if (str.toLowerCase() == "and")
+ return "&";
+ else if (str.toLowerCase() == "or")
+ return "|";
+ else
+ return str;
+ })
+ .desc("'and' or 'or'"),
+ // A date which can be YYYY-MM[-DDTHH:mm:ss].
+ rootDate: _ => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.regexp(/\d{4}/), parsimmon_umd_minExports.string("-"), parsimmon_umd_minExports.regexp(/\d{2}/), (year, _, month) => {
+ return DateTime.fromObject({ year: Number.parseInt(year), month: Number.parseInt(month) });
+ }).desc("date in format YYYY-MM[-DDTHH-MM-SS.MS]"),
+ dateShorthand: _ => parsimmon_umd_minExports.alt(...Object.keys(DATE_SHORTHANDS)
+ .sort((a, b) => b.length - a.length)
+ .map(parsimmon_umd_minExports.string)),
+ date: q => chainOpt(q.rootDate, (ym) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("-"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, day) => ym.set({ day: Number.parseInt(day) })), (ymd) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("T"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, hour) => ymd.set({ hour: Number.parseInt(hour) })), (ymdh) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string(":"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, minute) => ymdh.set({ minute: Number.parseInt(minute) })), (ymdhm) => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string(":"), parsimmon_umd_minExports.regexp(/\d{2}/), (_, second) => ymdhm.set({ second: Number.parseInt(second) })), (ymdhms) => parsimmon_umd_minExports.alt(parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("."), parsimmon_umd_minExports.regexp(/\d{3}/), (_, millisecond) => ymdhms.set({ millisecond: Number.parseInt(millisecond) })), parsimmon_umd_minExports.succeed(ymdhms) // pass
+ ), (dt) => parsimmon_umd_minExports.alt(parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("+").or(parsimmon_umd_minExports.string("-")), parsimmon_umd_minExports.regexp(/\d{1,2}(:\d{2})?/), (pm, hr) => dt.setZone("UTC" + pm + hr, { keepLocalTime: true })), parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("Z"), () => dt.setZone("utc", { keepLocalTime: true })), parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("["), parsimmon_umd_minExports.regexp(/[0-9A-Za-z+-\/]+/u), parsimmon_umd_minExports.string("]"), (_a, zone, _b) => dt.setZone(zone, { keepLocalTime: true }))))
+ .assert((dt) => dt.isValid, "valid date")
+ .desc("date in format YYYY-MM[-DDTHH-MM-SS.MS]"),
+ // A date, plus various shorthand times of day it could be.
+ datePlus: q => parsimmon_umd_minExports.alt(q.dateShorthand.map(d => DATE_SHORTHANDS[d]()), q.date).desc("date in format YYYY-MM[-DDTHH-MM-SS.MS] or in shorthand"),
+ // A duration of time.
+ durationType: _ => parsimmon_umd_minExports.alt(...Object.keys(DURATION_TYPES)
+ .sort((a, b) => b.length - a.length)
+ .map(parsimmon_umd_minExports.string)),
+ duration: q => parsimmon_umd_minExports.seqMap(q.number, parsimmon_umd_minExports.optWhitespace, q.durationType, (count, _, t) => DURATION_TYPES[t].mapUnits(x => x * count))
+ .sepBy1(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace).or(parsimmon_umd_minExports.optWhitespace))
+ .map(durations => durations.reduce((p, c) => p.plus(c)))
+ .desc("duration like 4hr2min"),
+ // A raw null value.
+ rawNull: _ => parsimmon_umd_minExports.string("null"),
+ // Source parsing.
+ tagSource: q => q.tag.map(tag => Sources.tag(tag)),
+ csvSource: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("csv(").skip(parsimmon_umd_minExports.optWhitespace), q.string, parsimmon_umd_minExports.string(")"), (_1, path, _2) => Sources.csv(path)),
+ linkIncomingSource: q => q.link.map(link => Sources.link(link.path, true)),
+ linkOutgoingSource: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("outgoing(").skip(parsimmon_umd_minExports.optWhitespace), q.link, parsimmon_umd_minExports.string(")"), (_1, link, _2) => Sources.link(link.path, false)),
+ folderSource: q => q.string.map(str => Sources.folder(str)),
+ parensSource: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("("), parsimmon_umd_minExports.optWhitespace, q.source, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (_1, _2, field, _3, _4) => field),
+ negateSource: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.alt(parsimmon_umd_minExports.string("-"), parsimmon_umd_minExports.string("!")), q.atomSource, (_, source) => Sources.negate(source)),
+ atomSource: q => parsimmon_umd_minExports.alt(q.parensSource, q.negateSource, q.linkOutgoingSource, q.linkIncomingSource, q.folderSource, q.tagSource, q.csvSource),
+ binaryOpSource: q => createBinaryParser(q.atomSource, q.binaryBooleanOp.map(s => s), Sources.binaryOp),
+ source: q => q.binaryOpSource,
+ // Field parsing.
+ variableField: q => q.identifier
+ .chain(r => {
+ if (KEYWORDS.includes(r.toUpperCase())) {
+ return parsimmon_umd_minExports.fail("Variable fields cannot be a keyword (" + KEYWORDS.join(" or ") + ")");
+ }
+ else {
+ return parsimmon_umd_minExports.succeed(Fields.variable(r));
+ }
+ })
+ .desc("variable"),
+ numberField: q => q.number.map(val => Fields.literal(val)).desc("number"),
+ stringField: q => q.string.map(val => Fields.literal(val)).desc("string"),
+ boolField: q => q.bool.map(val => Fields.literal(val)).desc("boolean"),
+ dateField: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("date("), parsimmon_umd_minExports.optWhitespace, q.datePlus, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (prefix, _1, date, _2, postfix) => Fields.literal(date)).desc("date"),
+ durationField: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("dur("), parsimmon_umd_minExports.optWhitespace, q.duration, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (prefix, _1, dur, _2, postfix) => Fields.literal(dur)).desc("duration"),
+ nullField: q => q.rawNull.map(_ => Fields.NULL),
+ linkField: q => q.link.map(f => Fields.literal(f)),
+ listField: q => q.field
+ .sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace))
+ .wrap(parsimmon_umd_minExports.string("[").skip(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.optWhitespace.then(parsimmon_umd_minExports.string("]")))
+ .map(l => Fields.list(l))
+ .desc("list ('[1, 2, 3]')"),
+ objectField: q => parsimmon_umd_minExports.seqMap(q.identifier.or(q.string), parsimmon_umd_minExports.string(":").trim(parsimmon_umd_minExports.optWhitespace), q.field, (name, _sep, value) => {
+ return { name, value };
+ })
+ .sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace))
+ .wrap(parsimmon_umd_minExports.string("{").skip(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.optWhitespace.then(parsimmon_umd_minExports.string("}")))
+ .map(vals => {
+ let res = {};
+ for (let entry of vals)
+ res[entry.name] = entry.value;
+ return Fields.object(res);
+ })
+ .desc("object ('{ a: 1, b: 2 }')"),
+ atomInlineField: q => parsimmon_umd_minExports.alt(q.date, q.duration.map(d => normalizeDuration(d)), q.string, q.tag, q.embedLink, q.bool, q.number, q.rawNull),
+ inlineFieldList: q => q.atomInlineField.sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace).lookahead(q.atomInlineField)),
+ inlineField: q => parsimmon_umd_minExports.alt(parsimmon_umd_minExports.seqMap(q.atomInlineField, parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace), q.inlineFieldList, (f, _s, l) => [f].concat(l)), q.atomInlineField),
+ atomField: q => parsimmon_umd_minExports.alt(
+ // Place embed links above negated fields as they are the special parser case '![[thing]]' and are generally unambiguous.
+ q.embedLink.map(l => Fields.literal(l)), q.negatedField, q.linkField, q.listField, q.objectField, q.lambdaField, q.parensField, q.boolField, q.numberField, q.stringField, q.dateField, q.durationField, q.nullField, q.variableField),
+ indexField: q => parsimmon_umd_minExports.seqMap(q.atomField, parsimmon_umd_minExports.alt(q.dotPostfix, q.indexPostfix, q.functionPostfix).many(), (obj, postfixes) => {
+ let result = obj;
+ for (let post of postfixes) {
+ switch (post.type) {
+ case "dot":
+ result = Fields.index(result, Fields.literal(post.field));
+ break;
+ case "index":
+ result = Fields.index(result, post.field);
+ break;
+ case "function":
+ result = Fields.func(result, post.fields);
+ break;
+ }
+ }
+ return result;
+ }),
+ negatedField: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("!"), q.indexField, (_, field) => Fields.negate(field)).desc("negated field"),
+ parensField: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("("), parsimmon_umd_minExports.optWhitespace, q.field, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (_1, _2, field, _3, _4) => field),
+ lambdaField: q => parsimmon_umd_minExports.seqMap(q.identifier
+ .sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace))
+ .wrap(parsimmon_umd_minExports.string("(").trim(parsimmon_umd_minExports.optWhitespace), parsimmon_umd_minExports.string(")").trim(parsimmon_umd_minExports.optWhitespace)), parsimmon_umd_minExports.string("=>").trim(parsimmon_umd_minExports.optWhitespace), q.field, (ident, _ignore, value) => {
+ return { type: "lambda", arguments: ident, value };
+ }),
+ dotPostfix: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("."), q.identifier, (_, field) => {
+ return { type: "dot", field: field };
+ }),
+ indexPostfix: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("["), parsimmon_umd_minExports.optWhitespace, q.field, parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string("]"), (_, _2, field, _3, _4) => {
+ return { type: "index", field };
+ }),
+ functionPostfix: q => parsimmon_umd_minExports.seqMap(parsimmon_umd_minExports.string("("), parsimmon_umd_minExports.optWhitespace, q.field.sepBy(parsimmon_umd_minExports.string(",").trim(parsimmon_umd_minExports.optWhitespace)), parsimmon_umd_minExports.optWhitespace, parsimmon_umd_minExports.string(")"), (_, _1, fields, _2, _3) => {
+ return { type: "function", fields };
+ }),
+ // The precedence hierarchy of operators - multiply/divide, add/subtract, compare, and then boolean operations.
+ binaryMulDivField: q => createBinaryParser(q.indexField, q.binaryMulDiv, Fields.binaryOp),
+ binaryPlusMinusField: q => createBinaryParser(q.binaryMulDivField, q.binaryPlusMinus, Fields.binaryOp),
+ binaryCompareField: q => createBinaryParser(q.binaryPlusMinusField, q.binaryCompareOp, Fields.binaryOp),
+ binaryBooleanField: q => createBinaryParser(q.binaryCompareField, q.binaryBooleanOp, Fields.binaryOp),
+ binaryOpField: q => q.binaryBooleanField,
+ field: q => q.binaryOpField,
+});
+/**
+ * Attempt to parse a field from the given text, returning a string error if the
+ * parse failed.
+ */
+function parseField(text) {
+ try {
+ return Result.success(EXPRESSION.field.tryParse(text));
+ }
+ catch (error) {
+ return Result.failure("" + error);
+ }
+}
+
+/** Parse inline fields and other embedded metadata in a line. */
+/** The wrapper characters that can be used to define an inline field. */
+const INLINE_FIELD_WRAPPERS = Object.freeze({
+ "[": "]",
+ "(": ")",
+});
+/**
+ * Find a matching closing bracket that occurs at or after `start`, respecting nesting and escapes. If found,
+ * returns the value contained within and the string index after the end of the value.
+ */
+function findClosing(line, start, open, close) {
+ let nesting = 0;
+ let escaped = false;
+ for (let index = start; index < line.length; index++) {
+ let char = line.charAt(index);
+ // Allows for double escapes like '\\' to be rendered normally.
+ if (char == "\\") {
+ escaped = !escaped;
+ continue;
+ }
+ // If escaped, ignore the next character for computing nesting, regardless of what it is.
+ if (escaped) {
+ escaped = false;
+ continue;
+ }
+ if (char == open)
+ nesting++;
+ else if (char == close)
+ nesting--;
+ // Only occurs if we are on a close character and there is no more nesting.
+ if (nesting < 0)
+ return { value: line.substring(start, index).trim(), endIndex: index + 1 };
+ escaped = false;
+ }
+ return undefined;
+}
+/** Find the '::' separator in an inline field. */
+function findSeparator(line, start) {
+ let sep = line.indexOf("::", start);
+ if (sep < 0)
+ return undefined;
+ return { key: line.substring(start, sep).trim(), valueIndex: sep + 2 };
+}
+/** Try to completely parse an inline field starting at the given position. Assumes `start` is on a wrapping character. */
+function findSpecificInlineField(line, start) {
+ let open = line.charAt(start);
+ let key = findSeparator(line, start + 1);
+ if (key === undefined)
+ return undefined;
+ // Fail the match if we find any separator characters (not allowed in keys).
+ for (let sep of Object.keys(INLINE_FIELD_WRAPPERS).concat(Object.values(INLINE_FIELD_WRAPPERS))) {
+ if (key.key.includes(sep))
+ return undefined;
+ }
+ let value = findClosing(line, key.valueIndex, open, INLINE_FIELD_WRAPPERS[open]);
+ if (value === undefined)
+ return undefined;
+ return {
+ key: key.key,
+ value: value.value,
+ start: start,
+ startValue: key.valueIndex,
+ end: value.endIndex,
+ wrapping: open,
+ };
+}
+/** Parse a textual inline field value into something we can work with. */
+function parseInlineValue(value) {
+ // Empty inline values (i.e., no text) should map to null to match long-term Dataview semantics.
+ // Null is also a more universal type to deal with than strings, since all functions accept nulls.
+ if (value.trim() == "")
+ return null;
+ // The stripped literal field parser understands all of the non-array/non-object fields and can parse them for us.
+ // Inline field objects are not currently supported; inline array objects have to be handled by the parser
+ // separately.
+ let inline = EXPRESSION.inlineField.parse(value);
+ if (inline.status)
+ return inline.value;
+ else
+ return value;
+}
+/** Extracts inline fields of the form '[key:: value]' from a line of text. This is done in a relatively
+ * "robust" way to avoid failing due to bad nesting or other interfering Markdown symbols:
+ *
+ * - Look for any wrappers ('[' and '(') in the line, trying to parse whatever comes after it as an inline key::.
+ * - If successful, scan until you find a matching end bracket, and parse whatever remains as an inline value.
+ */
+function extractInlineFields(line, includeTaskFields = false) {
+ let fields = [];
+ for (let wrapper of Object.keys(INLINE_FIELD_WRAPPERS)) {
+ let foundIndex = line.indexOf(wrapper);
+ while (foundIndex >= 0) {
+ let parsedField = findSpecificInlineField(line, foundIndex);
+ if (!parsedField) {
+ foundIndex = line.indexOf(wrapper, foundIndex + 1);
+ continue;
+ }
+ fields.push(parsedField);
+ foundIndex = line.indexOf(wrapper, parsedField.end);
+ }
+ }
+ if (includeTaskFields)
+ fields = fields.concat(extractSpecialTaskFields(line));
+ fields.sort((a, b) => a.start - b.start);
+ let filteredFields = [];
+ for (let i = 0; i < fields.length; i++) {
+ if (i == 0 || filteredFields[filteredFields.length - 1].end < fields[i].start) {
+ filteredFields.push(fields[i]);
+ }
+ }
+ return filteredFields;
+}
+/** Validates that a raw field name has a valid form. */
+const FULL_LINE_KEY_PART = parsimmon_umd_minExports.alt(parsimmon_umd_minExports.regexp(new RegExp(emojiRegex(), "u")), parsimmon_umd_minExports.regexp(/[0-9\p{Letter}\w\s_/-]+/u))
+ .many()
+ .map(parts => parts.join(""));
+parsimmon_umd_minExports.regexp(/[^0-9\w\p{Letter}]*/u)
+ .then(FULL_LINE_KEY_PART)
+ .skip(parsimmon_umd_minExports.regexp(/[_\*~`]*/u));
+const CREATED_DATE_REGEX = /\u{2795}\s*(\d{4}-\d{2}-\d{2})/u;
+const DUE_DATE_REGEX = /(?:\u{1F4C5}|\u{1F4C6}|\u{1F5D3}\u{FE0F}?)\s*(\d{4}-\d{2}-\d{2})/u;
+const DONE_DATE_REGEX = /\u{2705}\s*(\d{4}-\d{2}-\d{2})/u;
+const SCHEDULED_DATE_REGEX = /[\u{23F3}\u{231B}]\s*(\d{4}-\d{2}-\d{2})/u;
+const START_DATE_REGEX = /\u{1F6EB}\s*(\d{4}-\d{2}-\d{2})/u;
+const EMOJI_REGEXES = [
+ { regex: CREATED_DATE_REGEX, key: "created" },
+ { regex: START_DATE_REGEX, key: "start" },
+ { regex: SCHEDULED_DATE_REGEX, key: "scheduled" },
+ { regex: DUE_DATE_REGEX, key: "due" },
+ { regex: DONE_DATE_REGEX, key: "completion" },
+];
+/** Parse special completed/due/done task fields which are marked via emoji. */
+function extractSpecialTaskFields(line) {
+ let results = [];
+ for (let { regex, key } of EMOJI_REGEXES) {
+ const match = regex.exec(line);
+ if (!match)
+ continue;
+ results.push({
+ key,
+ value: match[1],
+ start: match.index,
+ startValue: match.index + 1,
+ end: match.index + match[0].length,
+ wrapping: "emoji-shorthand",
+ });
+ }
+ return results;
+}
+/** Sets or replaces the value of an inline field; if the value is 'undefined', deletes the key. */
+function setInlineField(source, key, value) {
+ let existing = extractInlineFields(source);
+ let existingKeys = existing.filter(f => f.key == key);
+ // Don't do anything if there are duplicate keys OR the key already doesn't exist.
+ if (existingKeys.length > 2 || (existingKeys.length == 0 && !value))
+ return source;
+ let existingKey = existingKeys[0];
+ let annotation = value ? `[${key}:: ${value}]` : "";
+ if (existingKey) {
+ let prefix = source.substring(0, existingKey.start);
+ let suffix = source.substring(existingKey.end);
+ if (annotation)
+ return `${prefix}${annotation}${suffix}`;
+ else
+ return `${prefix}${suffix.trimStart()}`;
+ }
+ else if (annotation) {
+ return `${source.trimEnd()} ${annotation}`;
+ }
+ return source;
+}
+function setEmojiShorthandCompletionField(source, value) {
+ const existing = extractInlineFields(source, true);
+ const existingKeys = existing.filter(f => f.key === "completion" && f.wrapping === "emoji-shorthand");
+ // Don't do anything if there are duplicate keys OR the key already doesn't exist.
+ if (existingKeys.length > 2 || (existingKeys.length == 0 && !value))
+ return source;
+ /* No wrapper, add own spacing at start */
+ const annotation = value ? ` ✅ ${value}` : "";
+ let existingKey = existingKeys[0];
+ if (existingKey) {
+ const prefix = source.substring(0, existingKey.start);
+ const suffix = source.substring(existingKey.end);
+ return `${prefix.trimEnd()}${annotation}${suffix}`;
+ }
+ else {
+ return `${source.trimEnd()}${annotation}`;
+ }
+}
+
+/** All extracted markdown file metadata obtained from a file. */
+class PageMetadata {
+ /** The path this file exists at. */
+ path;
+ /** Obsidian-provided date this page was created. */
+ ctime;
+ /** Obsidian-provided date this page was modified. */
+ mtime;
+ /** Obsidian-provided size of this page in bytes. */
+ size;
+ /** The day associated with this page, if relevant. */
+ day;
+ /** The first H1/H2 header in the file. May not exist. */
+ title;
+ /** All of the fields contained in this markdown file - both frontmatter AND in-file links. */
+ fields;
+ /** All of the exact tags (prefixed with '#') in this file overall. */
+ tags;
+ /** All of the aliases defined for this file. */
+ aliases;
+ /** All OUTGOING links (including embeds, header + block links) in this file. */
+ links;
+ /** All list items contained within this page. Filter for tasks to get just tasks. */
+ lists;
+ /** The raw frontmatter for this document. */
+ frontmatter;
+ constructor(path, init) {
+ this.path = path;
+ this.fields = new Map();
+ this.frontmatter = {};
+ this.tags = new Set();
+ this.aliases = new Set();
+ this.links = [];
+ Object.assign(this, init);
+ this.lists = (this.lists || []).map(l => new ListItem$1(l));
+ }
+ /** Canonicalize raw links and other data in partial data with normalizers, returning a completed object. */
+ static canonicalize(data, linkNormalizer) {
+ // Mutate the data for now, which is probably a bad idea but... all well.
+ if (data.frontmatter) {
+ data.frontmatter = Values.mapLeaves(data.frontmatter, t => Values.isLink(t) ? linkNormalizer(t) : t);
+ }
+ if (data.fields) {
+ for (let [key, value] of data.fields.entries()) {
+ data.fields.set(key, Values.mapLeaves(value, t => (Values.isLink(t) ? linkNormalizer(t) : t)));
+ }
+ }
+ if (data.lists) {
+ for (let item of data.lists) {
+ for (let [key, value] of item.fields.entries()) {
+ item.fields.set(key, value.map(x => Values.mapLeaves(x, t => (Values.isLink(t) ? linkNormalizer(t) : t))));
+ }
+ }
+ }
+ if (data.links) {
+ data.links = data.links.map(l => linkNormalizer(l));
+ }
+ // This is pretty ugly, but it's not possible to normalize on the worker thread that does parsing.
+ // The best way to improve this is to instead just canonicalize the entire data object; I can try to
+ // optimize `Values.mapLeaves` to only mutate if it actually changes things.
+ return new PageMetadata(data.path, data);
+ }
+ /** The name (based on path) of this file. */
+ name() {
+ return getFileTitle(this.path);
+ }
+ /** The containing folder (based on path) of this file. */
+ folder() {
+ return getParentFolder(this.path);
+ }
+ /** The extension of this file (likely 'md'). */
+ extension() {
+ return getExtension(this.path);
+ }
+ /** Return a set of tags AND all of their parent tags (so #hello/yes would become #hello, #hello/yes). */
+ fullTags() {
+ let result = new Set();
+ for (let tag of this.tags) {
+ for (let subtag of extractSubtags(tag))
+ result.add(subtag);
+ }
+ return result;
+ }
+ /** Convert all links in this file to file links. */
+ fileLinks() {
+ // We want to make them distinct, but where links are not raw links we
+ // now keep the additional metadata.
+ let distinctLinks = new Set(this.links);
+ return Array.from(distinctLinks);
+ }
+ /** Map this metadata to a full object; uses the index for additional data lookups. */
+ serialize(index, cache) {
+ // Convert list items via the canonicalization cache.
+ let realCache = cache ?? new ListSerializationCache(this.lists);
+ let result = {
+ file: {
+ path: this.path,
+ folder: this.folder(),
+ name: this.name(),
+ link: Link.file(this.path),
+ outlinks: this.fileLinks(),
+ inlinks: Array.from(index.links.getInverse(this.path)).map(l => Link.file(l)),
+ etags: Array.from(this.tags),
+ tags: Array.from(this.fullTags()),
+ aliases: Array.from(this.aliases),
+ lists: this.lists.map(l => realCache.get(l.line)),
+ tasks: this.lists.filter(l => !!l.task).map(l => realCache.get(l.line)),
+ ctime: this.ctime,
+ cday: stripTime(this.ctime),
+ mtime: this.mtime,
+ mday: stripTime(this.mtime),
+ size: this.size,
+ starred: index.starred.starred(this.path),
+ frontmatter: Values.deepCopy(this.frontmatter),
+ ext: this.extension(),
+ },
+ };
+ // Add the current day if present.
+ if (this.day)
+ result.file.day = this.day;
+ // Then append the computed fields.
+ for (let [key, value] of this.fields.entries()) {
+ if (key in result)
+ continue; // Don't allow fields to override existing keys.
+ result[key] = value;
+ }
+ return result;
+ }
+}
+/** A list item inside of a list. */
+class ListItem$1 {
+ /** The symbol ('*', '-', '1.') used to define this list item. */
+ symbol;
+ /** A link which points to this task, or to the closest block that this task is contained in. */
+ link;
+ /** A link to the section that contains this list element; could be a file if this is not in a section. */
+ section;
+ /** The text of this list item. This may be multiple lines of markdown. */
+ text;
+ /** The line that this list item starts on in the file. */
+ line;
+ /** The number of lines that define this list item. */
+ lineCount;
+ /** The line number for the first list item in the list this item belongs to. */
+ list;
+ /** Any links contained within this list item. */
+ links;
+ /** The tags contained within this list item. */
+ tags;
+ /** The raw Obsidian-provided position for where this task is. */
+ position;
+ /** The line number of the parent list item, if present; if this is undefined, this is a root item. */
+ parent;
+ /** The line numbers of children of this list item. */
+ children;
+ /** The block ID for this item, if one is present. */
+ blockId;
+ /** Any fields defined in this list item. For tasks, this includes fields underneath the task. */
+ fields;
+ task;
+ constructor(init) {
+ Object.assign(this, init);
+ this.fields = this.fields || new Map();
+ this.tags = this.tags || new Set();
+ this.children = this.children || [];
+ this.links = this.links || [];
+ }
+ id() {
+ return `${this.file().path}-${this.line}`;
+ }
+ file() {
+ return this.link.toFile();
+ }
+ markdown() {
+ if (this.task)
+ return `${this.symbol} [${this.task.completed ? "x" : " "}] ${this.text}`;
+ else
+ return `${this.symbol} ${this.text}`;
+ }
+ created() {
+ return (this.fields.get("created") ?? this.fields.get("ctime") ?? this.fields.get("cday"))?.[0];
+ }
+ due() {
+ return (this.fields.get("due") ?? this.fields.get("duetime") ?? this.fields.get("dueday"))?.[0];
+ }
+ completed() {
+ return (this.fields.get("completed") ??
+ this.fields.get("completion") ??
+ this.fields.get("comptime") ??
+ this.fields.get("compday"))?.[0];
+ }
+ start() {
+ return this.fields.get("start")?.[0];
+ }
+ scheduled() {
+ return this.fields.get("scheduled")?.[0];
+ }
+ /** Create an API-friendly copy of this list item. De-duplication is done via the provided cache. */
+ serialize(cache) {
+ // Map children to their serialized/de-duplicated equivalents right away.
+ let children = this.children.map(l => cache.get(l)).filter((l) => l !== undefined);
+ let result = {
+ symbol: this.symbol,
+ link: this.link,
+ section: this.section,
+ text: this.text,
+ tags: Array.from(this.tags),
+ line: this.line,
+ lineCount: this.lineCount,
+ list: this.list,
+ outlinks: Array.from(this.links),
+ path: this.link.path,
+ children: children,
+ task: !!this.task,
+ annotated: this.fields.size > 0,
+ position: Values.deepCopy(this.position),
+ subtasks: children,
+ real: !!this.task,
+ header: this.section, // @deprecated, use 'item.section' instead.
+ };
+ if (this.parent || this.parent === 0)
+ result.parent = this.parent;
+ if (this.blockId)
+ result.blockId = this.blockId;
+ addFields(this.fields, result);
+ if (this.task) {
+ result.status = this.task.status;
+ result.checked = this.task.checked;
+ result.completed = this.task.completed;
+ result.fullyCompleted = this.task.fullyCompleted;
+ let created = this.created(), due = this.due(), completed = this.completed(), start = this.start(), scheduled = this.scheduled();
+ if (created)
+ result.created = Values.deepCopy(created);
+ if (due)
+ result.due = Values.deepCopy(due);
+ if (completed)
+ result.completion = Values.deepCopy(completed);
+ if (start)
+ result.start = Values.deepCopy(start);
+ if (scheduled)
+ result.scheduled = Values.deepCopy(scheduled);
+ }
+ return result;
+ }
+}
+//////////////////////////////////////////
+// Conversion / Serialization Utilities //
+//////////////////////////////////////////
+/** De-duplicates list items across section metadata and page metadata. */
+class ListSerializationCache {
+ listItems;
+ cache;
+ seen;
+ constructor(listItems) {
+ this.listItems = {};
+ this.cache = {};
+ this.seen = new Set();
+ for (let item of listItems)
+ this.listItems[item.line] = item;
+ }
+ get(lineno) {
+ if (lineno in this.cache)
+ return this.cache[lineno];
+ else if (this.seen.has(lineno)) {
+ console.log(`Dataview: Encountered a circular list (line number ${lineno}; children ${this.listItems[lineno].children.join(", ")})`);
+ return undefined;
+ }
+ this.seen.add(lineno);
+ let result = this.listItems[lineno].serialize(this);
+ this.cache[lineno] = result;
+ return result;
+ }
+}
+function addFields(fields, target) {
+ for (let [key, values] of fields.entries()) {
+ if (key in target)
+ continue;
+ target[key] = values.length == 1 ? values[0] : values;
+ }
+ return target;
+}
+
+/** Importer for markdown documents. */
+// TODO: Consider using an actual parser in lieu of a more expensive regex.
+const LIST_ITEM_REGEX = /^[\s>]*(\d+\.|\d+\)|\*|-|\+)\s*(\[.{0,1}\])?\s*(.*)$/mu;
+/** Recursively convert frontmatter into fields. We have to dance around YAML structure. */
+function parseFrontmatter(value) {
+ if (value == null) {
+ return null;
+ }
+ else if (typeof value === "object") {
+ if (Array.isArray(value)) {
+ let result = [];
+ for (let child of value) {
+ result.push(parseFrontmatter(child));
+ }
+ return result;
+ }
+ else if (value instanceof Date) {
+ let dateParse = DateTime.fromJSDate(value);
+ return dateParse;
+ }
+ else {
+ let object = value;
+ let result = {};
+ for (let key in object) {
+ result[key] = parseFrontmatter(object[key]);
+ }
+ return result;
+ }
+ }
+ else if (typeof value === "number") {
+ return value;
+ }
+ else if (typeof value === "boolean") {
+ return value;
+ }
+ else if (typeof value === "string") {
+ let dateParse = EXPRESSION.date.parse(value);
+ if (dateParse.status)
+ return dateParse.value;
+ let durationParse = EXPRESSION.duration.parse(value);
+ if (durationParse.status)
+ return durationParse.value;
+ let linkParse = EXPRESSION.embedLink.parse(value);
+ if (linkParse.status)
+ return linkParse.value;
+ return value;
+ }
+ // Backup if we don't understand the type.
+ return null;
+}
+
+/** Parse a CSV file into a collection of data rows. */
+function parseCsv(content) {
+ let parsed = papaparse_minExports.parse(content, {
+ header: true,
+ skipEmptyLines: true,
+ comments: "#",
+ dynamicTyping: true,
+ });
+ let rows = [];
+ for (let parsedRow of parsed.data) {
+ let fields = parseFrontmatter(parsedRow);
+ let result = {};
+ for (let [key, value] of Object.entries(fields)) {
+ result[key] = value;
+ result[canonicalizeVarName(key)] = value;
+ }
+ rows.push(result);
+ }
+ return rows;
+}
+
+/** Simplifies passing dataview values across the JS web worker barrier. */
+var Transferable;
+(function (Transferable) {
+ /** Convert a literal value to a serializer-friendly transferable value. */
+ function transferable(value) {
+ // Handle simple universal types first.
+ if (value instanceof Map) {
+ let copied = new Map();
+ for (let [key, val] of value.entries())
+ copied.set(transferable(key), transferable(val));
+ return copied;
+ }
+ else if (value instanceof Set) {
+ let copied = new Set();
+ for (let val of value)
+ copied.add(transferable(val));
+ return copied;
+ }
+ let wrapped = Values.wrapValue(value);
+ if (wrapped === undefined)
+ throw Error("Unrecognized transferable value: " + value);
+ switch (wrapped.type) {
+ case "null":
+ case "number":
+ case "string":
+ case "boolean":
+ return wrapped.value;
+ case "date":
+ return {
+ "___transfer-type": "date",
+ value: transferable(wrapped.value.toObject()),
+ options: {
+ zone: wrapped.value.zone.equals(SystemZone.instance) ? undefined : wrapped.value.zoneName,
+ },
+ };
+ case "duration":
+ return { "___transfer-type": "duration", value: transferable(wrapped.value.toObject()) };
+ case "array":
+ return wrapped.value.map(v => transferable(v));
+ case "link":
+ return { "___transfer-type": "link", value: transferable(wrapped.value.toObject()) };
+ case "object":
+ let result = {};
+ for (let [key, value] of Object.entries(wrapped.value))
+ result[key] = transferable(value);
+ return result;
+ }
+ }
+ Transferable.transferable = transferable;
+ /** Convert a transferable value back to a literal value we can work with. */
+ function value(transferable) {
+ if (transferable === null) {
+ return null;
+ }
+ else if (transferable === undefined) {
+ return undefined;
+ }
+ else if (transferable instanceof Map) {
+ let real = new Map();
+ for (let [key, val] of transferable.entries())
+ real.set(value(key), value(val));
+ return real;
+ }
+ else if (transferable instanceof Set) {
+ let real = new Set();
+ for (let val of transferable)
+ real.add(value(val));
+ return real;
+ }
+ else if (Array.isArray(transferable)) {
+ return transferable.map(v => value(v));
+ }
+ else if (typeof transferable === "object") {
+ if ("___transfer-type" in transferable) {
+ switch (transferable["___transfer-type"]) {
+ case "date":
+ let dateOpts = value(transferable.options);
+ let dateData = value(transferable.value);
+ return DateTime.fromObject(dateData, { zone: dateOpts.zone });
+ case "duration":
+ return Duration.fromObject(value(transferable.value));
+ case "link":
+ return Link.fromObject(value(transferable.value));
+ default:
+ throw Error(`Unrecognized transfer type '${transferable["___transfer-type"]}'`);
+ }
+ }
+ let result = {};
+ for (let [key, val] of Object.entries(transferable))
+ result[key] = value(val);
+ return result;
+ }
+ return transferable;
+ }
+ Transferable.value = value;
+})(Transferable || (Transferable = {}));
+
+function commonjsRequire(path) {
+ throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
+}
+
+var localforage$1 = {exports: {}};
+
+/*!
+ localForage -- Offline Storage, Improved
+ Version 1.10.0
+ https://localforage.github.io/localForage
+ (c) 2013-2017 Mozilla, Apache License 2.0
+*/
+localforage$1.exports;
+
+(function (module, exports) {
+ (function(f){{module.exports=f();}})(function(){return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof commonjsRequire=="function"&&commonjsRequire;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw (f.code="MODULE_NOT_FOUND", f)}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r);}return n[o].exports}var i=typeof commonjsRequire=="function"&&commonjsRequire;for(var o=0;o element; its readystatechange event will be fired asynchronously once it is inserted
+ // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
+ var scriptEl = global.document.createElement('script');
+ scriptEl.onreadystatechange = function () {
+ nextTick();
+
+ scriptEl.onreadystatechange = null;
+ scriptEl.parentNode.removeChild(scriptEl);
+ scriptEl = null;
+ };
+ global.document.documentElement.appendChild(scriptEl);
+ };
+ } else {
+ scheduleDrain = function () {
+ setTimeout(nextTick, 0);
+ };
+ }
+ }
+
+ var draining;
+ var queue = [];
+ //named nextTick for less confusing stack traces
+ function nextTick() {
+ draining = true;
+ var i, oldQueue;
+ var len = queue.length;
+ while (len) {
+ oldQueue = queue;
+ queue = [];
+ i = -1;
+ while (++i < len) {
+ oldQueue[i]();
+ }
+ len = queue.length;
+ }
+ draining = false;
+ }
+
+ module.exports = immediate;
+ function immediate(task) {
+ if (queue.push(task) === 1 && !draining) {
+ scheduleDrain();
+ }
+ }
+
+ }).call(this,typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
+ },{}],2:[function(_dereq_,module,exports){
+ var immediate = _dereq_(1);
+
+ /* istanbul ignore next */
+ function INTERNAL() {}
+
+ var handlers = {};
+
+ var REJECTED = ['REJECTED'];
+ var FULFILLED = ['FULFILLED'];
+ var PENDING = ['PENDING'];
+
+ module.exports = Promise;
+
+ function Promise(resolver) {
+ if (typeof resolver !== 'function') {
+ throw new TypeError('resolver must be a function');
+ }
+ this.state = PENDING;
+ this.queue = [];
+ this.outcome = void 0;
+ if (resolver !== INTERNAL) {
+ safelyResolveThenable(this, resolver);
+ }
+ }
+
+ Promise.prototype["catch"] = function (onRejected) {
+ return this.then(null, onRejected);
+ };
+ Promise.prototype.then = function (onFulfilled, onRejected) {
+ if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
+ typeof onRejected !== 'function' && this.state === REJECTED) {
+ return this;
+ }
+ var promise = new this.constructor(INTERNAL);
+ if (this.state !== PENDING) {
+ var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
+ unwrap(promise, resolver, this.outcome);
+ } else {
+ this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
+ }
+
+ return promise;
+ };
+ function QueueItem(promise, onFulfilled, onRejected) {
+ this.promise = promise;
+ if (typeof onFulfilled === 'function') {
+ this.onFulfilled = onFulfilled;
+ this.callFulfilled = this.otherCallFulfilled;
+ }
+ if (typeof onRejected === 'function') {
+ this.onRejected = onRejected;
+ this.callRejected = this.otherCallRejected;
+ }
+ }
+ QueueItem.prototype.callFulfilled = function (value) {
+ handlers.resolve(this.promise, value);
+ };
+ QueueItem.prototype.otherCallFulfilled = function (value) {
+ unwrap(this.promise, this.onFulfilled, value);
+ };
+ QueueItem.prototype.callRejected = function (value) {
+ handlers.reject(this.promise, value);
+ };
+ QueueItem.prototype.otherCallRejected = function (value) {
+ unwrap(this.promise, this.onRejected, value);
+ };
+
+ function unwrap(promise, func, value) {
+ immediate(function () {
+ var returnValue;
+ try {
+ returnValue = func(value);
+ } catch (e) {
+ return handlers.reject(promise, e);
+ }
+ if (returnValue === promise) {
+ handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
+ } else {
+ handlers.resolve(promise, returnValue);
+ }
+ });
+ }
+
+ handlers.resolve = function (self, value) {
+ var result = tryCatch(getThen, value);
+ if (result.status === 'error') {
+ return handlers.reject(self, result.value);
+ }
+ var thenable = result.value;
+
+ if (thenable) {
+ safelyResolveThenable(self, thenable);
+ } else {
+ self.state = FULFILLED;
+ self.outcome = value;
+ var i = -1;
+ var len = self.queue.length;
+ while (++i < len) {
+ self.queue[i].callFulfilled(value);
+ }
+ }
+ return self;
+ };
+ handlers.reject = function (self, error) {
+ self.state = REJECTED;
+ self.outcome = error;
+ var i = -1;
+ var len = self.queue.length;
+ while (++i < len) {
+ self.queue[i].callRejected(error);
+ }
+ return self;
+ };
+
+ function getThen(obj) {
+ // Make sure we only access the accessor once as required by the spec
+ var then = obj && obj.then;
+ if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
+ return function appyThen() {
+ then.apply(obj, arguments);
+ };
+ }
+ }
+
+ function safelyResolveThenable(self, thenable) {
+ // Either fulfill, reject or reject with error
+ var called = false;
+ function onError(value) {
+ if (called) {
+ return;
+ }
+ called = true;
+ handlers.reject(self, value);
+ }
+
+ function onSuccess(value) {
+ if (called) {
+ return;
+ }
+ called = true;
+ handlers.resolve(self, value);
+ }
+
+ function tryToUnwrap() {
+ thenable(onSuccess, onError);
+ }
+
+ var result = tryCatch(tryToUnwrap);
+ if (result.status === 'error') {
+ onError(result.value);
+ }
+ }
+
+ function tryCatch(func, value) {
+ var out = {};
+ try {
+ out.value = func(value);
+ out.status = 'success';
+ } catch (e) {
+ out.status = 'error';
+ out.value = e;
+ }
+ return out;
+ }
+
+ Promise.resolve = resolve;
+ function resolve(value) {
+ if (value instanceof this) {
+ return value;
+ }
+ return handlers.resolve(new this(INTERNAL), value);
+ }
+
+ Promise.reject = reject;
+ function reject(reason) {
+ var promise = new this(INTERNAL);
+ return handlers.reject(promise, reason);
+ }
+
+ Promise.all = all;
+ function all(iterable) {
+ var self = this;
+ if (Object.prototype.toString.call(iterable) !== '[object Array]') {
+ return this.reject(new TypeError('must be an array'));
+ }
+
+ var len = iterable.length;
+ var called = false;
+ if (!len) {
+ return this.resolve([]);
+ }
+
+ var values = new Array(len);
+ var resolved = 0;
+ var i = -1;
+ var promise = new this(INTERNAL);
+
+ while (++i < len) {
+ allResolver(iterable[i], i);
+ }
+ return promise;
+ function allResolver(value, i) {
+ self.resolve(value).then(resolveFromAll, function (error) {
+ if (!called) {
+ called = true;
+ handlers.reject(promise, error);
+ }
+ });
+ function resolveFromAll(outValue) {
+ values[i] = outValue;
+ if (++resolved === len && !called) {
+ called = true;
+ handlers.resolve(promise, values);
+ }
+ }
+ }
+ }
+
+ Promise.race = race;
+ function race(iterable) {
+ var self = this;
+ if (Object.prototype.toString.call(iterable) !== '[object Array]') {
+ return this.reject(new TypeError('must be an array'));
+ }
+
+ var len = iterable.length;
+ var called = false;
+ if (!len) {
+ return this.resolve([]);
+ }
+
+ var i = -1;
+ var promise = new this(INTERNAL);
+
+ while (++i < len) {
+ resolver(iterable[i]);
+ }
+ return promise;
+ function resolver(value) {
+ self.resolve(value).then(function (response) {
+ if (!called) {
+ called = true;
+ handlers.resolve(promise, response);
+ }
+ }, function (error) {
+ if (!called) {
+ called = true;
+ handlers.reject(promise, error);
+ }
+ });
+ }
+ }
+
+ },{"1":1}],3:[function(_dereq_,module,exports){
+ (function (global){
+ if (typeof global.Promise !== 'function') {
+ global.Promise = _dereq_(2);
+ }
+
+ }).call(this,typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
+ },{"2":2}],4:[function(_dereq_,module,exports){
+
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
+
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+ function getIDB() {
+ /* global indexedDB,webkitIndexedDB,mozIndexedDB,OIndexedDB,msIndexedDB */
+ try {
+ if (typeof indexedDB !== 'undefined') {
+ return indexedDB;
+ }
+ if (typeof webkitIndexedDB !== 'undefined') {
+ return webkitIndexedDB;
+ }
+ if (typeof mozIndexedDB !== 'undefined') {
+ return mozIndexedDB;
+ }
+ if (typeof OIndexedDB !== 'undefined') {
+ return OIndexedDB;
+ }
+ if (typeof msIndexedDB !== 'undefined') {
+ return msIndexedDB;
+ }
+ } catch (e) {
+ return;
+ }
+ }
+
+ var idb = getIDB();
+
+ function isIndexedDBValid() {
+ try {
+ // Initialize IndexedDB; fall back to vendor-prefixed versions
+ // if needed.
+ if (!idb || !idb.open) {
+ return false;
+ }
+ // We mimic PouchDB here;
+ //
+ // We test for openDatabase because IE Mobile identifies itself
+ // as Safari. Oh the lulz...
+ var isSafari = typeof openDatabase !== 'undefined' && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);
+
+ var hasFetch = typeof fetch === 'function' && fetch.toString().indexOf('[native code') !== -1;
+
+ // Safari <10.1 does not meet our requirements for IDB support
+ // (see: https://github.com/pouchdb/pouchdb/issues/5572).
+ // Safari 10.1 shipped with fetch, we can use that to detect it.
+ // Note: this creates issues with `window.fetch` polyfills and
+ // overrides; see:
+ // https://github.com/localForage/localForage/issues/856
+ return (!isSafari || hasFetch) && typeof indexedDB !== 'undefined' &&
+ // some outdated implementations of IDB that appear on Samsung
+ // and HTC Android devices <4.4 are missing IDBKeyRange
+ // See: https://github.com/mozilla/localForage/issues/128
+ // See: https://github.com/mozilla/localForage/issues/272
+ typeof IDBKeyRange !== 'undefined';
+ } catch (e) {
+ return false;
+ }
+ }
+
+ // Abstracts constructing a Blob object, so it also works in older
+ // browsers that don't support the native Blob constructor. (i.e.
+ // old QtWebKit versions, at least).
+ // Abstracts constructing a Blob object, so it also works in older
+ // browsers that don't support the native Blob constructor. (i.e.
+ // old QtWebKit versions, at least).
+ function createBlob(parts, properties) {
+ /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
+ parts = parts || [];
+ properties = properties || {};
+ try {
+ return new Blob(parts, properties);
+ } catch (e) {
+ if (e.name !== 'TypeError') {
+ throw e;
+ }
+ var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder;
+ var builder = new Builder();
+ for (var i = 0; i < parts.length; i += 1) {
+ builder.append(parts[i]);
+ }
+ return builder.getBlob(properties.type);
+ }
+ }
+
+ // This is CommonJS because lie is an external dependency, so Rollup
+ // can just ignore it.
+ if (typeof Promise === 'undefined') {
+ // In the "nopromises" build this will just throw if you don't have
+ // a global promise object, but it would throw anyway later.
+ _dereq_(3);
+ }
+ var Promise$1 = Promise;
+
+ function executeCallback(promise, callback) {
+ if (callback) {
+ promise.then(function (result) {
+ callback(null, result);
+ }, function (error) {
+ callback(error);
+ });
+ }
+ }
+
+ function executeTwoCallbacks(promise, callback, errorCallback) {
+ if (typeof callback === 'function') {
+ promise.then(callback);
+ }
+
+ if (typeof errorCallback === 'function') {
+ promise["catch"](errorCallback);
+ }
+ }
+
+ function normalizeKey(key) {
+ // Cast the key to a string, as that's all we can set as a key.
+ if (typeof key !== 'string') {
+ console.warn(key + ' used as a key, but it is not a string.');
+ key = String(key);
+ }
+
+ return key;
+ }
+
+ function getCallback() {
+ if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
+ return arguments[arguments.length - 1];
+ }
+ }
+
+ // Some code originally from async_storage.js in
+ // [Gaia](https://github.com/mozilla-b2g/gaia).
+
+ var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support';
+ var supportsBlobs = void 0;
+ var dbContexts = {};
+ var toString = Object.prototype.toString;
+
+ // Transaction Modes
+ var READ_ONLY = 'readonly';
+ var READ_WRITE = 'readwrite';
+
+ // Transform a binary string to an array buffer, because otherwise
+ // weird stuff happens when you try to work with the binary string directly.
+ // It is known.
+ // From http://stackoverflow.com/questions/14967647/ (continues on next line)
+ // encode-decode-image-with-base64-breaks-image (2013-04-21)
+ function _binStringToArrayBuffer(bin) {
+ var length = bin.length;
+ var buf = new ArrayBuffer(length);
+ var arr = new Uint8Array(buf);
+ for (var i = 0; i < length; i++) {
+ arr[i] = bin.charCodeAt(i);
+ }
+ return buf;
+ }
+
+ //
+ // Blobs are not supported in all versions of IndexedDB, notably
+ // Chrome <37 and Android <5. In those versions, storing a blob will throw.
+ //
+ // Various other blob bugs exist in Chrome v37-42 (inclusive).
+ // Detecting them is expensive and confusing to users, and Chrome 37-42
+ // is at very low usage worldwide, so we do a hacky userAgent check instead.
+ //
+ // content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120
+ // 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
+ // FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
+ //
+ // Code borrowed from PouchDB. See:
+ // https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js
+ //
+ function _checkBlobSupportWithoutCaching(idb) {
+ return new Promise$1(function (resolve) {
+ var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
+ var blob = createBlob(['']);
+ txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');
+
+ txn.onabort = function (e) {
+ // If the transaction aborts now its due to not being able to
+ // write to the database, likely due to the disk being full
+ e.preventDefault();
+ e.stopPropagation();
+ resolve(false);
+ };
+
+ txn.oncomplete = function () {
+ var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
+ var matchedEdge = navigator.userAgent.match(/Edge\//);
+ // MS Edge pretends to be Chrome 42:
+ // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx
+ resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);
+ };
+ })["catch"](function () {
+ return false; // error, so assume unsupported
+ });
+ }
+
+ function _checkBlobSupport(idb) {
+ if (typeof supportsBlobs === 'boolean') {
+ return Promise$1.resolve(supportsBlobs);
+ }
+ return _checkBlobSupportWithoutCaching(idb).then(function (value) {
+ supportsBlobs = value;
+ return supportsBlobs;
+ });
+ }
+
+ function _deferReadiness(dbInfo) {
+ var dbContext = dbContexts[dbInfo.name];
+
+ // Create a deferred object representing the current database operation.
+ var deferredOperation = {};
+
+ deferredOperation.promise = new Promise$1(function (resolve, reject) {
+ deferredOperation.resolve = resolve;
+ deferredOperation.reject = reject;
+ });
+
+ // Enqueue the deferred operation.
+ dbContext.deferredOperations.push(deferredOperation);
+
+ // Chain its promise to the database readiness.
+ if (!dbContext.dbReady) {
+ dbContext.dbReady = deferredOperation.promise;
+ } else {
+ dbContext.dbReady = dbContext.dbReady.then(function () {
+ return deferredOperation.promise;
+ });
+ }
+ }
+
+ function _advanceReadiness(dbInfo) {
+ var dbContext = dbContexts[dbInfo.name];
+
+ // Dequeue a deferred operation.
+ var deferredOperation = dbContext.deferredOperations.pop();
+
+ // Resolve its promise (which is part of the database readiness
+ // chain of promises).
+ if (deferredOperation) {
+ deferredOperation.resolve();
+ return deferredOperation.promise;
+ }
+ }
+
+ function _rejectReadiness(dbInfo, err) {
+ var dbContext = dbContexts[dbInfo.name];
+
+ // Dequeue a deferred operation.
+ var deferredOperation = dbContext.deferredOperations.pop();
+
+ // Reject its promise (which is part of the database readiness
+ // chain of promises).
+ if (deferredOperation) {
+ deferredOperation.reject(err);
+ return deferredOperation.promise;
+ }
+ }
+
+ function _getConnection(dbInfo, upgradeNeeded) {
+ return new Promise$1(function (resolve, reject) {
+ dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();
+
+ if (dbInfo.db) {
+ if (upgradeNeeded) {
+ _deferReadiness(dbInfo);
+ dbInfo.db.close();
+ } else {
+ return resolve(dbInfo.db);
+ }
+ }
+
+ var dbArgs = [dbInfo.name];
+
+ if (upgradeNeeded) {
+ dbArgs.push(dbInfo.version);
+ }
+
+ var openreq = idb.open.apply(idb, dbArgs);
+
+ if (upgradeNeeded) {
+ openreq.onupgradeneeded = function (e) {
+ var db = openreq.result;
+ try {
+ db.createObjectStore(dbInfo.storeName);
+ if (e.oldVersion <= 1) {
+ // Added when support for blob shims was added
+ db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
+ }
+ } catch (ex) {
+ if (ex.name === 'ConstraintError') {
+ console.warn('The database "' + dbInfo.name + '"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.');
+ } else {
+ throw ex;
+ }
+ }
+ };
+ }
+
+ openreq.onerror = function (e) {
+ e.preventDefault();
+ reject(openreq.error);
+ };
+
+ openreq.onsuccess = function () {
+ var db = openreq.result;
+ db.onversionchange = function (e) {
+ // Triggered when the database is modified (e.g. adding an objectStore) or
+ // deleted (even when initiated by other sessions in different tabs).
+ // Closing the connection here prevents those operations from being blocked.
+ // If the database is accessed again later by this instance, the connection
+ // will be reopened or the database recreated as needed.
+ e.target.close();
+ };
+ resolve(db);
+ _advanceReadiness(dbInfo);
+ };
+ });
+ }
+
+ function _getOriginalConnection(dbInfo) {
+ return _getConnection(dbInfo, false);
+ }
+
+ function _getUpgradedConnection(dbInfo) {
+ return _getConnection(dbInfo, true);
+ }
+
+ function _isUpgradeNeeded(dbInfo, defaultVersion) {
+ if (!dbInfo.db) {
+ return true;
+ }
+
+ var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
+ var isDowngrade = dbInfo.version < dbInfo.db.version;
+ var isUpgrade = dbInfo.version > dbInfo.db.version;
+
+ if (isDowngrade) {
+ // If the version is not the default one
+ // then warn for impossible downgrade.
+ if (dbInfo.version !== defaultVersion) {
+ console.warn('The database "' + dbInfo.name + '"' + " can't be downgraded from version " + dbInfo.db.version + ' to version ' + dbInfo.version + '.');
+ }
+ // Align the versions to prevent errors.
+ dbInfo.version = dbInfo.db.version;
+ }
+
+ if (isUpgrade || isNewStore) {
+ // If the store is new then increment the version (if needed).
+ // This will trigger an "upgradeneeded" event which is required
+ // for creating a store.
+ if (isNewStore) {
+ var incVersion = dbInfo.db.version + 1;
+ if (incVersion > dbInfo.version) {
+ dbInfo.version = incVersion;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // encode a blob for indexeddb engines that don't support blobs
+ function _encodeBlob(blob) {
+ return new Promise$1(function (resolve, reject) {
+ var reader = new FileReader();
+ reader.onerror = reject;
+ reader.onloadend = function (e) {
+ var base64 = btoa(e.target.result || '');
+ resolve({
+ __local_forage_encoded_blob: true,
+ data: base64,
+ type: blob.type
+ });
+ };
+ reader.readAsBinaryString(blob);
+ });
+ }
+
+ // decode an encoded blob
+ function _decodeBlob(encodedBlob) {
+ var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));
+ return createBlob([arrayBuff], { type: encodedBlob.type });
+ }
+
+ // is this one of our fancy encoded blobs?
+ function _isEncodedBlob(value) {
+ return value && value.__local_forage_encoded_blob;
+ }
+
+ // Specialize the default `ready()` function by making it dependent
+ // on the current database operations. Thus, the driver will be actually
+ // ready when it's been initialized (default) *and* there are no pending
+ // operations on the database (initiated by some other instances).
+ function _fullyReady(callback) {
+ var self = this;
+
+ var promise = self._initReady().then(function () {
+ var dbContext = dbContexts[self._dbInfo.name];
+
+ if (dbContext && dbContext.dbReady) {
+ return dbContext.dbReady;
+ }
+ });
+
+ executeTwoCallbacks(promise, callback, callback);
+ return promise;
+ }
+
+ // Try to establish a new db connection to replace the
+ // current one which is broken (i.e. experiencing
+ // InvalidStateError while creating a transaction).
+ function _tryReconnect(dbInfo) {
+ _deferReadiness(dbInfo);
+
+ var dbContext = dbContexts[dbInfo.name];
+ var forages = dbContext.forages;
+
+ for (var i = 0; i < forages.length; i++) {
+ var forage = forages[i];
+ if (forage._dbInfo.db) {
+ forage._dbInfo.db.close();
+ forage._dbInfo.db = null;
+ }
+ }
+ dbInfo.db = null;
+
+ return _getOriginalConnection(dbInfo).then(function (db) {
+ dbInfo.db = db;
+ if (_isUpgradeNeeded(dbInfo)) {
+ // Reopen the database for upgrading.
+ return _getUpgradedConnection(dbInfo);
+ }
+ return db;
+ }).then(function (db) {
+ // store the latest db reference
+ // in case the db was upgraded
+ dbInfo.db = dbContext.db = db;
+ for (var i = 0; i < forages.length; i++) {
+ forages[i]._dbInfo.db = db;
+ }
+ })["catch"](function (err) {
+ _rejectReadiness(dbInfo, err);
+ throw err;
+ });
+ }
+
+ // FF doesn't like Promises (micro-tasks) and IDDB store operations,
+ // so we have to do it with callbacks
+ function createTransaction(dbInfo, mode, callback, retries) {
+ if (retries === undefined) {
+ retries = 1;
+ }
+
+ try {
+ var tx = dbInfo.db.transaction(dbInfo.storeName, mode);
+ callback(null, tx);
+ } catch (err) {
+ if (retries > 0 && (!dbInfo.db || err.name === 'InvalidStateError' || err.name === 'NotFoundError')) {
+ return Promise$1.resolve().then(function () {
+ if (!dbInfo.db || err.name === 'NotFoundError' && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {
+ // increase the db version, to create the new ObjectStore
+ if (dbInfo.db) {
+ dbInfo.version = dbInfo.db.version + 1;
+ }
+ // Reopen the database for upgrading.
+ return _getUpgradedConnection(dbInfo);
+ }
+ }).then(function () {
+ return _tryReconnect(dbInfo).then(function () {
+ createTransaction(dbInfo, mode, callback, retries - 1);
+ });
+ })["catch"](callback);
+ }
+
+ callback(err);
+ }
+ }
+
+ function createDbContext() {
+ return {
+ // Running localForages sharing a database.
+ forages: [],
+ // Shared database.
+ db: null,
+ // Database readiness (promise).
+ dbReady: null,
+ // Deferred operations on the database.
+ deferredOperations: []
+ };
+ }
+
+ // Open the IndexedDB database (automatically creates one if one didn't
+ // previously exist), using any options set in the config.
+ function _initStorage(options) {
+ var self = this;
+ var dbInfo = {
+ db: null
+ };
+
+ if (options) {
+ for (var i in options) {
+ dbInfo[i] = options[i];
+ }
+ }
+
+ // Get the current context of the database;
+ var dbContext = dbContexts[dbInfo.name];
+
+ // ...or create a new context.
+ if (!dbContext) {
+ dbContext = createDbContext();
+ // Register the new context in the global container.
+ dbContexts[dbInfo.name] = dbContext;
+ }
+
+ // Register itself as a running localForage in the current context.
+ dbContext.forages.push(self);
+
+ // Replace the default `ready()` function with the specialized one.
+ if (!self._initReady) {
+ self._initReady = self.ready;
+ self.ready = _fullyReady;
+ }
+
+ // Create an array of initialization states of the related localForages.
+ var initPromises = [];
+
+ function ignoreErrors() {
+ // Don't handle errors here,
+ // just makes sure related localForages aren't pending.
+ return Promise$1.resolve();
+ }
+
+ for (var j = 0; j < dbContext.forages.length; j++) {
+ var forage = dbContext.forages[j];
+ if (forage !== self) {
+ // Don't wait for itself...
+ initPromises.push(forage._initReady()["catch"](ignoreErrors));
+ }
+ }
+
+ // Take a snapshot of the related localForages.
+ var forages = dbContext.forages.slice(0);
+
+ // Initialize the connection process only when
+ // all the related localForages aren't pending.
+ return Promise$1.all(initPromises).then(function () {
+ dbInfo.db = dbContext.db;
+ // Get the connection or open a new one without upgrade.
+ return _getOriginalConnection(dbInfo);
+ }).then(function (db) {
+ dbInfo.db = db;
+ if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {
+ // Reopen the database for upgrading.
+ return _getUpgradedConnection(dbInfo);
+ }
+ return db;
+ }).then(function (db) {
+ dbInfo.db = dbContext.db = db;
+ self._dbInfo = dbInfo;
+ // Share the final connection amongst related localForages.
+ for (var k = 0; k < forages.length; k++) {
+ var forage = forages[k];
+ if (forage !== self) {
+ // Self is already up-to-date.
+ forage._dbInfo.db = dbInfo.db;
+ forage._dbInfo.version = dbInfo.version;
+ }
+ }
+ });
+ }
+
+ function getItem(key, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ var req = store.get(key);
+
+ req.onsuccess = function () {
+ var value = req.result;
+ if (value === undefined) {
+ value = null;
+ }
+ if (_isEncodedBlob(value)) {
+ value = _decodeBlob(value);
+ }
+ resolve(value);
+ };
+
+ req.onerror = function () {
+ reject(req.error);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Iterate over all items stored in database.
+ function iterate(iterator, callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ var req = store.openCursor();
+ var iterationNumber = 1;
+
+ req.onsuccess = function () {
+ var cursor = req.result;
+
+ if (cursor) {
+ var value = cursor.value;
+ if (_isEncodedBlob(value)) {
+ value = _decodeBlob(value);
+ }
+ var result = iterator(value, cursor.key, iterationNumber++);
+
+ // when the iterator callback returns any
+ // (non-`undefined`) value, then we stop
+ // the iteration immediately
+ if (result !== void 0) {
+ resolve(result);
+ } else {
+ cursor["continue"]();
+ }
+ } else {
+ resolve();
+ }
+ };
+
+ req.onerror = function () {
+ reject(req.error);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+
+ return promise;
+ }
+
+ function setItem(key, value, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = new Promise$1(function (resolve, reject) {
+ var dbInfo;
+ self.ready().then(function () {
+ dbInfo = self._dbInfo;
+ if (toString.call(value) === '[object Blob]') {
+ return _checkBlobSupport(dbInfo.db).then(function (blobSupport) {
+ if (blobSupport) {
+ return value;
+ }
+ return _encodeBlob(value);
+ });
+ }
+ return value;
+ }).then(function (value) {
+ createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+
+ // The reason we don't _save_ null is because IE 10 does
+ // not support saving the `null` type in IndexedDB. How
+ // ironic, given the bug below!
+ // See: https://github.com/mozilla/localForage/issues/161
+ if (value === null) {
+ value = undefined;
+ }
+
+ var req = store.put(value, key);
+
+ transaction.oncomplete = function () {
+ // Cast to undefined so the value passed to
+ // callback/promise is the same as what one would get out
+ // of `getItem()` later. This leads to some weirdness
+ // (setItem('foo', undefined) will return `null`), but
+ // it's not my fault localStorage is our baseline and that
+ // it's weird.
+ if (value === undefined) {
+ value = null;
+ }
+
+ resolve(value);
+ };
+ transaction.onabort = transaction.onerror = function () {
+ var err = req.error ? req.error : req.transaction.error;
+ reject(err);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function removeItem(key, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ // We use a Grunt task to make this safe for IE and some
+ // versions of Android (including those used by Cordova).
+ // Normally IE won't like `.delete()` and will insist on
+ // using `['delete']()`, but we have a build step that
+ // fixes this for us now.
+ var req = store["delete"](key);
+ transaction.oncomplete = function () {
+ resolve();
+ };
+
+ transaction.onerror = function () {
+ reject(req.error);
+ };
+
+ // The request will be also be aborted if we've exceeded our storage
+ // space.
+ transaction.onabort = function () {
+ var err = req.error ? req.error : req.transaction.error;
+ reject(err);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function clear(callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ var req = store.clear();
+
+ transaction.oncomplete = function () {
+ resolve();
+ };
+
+ transaction.onabort = transaction.onerror = function () {
+ var err = req.error ? req.error : req.transaction.error;
+ reject(err);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function length(callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ var req = store.count();
+
+ req.onsuccess = function () {
+ resolve(req.result);
+ };
+
+ req.onerror = function () {
+ reject(req.error);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function key(n, callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ if (n < 0) {
+ resolve(null);
+
+ return;
+ }
+
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ var advanced = false;
+ var req = store.openKeyCursor();
+
+ req.onsuccess = function () {
+ var cursor = req.result;
+ if (!cursor) {
+ // this means there weren't enough keys
+ resolve(null);
+
+ return;
+ }
+
+ if (n === 0) {
+ // We have the first key, return it if that's what they
+ // wanted.
+ resolve(cursor.key);
+ } else {
+ if (!advanced) {
+ // Otherwise, ask the cursor to skip ahead n
+ // records.
+ advanced = true;
+ cursor.advance(n);
+ } else {
+ // When we get here, we've got the nth key.
+ resolve(cursor.key);
+ }
+ }
+ };
+
+ req.onerror = function () {
+ reject(req.error);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function keys(callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
+ if (err) {
+ return reject(err);
+ }
+
+ try {
+ var store = transaction.objectStore(self._dbInfo.storeName);
+ var req = store.openKeyCursor();
+ var keys = [];
+
+ req.onsuccess = function () {
+ var cursor = req.result;
+
+ if (!cursor) {
+ resolve(keys);
+ return;
+ }
+
+ keys.push(cursor.key);
+ cursor["continue"]();
+ };
+
+ req.onerror = function () {
+ reject(req.error);
+ };
+ } catch (e) {
+ reject(e);
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function dropInstance(options, callback) {
+ callback = getCallback.apply(this, arguments);
+
+ var currentConfig = this.config();
+ options = typeof options !== 'function' && options || {};
+ if (!options.name) {
+ options.name = options.name || currentConfig.name;
+ options.storeName = options.storeName || currentConfig.storeName;
+ }
+
+ var self = this;
+ var promise;
+ if (!options.name) {
+ promise = Promise$1.reject('Invalid arguments');
+ } else {
+ var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db;
+
+ var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function (db) {
+ var dbContext = dbContexts[options.name];
+ var forages = dbContext.forages;
+ dbContext.db = db;
+ for (var i = 0; i < forages.length; i++) {
+ forages[i]._dbInfo.db = db;
+ }
+ return db;
+ });
+
+ if (!options.storeName) {
+ promise = dbPromise.then(function (db) {
+ _deferReadiness(options);
+
+ var dbContext = dbContexts[options.name];
+ var forages = dbContext.forages;
+
+ db.close();
+ for (var i = 0; i < forages.length; i++) {
+ var forage = forages[i];
+ forage._dbInfo.db = null;
+ }
+
+ var dropDBPromise = new Promise$1(function (resolve, reject) {
+ var req = idb.deleteDatabase(options.name);
+
+ req.onerror = function () {
+ var db = req.result;
+ if (db) {
+ db.close();
+ }
+ reject(req.error);
+ };
+
+ req.onblocked = function () {
+ // Closing all open connections in onversionchange handler should prevent this situation, but if
+ // we do get here, it just means the request remains pending - eventually it will succeed or error
+ console.warn('dropInstance blocked for database "' + options.name + '" until all open connections are closed');
+ };
+
+ req.onsuccess = function () {
+ var db = req.result;
+ if (db) {
+ db.close();
+ }
+ resolve(db);
+ };
+ });
+
+ return dropDBPromise.then(function (db) {
+ dbContext.db = db;
+ for (var i = 0; i < forages.length; i++) {
+ var _forage = forages[i];
+ _advanceReadiness(_forage._dbInfo);
+ }
+ })["catch"](function (err) {
+ (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
+ throw err;
+ });
+ });
+ } else {
+ promise = dbPromise.then(function (db) {
+ if (!db.objectStoreNames.contains(options.storeName)) {
+ return;
+ }
+
+ var newVersion = db.version + 1;
+
+ _deferReadiness(options);
+
+ var dbContext = dbContexts[options.name];
+ var forages = dbContext.forages;
+
+ db.close();
+ for (var i = 0; i < forages.length; i++) {
+ var forage = forages[i];
+ forage._dbInfo.db = null;
+ forage._dbInfo.version = newVersion;
+ }
+
+ var dropObjectPromise = new Promise$1(function (resolve, reject) {
+ var req = idb.open(options.name, newVersion);
+
+ req.onerror = function (err) {
+ var db = req.result;
+ db.close();
+ reject(err);
+ };
+
+ req.onupgradeneeded = function () {
+ var db = req.result;
+ db.deleteObjectStore(options.storeName);
+ };
+
+ req.onsuccess = function () {
+ var db = req.result;
+ db.close();
+ resolve(db);
+ };
+ });
+
+ return dropObjectPromise.then(function (db) {
+ dbContext.db = db;
+ for (var j = 0; j < forages.length; j++) {
+ var _forage2 = forages[j];
+ _forage2._dbInfo.db = db;
+ _advanceReadiness(_forage2._dbInfo);
+ }
+ })["catch"](function (err) {
+ (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
+ throw err;
+ });
+ });
+ }
+ }
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ var asyncStorage = {
+ _driver: 'asyncStorage',
+ _initStorage: _initStorage,
+ _support: isIndexedDBValid(),
+ iterate: iterate,
+ getItem: getItem,
+ setItem: setItem,
+ removeItem: removeItem,
+ clear: clear,
+ length: length,
+ key: key,
+ keys: keys,
+ dropInstance: dropInstance
+ };
+
+ function isWebSQLValid() {
+ return typeof openDatabase === 'function';
+ }
+
+ // Sadly, the best way to save binary data in WebSQL/localStorage is serializing
+ // it to Base64, so this is how we store it to prevent very strange errors with less
+ // verbose ways of binary <-> string data storage.
+ var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+ var BLOB_TYPE_PREFIX = '~~local_forage_type~';
+ var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;
+
+ var SERIALIZED_MARKER = '__lfsc__:';
+ var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;
+
+ // OMG the serializations!
+ var TYPE_ARRAYBUFFER = 'arbf';
+ var TYPE_BLOB = 'blob';
+ var TYPE_INT8ARRAY = 'si08';
+ var TYPE_UINT8ARRAY = 'ui08';
+ var TYPE_UINT8CLAMPEDARRAY = 'uic8';
+ var TYPE_INT16ARRAY = 'si16';
+ var TYPE_INT32ARRAY = 'si32';
+ var TYPE_UINT16ARRAY = 'ur16';
+ var TYPE_UINT32ARRAY = 'ui32';
+ var TYPE_FLOAT32ARRAY = 'fl32';
+ var TYPE_FLOAT64ARRAY = 'fl64';
+ var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;
+
+ var toString$1 = Object.prototype.toString;
+
+ function stringToBuffer(serializedString) {
+ // Fill the string into a ArrayBuffer.
+ var bufferLength = serializedString.length * 0.75;
+ var len = serializedString.length;
+ var i;
+ var p = 0;
+ var encoded1, encoded2, encoded3, encoded4;
+
+ if (serializedString[serializedString.length - 1] === '=') {
+ bufferLength--;
+ if (serializedString[serializedString.length - 2] === '=') {
+ bufferLength--;
+ }
+ }
+
+ var buffer = new ArrayBuffer(bufferLength);
+ var bytes = new Uint8Array(buffer);
+
+ for (i = 0; i < len; i += 4) {
+ encoded1 = BASE_CHARS.indexOf(serializedString[i]);
+ encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);
+ encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);
+ encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);
+
+ /*jslint bitwise: true */
+ bytes[p++] = encoded1 << 2 | encoded2 >> 4;
+ bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
+ bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
+ }
+ return buffer;
+ }
+
+ // Converts a buffer to a string to store, serialized, in the backend
+ // storage library.
+ function bufferToString(buffer) {
+ // base64-arraybuffer
+ var bytes = new Uint8Array(buffer);
+ var base64String = '';
+ var i;
+
+ for (i = 0; i < bytes.length; i += 3) {
+ /*jslint bitwise: true */
+ base64String += BASE_CHARS[bytes[i] >> 2];
+ base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
+ base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
+ base64String += BASE_CHARS[bytes[i + 2] & 63];
+ }
+
+ if (bytes.length % 3 === 2) {
+ base64String = base64String.substring(0, base64String.length - 1) + '=';
+ } else if (bytes.length % 3 === 1) {
+ base64String = base64String.substring(0, base64String.length - 2) + '==';
+ }
+
+ return base64String;
+ }
+
+ // Serialize a value, afterwards executing a callback (which usually
+ // instructs the `setItem()` callback/promise to be executed). This is how
+ // we store binary data with localStorage.
+ function serialize(value, callback) {
+ var valueType = '';
+ if (value) {
+ valueType = toString$1.call(value);
+ }
+
+ // Cannot use `value instanceof ArrayBuffer` or such here, as these
+ // checks fail when running the tests using casper.js...
+ //
+ // TODO: See why those tests fail and use a better solution.
+ if (value && (valueType === '[object ArrayBuffer]' || value.buffer && toString$1.call(value.buffer) === '[object ArrayBuffer]')) {
+ // Convert binary arrays to a string and prefix the string with
+ // a special marker.
+ var buffer;
+ var marker = SERIALIZED_MARKER;
+
+ if (value instanceof ArrayBuffer) {
+ buffer = value;
+ marker += TYPE_ARRAYBUFFER;
+ } else {
+ buffer = value.buffer;
+
+ if (valueType === '[object Int8Array]') {
+ marker += TYPE_INT8ARRAY;
+ } else if (valueType === '[object Uint8Array]') {
+ marker += TYPE_UINT8ARRAY;
+ } else if (valueType === '[object Uint8ClampedArray]') {
+ marker += TYPE_UINT8CLAMPEDARRAY;
+ } else if (valueType === '[object Int16Array]') {
+ marker += TYPE_INT16ARRAY;
+ } else if (valueType === '[object Uint16Array]') {
+ marker += TYPE_UINT16ARRAY;
+ } else if (valueType === '[object Int32Array]') {
+ marker += TYPE_INT32ARRAY;
+ } else if (valueType === '[object Uint32Array]') {
+ marker += TYPE_UINT32ARRAY;
+ } else if (valueType === '[object Float32Array]') {
+ marker += TYPE_FLOAT32ARRAY;
+ } else if (valueType === '[object Float64Array]') {
+ marker += TYPE_FLOAT64ARRAY;
+ } else {
+ callback(new Error('Failed to get type for BinaryArray'));
+ }
+ }
+
+ callback(marker + bufferToString(buffer));
+ } else if (valueType === '[object Blob]') {
+ // Conver the blob to a binaryArray and then to a string.
+ var fileReader = new FileReader();
+
+ fileReader.onload = function () {
+ // Backwards-compatible prefix for the blob type.
+ var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result);
+
+ callback(SERIALIZED_MARKER + TYPE_BLOB + str);
+ };
+
+ fileReader.readAsArrayBuffer(value);
+ } else {
+ try {
+ callback(JSON.stringify(value));
+ } catch (e) {
+ console.error("Couldn't convert value into a JSON string: ", value);
+
+ callback(null, e);
+ }
+ }
+ }
+
+ // Deserialize data we've inserted into a value column/field. We place
+ // special markers into our strings to mark them as encoded; this isn't
+ // as nice as a meta field, but it's the only sane thing we can do whilst
+ // keeping localStorage support intact.
+ //
+ // Oftentimes this will just deserialize JSON content, but if we have a
+ // special marker (SERIALIZED_MARKER, defined above), we will extract
+ // some kind of arraybuffer/binary data/typed array out of the string.
+ function deserialize(value) {
+ // If we haven't marked this string as being specially serialized (i.e.
+ // something other than serialized JSON), we can just return it and be
+ // done with it.
+ if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {
+ return JSON.parse(value);
+ }
+
+ // The following code deals with deserializing some kind of Blob or
+ // TypedArray. First we separate out the type of data we're dealing
+ // with from the data itself.
+ var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
+ var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);
+
+ var blobType;
+ // Backwards-compatible blob type serialization strategy.
+ // DBs created with older versions of localForage will simply not have the blob type.
+ if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {
+ var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);
+ blobType = matcher[1];
+ serializedString = serializedString.substring(matcher[0].length);
+ }
+ var buffer = stringToBuffer(serializedString);
+
+ // Return the right type based on the code/type set during
+ // serialization.
+ switch (type) {
+ case TYPE_ARRAYBUFFER:
+ return buffer;
+ case TYPE_BLOB:
+ return createBlob([buffer], { type: blobType });
+ case TYPE_INT8ARRAY:
+ return new Int8Array(buffer);
+ case TYPE_UINT8ARRAY:
+ return new Uint8Array(buffer);
+ case TYPE_UINT8CLAMPEDARRAY:
+ return new Uint8ClampedArray(buffer);
+ case TYPE_INT16ARRAY:
+ return new Int16Array(buffer);
+ case TYPE_UINT16ARRAY:
+ return new Uint16Array(buffer);
+ case TYPE_INT32ARRAY:
+ return new Int32Array(buffer);
+ case TYPE_UINT32ARRAY:
+ return new Uint32Array(buffer);
+ case TYPE_FLOAT32ARRAY:
+ return new Float32Array(buffer);
+ case TYPE_FLOAT64ARRAY:
+ return new Float64Array(buffer);
+ default:
+ throw new Error('Unkown type: ' + type);
+ }
+ }
+
+ var localforageSerializer = {
+ serialize: serialize,
+ deserialize: deserialize,
+ stringToBuffer: stringToBuffer,
+ bufferToString: bufferToString
+ };
+
+ /*
+ * Includes code from:
+ *
+ * base64-arraybuffer
+ * https://github.com/niklasvh/base64-arraybuffer
+ *
+ * Copyright (c) 2012 Niklas von Hertzen
+ * Licensed under the MIT license.
+ */
+
+ function createDbTable(t, dbInfo, callback, errorCallback) {
+ t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' ' + '(id INTEGER PRIMARY KEY, key unique, value)', [], callback, errorCallback);
+ }
+
+ // Open the WebSQL database (automatically creates one if one didn't
+ // previously exist), using any options set in the config.
+ function _initStorage$1(options) {
+ var self = this;
+ var dbInfo = {
+ db: null
+ };
+
+ if (options) {
+ for (var i in options) {
+ dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i];
+ }
+ }
+
+ var dbInfoPromise = new Promise$1(function (resolve, reject) {
+ // Open the database; the openDatabase API will automatically
+ // create it for us if it doesn't exist.
+ try {
+ dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);
+ } catch (e) {
+ return reject(e);
+ }
+
+ // Create our key/value table if it doesn't exist.
+ dbInfo.db.transaction(function (t) {
+ createDbTable(t, dbInfo, function () {
+ self._dbInfo = dbInfo;
+ resolve();
+ }, function (t, error) {
+ reject(error);
+ });
+ }, reject);
+ });
+
+ dbInfo.serializer = localforageSerializer;
+ return dbInfoPromise;
+ }
+
+ function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
+ t.executeSql(sqlStatement, args, callback, function (t, error) {
+ if (error.code === error.SYNTAX_ERR) {
+ t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name = ?", [dbInfo.storeName], function (t, results) {
+ if (!results.rows.length) {
+ // if the table is missing (was deleted)
+ // re-create it table and retry
+ createDbTable(t, dbInfo, function () {
+ t.executeSql(sqlStatement, args, callback, errorCallback);
+ }, errorCallback);
+ } else {
+ errorCallback(t, error);
+ }
+ }, errorCallback);
+ } else {
+ errorCallback(t, error);
+ }
+ }, errorCallback);
+ }
+
+ function getItem$1(key, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) {
+ var result = results.rows.length ? results.rows.item(0).value : null;
+
+ // Check to see if this is serialized content we need to
+ // unpack.
+ if (result) {
+ result = dbInfo.serializer.deserialize(result);
+ }
+
+ resolve(result);
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function iterate$1(iterator, callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName, [], function (t, results) {
+ var rows = results.rows;
+ var length = rows.length;
+
+ for (var i = 0; i < length; i++) {
+ var item = rows.item(i);
+ var result = item.value;
+
+ // Check to see if this is serialized content
+ // we need to unpack.
+ if (result) {
+ result = dbInfo.serializer.deserialize(result);
+ }
+
+ result = iterator(result, item.key, i + 1);
+
+ // void(0) prevents problems with redefinition
+ // of `undefined`.
+ if (result !== void 0) {
+ resolve(result);
+ return;
+ }
+ }
+
+ resolve();
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function _setItem(key, value, callback, retriesLeft) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ // The localStorage API doesn't return undefined values in an
+ // "expected" way, so undefined is always cast to null in all
+ // drivers. See: https://github.com/mozilla/localForage/pull/42
+ if (value === undefined) {
+ value = null;
+ }
+
+ // Save the original value to pass to the callback.
+ var originalValue = value;
+
+ var dbInfo = self._dbInfo;
+ dbInfo.serializer.serialize(value, function (value, error) {
+ if (error) {
+ reject(error);
+ } else {
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'INSERT OR REPLACE INTO ' + dbInfo.storeName + ' ' + '(key, value) VALUES (?, ?)', [key, value], function () {
+ resolve(originalValue);
+ }, function (t, error) {
+ reject(error);
+ });
+ }, function (sqlError) {
+ // The transaction failed; check
+ // to see if it's a quota error.
+ if (sqlError.code === sqlError.QUOTA_ERR) {
+ // We reject the callback outright for now, but
+ // it's worth trying to re-run the transaction.
+ // Even if the user accepts the prompt to use
+ // more storage on Safari, this error will
+ // be called.
+ //
+ // Try to re-run the transaction.
+ if (retriesLeft > 0) {
+ resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
+ return;
+ }
+ reject(sqlError);
+ }
+ });
+ }
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function setItem$1(key, value, callback) {
+ return _setItem.apply(this, [key, value, callback, 1]);
+ }
+
+ function removeItem$1(key, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () {
+ resolve();
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Deletes every item in the table.
+ // TODO: Find out if this resets the AUTO_INCREMENT number.
+ function clear$1(callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName, [], function () {
+ resolve();
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Does a simple `COUNT(key)` to get the number of items stored in
+ // localForage.
+ function length$1(callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ dbInfo.db.transaction(function (t) {
+ // Ahhh, SQL makes this one soooooo easy.
+ tryExecuteSql(t, dbInfo, 'SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) {
+ var result = results.rows.item(0).c;
+ resolve(result);
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Return the key located at key index X; essentially gets the key from a
+ // `WHERE id = ?`. This is the most efficient way I can think to implement
+ // this rarely-used (in my experience) part of the API, but it can seem
+ // inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so
+ // the ID of each key will change every time it's updated. Perhaps a stored
+ // procedure for the `setItem()` SQL would solve this problem?
+ // TODO: Don't change ID on `setItem()`.
+ function key$1(n, callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) {
+ var result = results.rows.length ? results.rows.item(0).key : null;
+ resolve(result);
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function keys$1(callback) {
+ var self = this;
+
+ var promise = new Promise$1(function (resolve, reject) {
+ self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ dbInfo.db.transaction(function (t) {
+ tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName, [], function (t, results) {
+ var keys = [];
+
+ for (var i = 0; i < results.rows.length; i++) {
+ keys.push(results.rows.item(i).key);
+ }
+
+ resolve(keys);
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ })["catch"](reject);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // https://www.w3.org/TR/webdatabase/#databases
+ // > There is no way to enumerate or delete the databases available for an origin from this API.
+ function getAllStoreNames(db) {
+ return new Promise$1(function (resolve, reject) {
+ db.transaction(function (t) {
+ t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function (t, results) {
+ var storeNames = [];
+
+ for (var i = 0; i < results.rows.length; i++) {
+ storeNames.push(results.rows.item(i).name);
+ }
+
+ resolve({
+ db: db,
+ storeNames: storeNames
+ });
+ }, function (t, error) {
+ reject(error);
+ });
+ }, function (sqlError) {
+ reject(sqlError);
+ });
+ });
+ }
+
+ function dropInstance$1(options, callback) {
+ callback = getCallback.apply(this, arguments);
+
+ var currentConfig = this.config();
+ options = typeof options !== 'function' && options || {};
+ if (!options.name) {
+ options.name = options.name || currentConfig.name;
+ options.storeName = options.storeName || currentConfig.storeName;
+ }
+
+ var self = this;
+ var promise;
+ if (!options.name) {
+ promise = Promise$1.reject('Invalid arguments');
+ } else {
+ promise = new Promise$1(function (resolve) {
+ var db;
+ if (options.name === currentConfig.name) {
+ // use the db reference of the current instance
+ db = self._dbInfo.db;
+ } else {
+ db = openDatabase(options.name, '', '', 0);
+ }
+
+ if (!options.storeName) {
+ // drop all database tables
+ resolve(getAllStoreNames(db));
+ } else {
+ resolve({
+ db: db,
+ storeNames: [options.storeName]
+ });
+ }
+ }).then(function (operationInfo) {
+ return new Promise$1(function (resolve, reject) {
+ operationInfo.db.transaction(function (t) {
+ function dropTable(storeName) {
+ return new Promise$1(function (resolve, reject) {
+ t.executeSql('DROP TABLE IF EXISTS ' + storeName, [], function () {
+ resolve();
+ }, function (t, error) {
+ reject(error);
+ });
+ });
+ }
+
+ var operations = [];
+ for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) {
+ operations.push(dropTable(operationInfo.storeNames[i]));
+ }
+
+ Promise$1.all(operations).then(function () {
+ resolve();
+ })["catch"](function (e) {
+ reject(e);
+ });
+ }, function (sqlError) {
+ reject(sqlError);
+ });
+ });
+ });
+ }
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ var webSQLStorage = {
+ _driver: 'webSQLStorage',
+ _initStorage: _initStorage$1,
+ _support: isWebSQLValid(),
+ iterate: iterate$1,
+ getItem: getItem$1,
+ setItem: setItem$1,
+ removeItem: removeItem$1,
+ clear: clear$1,
+ length: length$1,
+ key: key$1,
+ keys: keys$1,
+ dropInstance: dropInstance$1
+ };
+
+ function isLocalStorageValid() {
+ try {
+ return typeof localStorage !== 'undefined' && 'setItem' in localStorage &&
+ // in IE8 typeof localStorage.setItem === 'object'
+ !!localStorage.setItem;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ function _getKeyPrefix(options, defaultConfig) {
+ var keyPrefix = options.name + '/';
+
+ if (options.storeName !== defaultConfig.storeName) {
+ keyPrefix += options.storeName + '/';
+ }
+ return keyPrefix;
+ }
+
+ // Check if localStorage throws when saving an item
+ function checkIfLocalStorageThrows() {
+ var localStorageTestKey = '_localforage_support_test';
+
+ try {
+ localStorage.setItem(localStorageTestKey, true);
+ localStorage.removeItem(localStorageTestKey);
+
+ return false;
+ } catch (e) {
+ return true;
+ }
+ }
+
+ // Check if localStorage is usable and allows to save an item
+ // This method checks if localStorage is usable in Safari Private Browsing
+ // mode, or in any other case where the available quota for localStorage
+ // is 0 and there wasn't any saved items yet.
+ function _isLocalStorageUsable() {
+ return !checkIfLocalStorageThrows() || localStorage.length > 0;
+ }
+
+ // Config the localStorage backend, using options set in the config.
+ function _initStorage$2(options) {
+ var self = this;
+ var dbInfo = {};
+ if (options) {
+ for (var i in options) {
+ dbInfo[i] = options[i];
+ }
+ }
+
+ dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);
+
+ if (!_isLocalStorageUsable()) {
+ return Promise$1.reject();
+ }
+
+ self._dbInfo = dbInfo;
+ dbInfo.serializer = localforageSerializer;
+
+ return Promise$1.resolve();
+ }
+
+ // Remove all keys from the datastore, effectively destroying all data in
+ // the app's key/value store!
+ function clear$2(callback) {
+ var self = this;
+ var promise = self.ready().then(function () {
+ var keyPrefix = self._dbInfo.keyPrefix;
+
+ for (var i = localStorage.length - 1; i >= 0; i--) {
+ var key = localStorage.key(i);
+
+ if (key.indexOf(keyPrefix) === 0) {
+ localStorage.removeItem(key);
+ }
+ }
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Retrieve an item from the store. Unlike the original async_storage
+ // library in Gaia, we don't modify return values at all. If a key's value
+ // is `undefined`, we pass that value to the callback function.
+ function getItem$2(key, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ var result = localStorage.getItem(dbInfo.keyPrefix + key);
+
+ // If a result was found, parse it from the serialized
+ // string into a JS object. If result isn't truthy, the key
+ // is likely undefined and we'll pass it straight to the
+ // callback.
+ if (result) {
+ result = dbInfo.serializer.deserialize(result);
+ }
+
+ return result;
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Iterate over all items in the store.
+ function iterate$2(iterator, callback) {
+ var self = this;
+
+ var promise = self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ var keyPrefix = dbInfo.keyPrefix;
+ var keyPrefixLength = keyPrefix.length;
+ var length = localStorage.length;
+
+ // We use a dedicated iterator instead of the `i` variable below
+ // so other keys we fetch in localStorage aren't counted in
+ // the `iterationNumber` argument passed to the `iterate()`
+ // callback.
+ //
+ // See: github.com/mozilla/localForage/pull/435#discussion_r38061530
+ var iterationNumber = 1;
+
+ for (var i = 0; i < length; i++) {
+ var key = localStorage.key(i);
+ if (key.indexOf(keyPrefix) !== 0) {
+ continue;
+ }
+ var value = localStorage.getItem(key);
+
+ // If a result was found, parse it from the serialized
+ // string into a JS object. If result isn't truthy, the
+ // key is likely undefined and we'll pass it straight
+ // to the iterator.
+ if (value) {
+ value = dbInfo.serializer.deserialize(value);
+ }
+
+ value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);
+
+ if (value !== void 0) {
+ return value;
+ }
+ }
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Same as localStorage's key() method, except takes a callback.
+ function key$2(n, callback) {
+ var self = this;
+ var promise = self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ var result;
+ try {
+ result = localStorage.key(n);
+ } catch (error) {
+ result = null;
+ }
+
+ // Remove the prefix from the key, if a key is found.
+ if (result) {
+ result = result.substring(dbInfo.keyPrefix.length);
+ }
+
+ return result;
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function keys$2(callback) {
+ var self = this;
+ var promise = self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ var length = localStorage.length;
+ var keys = [];
+
+ for (var i = 0; i < length; i++) {
+ var itemKey = localStorage.key(i);
+ if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {
+ keys.push(itemKey.substring(dbInfo.keyPrefix.length));
+ }
+ }
+
+ return keys;
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Supply the number of keys in the datastore to the callback function.
+ function length$2(callback) {
+ var self = this;
+ var promise = self.keys().then(function (keys) {
+ return keys.length;
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Remove an item from the store, nice and simple.
+ function removeItem$2(key, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = self.ready().then(function () {
+ var dbInfo = self._dbInfo;
+ localStorage.removeItem(dbInfo.keyPrefix + key);
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ // Set a key's value and run an optional callback once the value is set.
+ // Unlike Gaia's implementation, the callback function is passed the value,
+ // in case you want to operate on that value only after you're sure it
+ // saved, or something like that.
+ function setItem$2(key, value, callback) {
+ var self = this;
+
+ key = normalizeKey(key);
+
+ var promise = self.ready().then(function () {
+ // Convert undefined values to null.
+ // https://github.com/mozilla/localForage/pull/42
+ if (value === undefined) {
+ value = null;
+ }
+
+ // Save the original value to pass to the callback.
+ var originalValue = value;
+
+ return new Promise$1(function (resolve, reject) {
+ var dbInfo = self._dbInfo;
+ dbInfo.serializer.serialize(value, function (value, error) {
+ if (error) {
+ reject(error);
+ } else {
+ try {
+ localStorage.setItem(dbInfo.keyPrefix + key, value);
+ resolve(originalValue);
+ } catch (e) {
+ // localStorage capacity exceeded.
+ // TODO: Make this a specific error/event.
+ if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
+ reject(e);
+ }
+ reject(e);
+ }
+ }
+ });
+ });
+ });
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ function dropInstance$2(options, callback) {
+ callback = getCallback.apply(this, arguments);
+
+ options = typeof options !== 'function' && options || {};
+ if (!options.name) {
+ var currentConfig = this.config();
+ options.name = options.name || currentConfig.name;
+ options.storeName = options.storeName || currentConfig.storeName;
+ }
+
+ var self = this;
+ var promise;
+ if (!options.name) {
+ promise = Promise$1.reject('Invalid arguments');
+ } else {
+ promise = new Promise$1(function (resolve) {
+ if (!options.storeName) {
+ resolve(options.name + '/');
+ } else {
+ resolve(_getKeyPrefix(options, self._defaultConfig));
+ }
+ }).then(function (keyPrefix) {
+ for (var i = localStorage.length - 1; i >= 0; i--) {
+ var key = localStorage.key(i);
+
+ if (key.indexOf(keyPrefix) === 0) {
+ localStorage.removeItem(key);
+ }
+ }
+ });
+ }
+
+ executeCallback(promise, callback);
+ return promise;
+ }
+
+ var localStorageWrapper = {
+ _driver: 'localStorageWrapper',
+ _initStorage: _initStorage$2,
+ _support: isLocalStorageValid(),
+ iterate: iterate$2,
+ getItem: getItem$2,
+ setItem: setItem$2,
+ removeItem: removeItem$2,
+ clear: clear$2,
+ length: length$2,
+ key: key$2,
+ keys: keys$2,
+ dropInstance: dropInstance$2
+ };
+
+ var sameValue = function sameValue(x, y) {
+ return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y);
+ };
+
+ var includes = function includes(array, searchElement) {
+ var len = array.length;
+ var i = 0;
+ while (i < len) {
+ if (sameValue(array[i], searchElement)) {
+ return true;
+ }
+ i++;
+ }
+
+ return false;
+ };
+
+ var isArray = Array.isArray || function (arg) {
+ return Object.prototype.toString.call(arg) === '[object Array]';
+ };
+
+ // Drivers are stored here when `defineDriver()` is called.
+ // They are shared across all instances of localForage.
+ var DefinedDrivers = {};
+
+ var DriverSupport = {};
+
+ var DefaultDrivers = {
+ INDEXEDDB: asyncStorage,
+ WEBSQL: webSQLStorage,
+ LOCALSTORAGE: localStorageWrapper
+ };
+
+ var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver];
+
+ var OptionalDriverMethods = ['dropInstance'];
+
+ var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods);
+
+ var DefaultConfig = {
+ description: '',
+ driver: DefaultDriverOrder.slice(),
+ name: 'localforage',
+ // Default DB size is _JUST UNDER_ 5MB, as it's the highest size
+ // we can use without a prompt.
+ size: 4980736,
+ storeName: 'keyvaluepairs',
+ version: 1.0
+ };
+
+ function callWhenReady(localForageInstance, libraryMethod) {
+ localForageInstance[libraryMethod] = function () {
+ var _args = arguments;
+ return localForageInstance.ready().then(function () {
+ return localForageInstance[libraryMethod].apply(localForageInstance, _args);
+ });
+ };
+ }
+
+ function extend() {
+ for (var i = 1; i < arguments.length; i++) {
+ var arg = arguments[i];
+
+ if (arg) {
+ for (var _key in arg) {
+ if (arg.hasOwnProperty(_key)) {
+ if (isArray(arg[_key])) {
+ arguments[0][_key] = arg[_key].slice();
+ } else {
+ arguments[0][_key] = arg[_key];
+ }
+ }
+ }
+ }
+ }
+
+ return arguments[0];
+ }
+
+ var LocalForage = function () {
+ function LocalForage(options) {
+ _classCallCheck(this, LocalForage);
+
+ for (var driverTypeKey in DefaultDrivers) {
+ if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {
+ var driver = DefaultDrivers[driverTypeKey];
+ var driverName = driver._driver;
+ this[driverTypeKey] = driverName;
+
+ if (!DefinedDrivers[driverName]) {
+ // we don't need to wait for the promise,
+ // since the default drivers can be defined
+ // in a blocking manner
+ this.defineDriver(driver);
+ }
+ }
+ }
+
+ this._defaultConfig = extend({}, DefaultConfig);
+ this._config = extend({}, this._defaultConfig, options);
+ this._driverSet = null;
+ this._initDriver = null;
+ this._ready = false;
+ this._dbInfo = null;
+
+ this._wrapLibraryMethodsWithReady();
+ this.setDriver(this._config.driver)["catch"](function () {});
+ }
+
+ // Set any config values for localForage; can be called anytime before
+ // the first API call (e.g. `getItem`, `setItem`).
+ // We loop through options so we don't overwrite existing config
+ // values.
+
+
+ LocalForage.prototype.config = function config(options) {
+ // If the options argument is an object, we use it to set values.
+ // Otherwise, we return either a specified config value or all
+ // config values.
+ if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
+ // If localforage is ready and fully initialized, we can't set
+ // any new configuration values. Instead, we return an error.
+ if (this._ready) {
+ return new Error("Can't call config() after localforage " + 'has been used.');
+ }
+
+ for (var i in options) {
+ if (i === 'storeName') {
+ options[i] = options[i].replace(/\W/g, '_');
+ }
+
+ if (i === 'version' && typeof options[i] !== 'number') {
+ return new Error('Database version must be a number.');
+ }
+
+ this._config[i] = options[i];
+ }
+
+ // after all config options are set and
+ // the driver option is used, try setting it
+ if ('driver' in options && options.driver) {
+ return this.setDriver(this._config.driver);
+ }
+
+ return true;
+ } else if (typeof options === 'string') {
+ return this._config[options];
+ } else {
+ return this._config;
+ }
+ };
+
+ // Used to define a custom driver, shared across all instances of
+ // localForage.
+
+
+ LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {
+ var promise = new Promise$1(function (resolve, reject) {
+ try {
+ var driverName = driverObject._driver;
+ var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver');
+
+ // A driver name should be defined and not overlap with the
+ // library-defined, default drivers.
+ if (!driverObject._driver) {
+ reject(complianceError);
+ return;
+ }
+
+ var driverMethods = LibraryMethods.concat('_initStorage');
+ for (var i = 0, len = driverMethods.length; i < len; i++) {
+ var driverMethodName = driverMethods[i];
+
+ // when the property is there,
+ // it should be a method even when optional
+ var isRequired = !includes(OptionalDriverMethods, driverMethodName);
+ if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== 'function') {
+ reject(complianceError);
+ return;
+ }
+ }
+
+ var configureMissingMethods = function configureMissingMethods() {
+ var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {
+ return function () {
+ var error = new Error('Method ' + methodName + ' is not implemented by the current driver');
+ var promise = Promise$1.reject(error);
+ executeCallback(promise, arguments[arguments.length - 1]);
+ return promise;
+ };
+ };
+
+ for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {
+ var optionalDriverMethod = OptionalDriverMethods[_i];
+ if (!driverObject[optionalDriverMethod]) {
+ driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);
+ }
+ }
+ };
+
+ configureMissingMethods();
+
+ var setDriverSupport = function setDriverSupport(support) {
+ if (DefinedDrivers[driverName]) {
+ console.info('Redefining LocalForage driver: ' + driverName);
+ }
+ DefinedDrivers[driverName] = driverObject;
+ DriverSupport[driverName] = support;
+ // don't use a then, so that we can define
+ // drivers that have simple _support methods
+ // in a blocking manner
+ resolve();
+ };
+
+ if ('_support' in driverObject) {
+ if (driverObject._support && typeof driverObject._support === 'function') {
+ driverObject._support().then(setDriverSupport, reject);
+ } else {
+ setDriverSupport(!!driverObject._support);
+ }
+ } else {
+ setDriverSupport(true);
+ }
+ } catch (e) {
+ reject(e);
+ }
+ });
+
+ executeTwoCallbacks(promise, callback, errorCallback);
+ return promise;
+ };
+
+ LocalForage.prototype.driver = function driver() {
+ return this._driver || null;
+ };
+
+ LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {
+ var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error('Driver not found.'));
+
+ executeTwoCallbacks(getDriverPromise, callback, errorCallback);
+ return getDriverPromise;
+ };
+
+ LocalForage.prototype.getSerializer = function getSerializer(callback) {
+ var serializerPromise = Promise$1.resolve(localforageSerializer);
+ executeTwoCallbacks(serializerPromise, callback);
+ return serializerPromise;
+ };
+
+ LocalForage.prototype.ready = function ready(callback) {
+ var self = this;
+
+ var promise = self._driverSet.then(function () {
+ if (self._ready === null) {
+ self._ready = self._initDriver();
+ }
+
+ return self._ready;
+ });
+
+ executeTwoCallbacks(promise, callback, callback);
+ return promise;
+ };
+
+ LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {
+ var self = this;
+
+ if (!isArray(drivers)) {
+ drivers = [drivers];
+ }
+
+ var supportedDrivers = this._getSupportedDrivers(drivers);
+
+ function setDriverToConfig() {
+ self._config.driver = self.driver();
+ }
+
+ function extendSelfWithDriver(driver) {
+ self._extend(driver);
+ setDriverToConfig();
+
+ self._ready = self._initStorage(self._config);
+ return self._ready;
+ }
+
+ function initDriver(supportedDrivers) {
+ return function () {
+ var currentDriverIndex = 0;
+
+ function driverPromiseLoop() {
+ while (currentDriverIndex < supportedDrivers.length) {
+ var driverName = supportedDrivers[currentDriverIndex];
+ currentDriverIndex++;
+
+ self._dbInfo = null;
+ self._ready = null;
+
+ return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop);
+ }
+
+ setDriverToConfig();
+ var error = new Error('No available storage method found.');
+ self._driverSet = Promise$1.reject(error);
+ return self._driverSet;
+ }
+
+ return driverPromiseLoop();
+ };
+ }
+
+ // There might be a driver initialization in progress
+ // so wait for it to finish in order to avoid a possible
+ // race condition to set _dbInfo
+ var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function () {
+ return Promise$1.resolve();
+ }) : Promise$1.resolve();
+
+ this._driverSet = oldDriverSetDone.then(function () {
+ var driverName = supportedDrivers[0];
+ self._dbInfo = null;
+ self._ready = null;
+
+ return self.getDriver(driverName).then(function (driver) {
+ self._driver = driver._driver;
+ setDriverToConfig();
+ self._wrapLibraryMethodsWithReady();
+ self._initDriver = initDriver(supportedDrivers);
+ });
+ })["catch"](function () {
+ setDriverToConfig();
+ var error = new Error('No available storage method found.');
+ self._driverSet = Promise$1.reject(error);
+ return self._driverSet;
+ });
+
+ executeTwoCallbacks(this._driverSet, callback, errorCallback);
+ return this._driverSet;
+ };
+
+ LocalForage.prototype.supports = function supports(driverName) {
+ return !!DriverSupport[driverName];
+ };
+
+ LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {
+ extend(this, libraryMethodsAndProperties);
+ };
+
+ LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {
+ var supportedDrivers = [];
+ for (var i = 0, len = drivers.length; i < len; i++) {
+ var driverName = drivers[i];
+ if (this.supports(driverName)) {
+ supportedDrivers.push(driverName);
+ }
+ }
+ return supportedDrivers;
+ };
+
+ LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {
+ // Add a stub for each driver API method that delays the call to the
+ // corresponding driver method until localForage is ready. These stubs
+ // will be replaced by the driver methods as soon as the driver is
+ // loaded, so there is no performance impact.
+ for (var i = 0, len = LibraryMethods.length; i < len; i++) {
+ callWhenReady(this, LibraryMethods[i]);
+ }
+ };
+
+ LocalForage.prototype.createInstance = function createInstance(options) {
+ return new LocalForage(options);
+ };
+
+ return LocalForage;
+ }();
+
+ // The actual localForage object that we expose as a module or via a
+ // global. It's extended by pulling in one of our other libraries.
+
+
+ var localforage_js = new LocalForage();
+
+ module.exports = localforage_js;
+
+ },{"3":3}]},{},[4])(4)
+ });
+} (localforage$1, localforage$1.exports));
+
+var localforageExports = localforage$1.exports;
+var localforage = /*@__PURE__*/getDefaultExportFromCjs(localforageExports);
+
+/** Simpler wrapper for a file-backed cache for arbitrary metadata. */
+class LocalStorageCache {
+ appId;
+ version;
+ persister;
+ constructor(appId, version) {
+ this.appId = appId;
+ this.version = version;
+ this.persister = localforage.createInstance({
+ name: "dataview/cache/" + appId,
+ driver: [localforage.INDEXEDDB],
+ description: "Cache metadata about files and sections in the dataview index.",
+ });
+ }
+ /** Drop the entire cache instance and re-create a new fresh instance. */
+ async recreate() {
+ await localforage.dropInstance({ name: "dataview/cache/" + this.appId });
+ this.persister = localforage.createInstance({
+ name: "dataview/cache/" + this.appId,
+ driver: [localforage.INDEXEDDB],
+ description: "Cache metadata about files and sections in the dataview index.",
+ });
+ }
+ /** Load file metadata by path. */
+ async loadFile(path) {
+ return this.persister.getItem(this.fileKey(path)).then(raw => {
+ let result = raw;
+ if (result)
+ result.data = Transferable.value(result.data);
+ return result;
+ });
+ }
+ /** Store file metadata by path. */
+ async storeFile(path, data) {
+ await this.persister.setItem(this.fileKey(path), {
+ version: this.version,
+ time: Date.now(),
+ data: Transferable.transferable(data),
+ });
+ }
+ /** Drop old file keys that no longer exist. */
+ async synchronize(existing) {
+ let keys = new Set(await this.allFiles());
+ for (let exist of existing)
+ keys.delete(exist);
+ // Any keys remaining after deleting existing keys are non-existent keys that should be cleared from cache.
+ for (let key of keys)
+ await this.persister.removeItem(this.fileKey(key));
+ return keys;
+ }
+ /** Obtain a list of all metadata keys. */
+ async allKeys() {
+ return this.persister.keys();
+ }
+ /** Obtain a list of all persisted files. */
+ async allFiles() {
+ let keys = await this.allKeys();
+ return keys.filter(k => k.startsWith("file:")).map(k => k.substring(5));
+ }
+ fileKey(path) {
+ return "file:" + path;
+ }
+}
+
+function decodeBase64(base64, enableUnicode) {
+ var binaryString = atob(base64);
+ if (enableUnicode) {
+ var binaryView = new Uint8Array(binaryString.length);
+ for (var i = 0, n = binaryString.length; i < n; ++i) {
+ binaryView[i] = binaryString.charCodeAt(i);
+ }
+ return String.fromCharCode.apply(null, new Uint16Array(binaryView.buffer));
+ }
+ return binaryString;
+}
+
+function createURL(base64, sourcemapArg, enableUnicodeArg) {
+ var sourcemap = sourcemapArg === undefined ? null : sourcemapArg;
+ var enableUnicode = enableUnicodeArg === undefined ? false : enableUnicodeArg;
+ var source = decodeBase64(base64, enableUnicode);
+ var start = source.indexOf('\n', 10) + 1;
+ var body = source.substring(start) + (sourcemap ? '\/\/# sourceMappingURL=' + sourcemap : '');
+ var blob = new Blob([body], { type: 'application/javascript' });
+ return URL.createObjectURL(blob);
+}
+
+function createBase64WorkerFactory(base64, sourcemapArg, enableUnicodeArg) {
+ var url;
+ return function WorkerFactory(options) {
+ url = url || createURL(base64, sourcemapArg, enableUnicodeArg);
+ return new Worker(url, options);
+ };
+}
+
+var WorkerFactory = createBase64WorkerFactory('Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwooZnVuY3Rpb24gKCkgewogICd1c2Ugc3RyaWN0JzsKCiAgLy8gdGhlc2UgYXJlbid0IHJlYWxseSBwcml2YXRlLCBidXQgbm9yIGFyZSB0aGV5IHJlYWxseSB1c2VmdWwgdG8gZG9jdW1lbnQKCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KICBjbGFzcyBMdXhvbkVycm9yIGV4dGVuZHMgRXJyb3Ige30KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KICBjbGFzcyBJbnZhbGlkRGF0ZVRpbWVFcnJvciBleHRlbmRzIEx1eG9uRXJyb3IgewogICAgY29uc3RydWN0b3IocmVhc29uKSB7CiAgICAgIHN1cGVyKGBJbnZhbGlkIERhdGVUaW1lOiAke3JlYXNvbi50b01lc3NhZ2UoKX1gKTsKICAgIH0KICB9CgogIC8qKgogICAqIEBwcml2YXRlCiAgICovCiAgY2xhc3MgSW52YWxpZEludGVydmFsRXJyb3IgZXh0ZW5kcyBMdXhvbkVycm9yIHsKICAgIGNvbnN0cnVjdG9yKHJlYXNvbikgewogICAgICBzdXBlcihgSW52YWxpZCBJbnRlcnZhbDogJHtyZWFzb24udG9NZXNzYWdlKCl9YCk7CiAgICB9CiAgfQoKICAvKioKICAgKiBAcHJpdmF0ZQogICAqLwogIGNsYXNzIEludmFsaWREdXJhdGlvbkVycm9yIGV4dGVuZHMgTHV4b25FcnJvciB7CiAgICBjb25zdHJ1Y3RvcihyZWFzb24pIHsKICAgICAgc3VwZXIoYEludmFsaWQgRHVyYXRpb246ICR7cmVhc29uLnRvTWVzc2FnZSgpfWApOwogICAgfQogIH0KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KICBjbGFzcyBDb25mbGljdGluZ1NwZWNpZmljYXRpb25FcnJvciBleHRlbmRzIEx1eG9uRXJyb3Ige30KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KICBjbGFzcyBJbnZhbGlkVW5pdEVycm9yIGV4dGVuZHMgTHV4b25FcnJvciB7CiAgICBjb25zdHJ1Y3Rvcih1bml0KSB7CiAgICAgIHN1cGVyKGBJbnZhbGlkIHVuaXQgJHt1bml0fWApOwogICAgfQogIH0KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KICBjbGFzcyBJbnZhbGlkQXJndW1lbnRFcnJvciBleHRlbmRzIEx1eG9uRXJyb3Ige30KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KICBjbGFzcyBab25lSXNBYnN0cmFjdEVycm9yIGV4dGVuZHMgTHV4b25FcnJvciB7CiAgICBjb25zdHJ1Y3RvcigpIHsKICAgICAgc3VwZXIoIlpvbmUgaXMgYW4gYWJzdHJhY3QgY2xhc3MiKTsKICAgIH0KICB9CgogIC8qKgogICAqIEBwcml2YXRlCiAgICovCgogIGNvbnN0IG4gPSAibnVtZXJpYyIsCiAgICBzID0gInNob3J0IiwKICAgIGwgPSAibG9uZyI7CgogIGNvbnN0IERBVEVfU0hPUlQgPSB7CiAgICB5ZWFyOiBuLAogICAgbW9udGg6IG4sCiAgICBkYXk6IG4sCiAgfTsKCiAgY29uc3QgREFURV9NRUQgPSB7CiAgICB5ZWFyOiBuLAogICAgbW9udGg6IHMsCiAgICBkYXk6IG4sCiAgfTsKCiAgY29uc3QgREFURV9NRURfV0lUSF9XRUVLREFZID0gewogICAgeWVhcjogbiwKICAgIG1vbnRoOiBzLAogICAgZGF5OiBuLAogICAgd2Vla2RheTogcywKICB9OwoKICBjb25zdCBEQVRFX0ZVTEwgPSB7CiAgICB5ZWFyOiBuLAogICAgbW9udGg6IGwsCiAgICBkYXk6IG4sCiAgfTsKCiAgY29uc3QgREFURV9IVUdFID0gewogICAgeWVhcjogbiwKICAgIG1vbnRoOiBsLAogICAgZGF5OiBuLAogICAgd2Vla2RheTogbCwKICB9OwoKICBjb25zdCBUSU1FX1NJTVBMRSA9IHsKICAgIGhvdXI6IG4sCiAgICBtaW51dGU6IG4sCiAgfTsKCiAgY29uc3QgVElNRV9XSVRIX1NFQ09ORFMgPSB7CiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgc2Vjb25kOiBuLAogIH07CgogIGNvbnN0IFRJTUVfV0lUSF9TSE9SVF9PRkZTRVQgPSB7CiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgc2Vjb25kOiBuLAogICAgdGltZVpvbmVOYW1lOiBzLAogIH07CgogIGNvbnN0IFRJTUVfV0lUSF9MT05HX09GRlNFVCA9IHsKICAgIGhvdXI6IG4sCiAgICBtaW51dGU6IG4sCiAgICBzZWNvbmQ6IG4sCiAgICB0aW1lWm9uZU5hbWU6IGwsCiAgfTsKCiAgY29uc3QgVElNRV8yNF9TSU1QTEUgPSB7CiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgaG91ckN5Y2xlOiAiaDIzIiwKICB9OwoKICBjb25zdCBUSU1FXzI0X1dJVEhfU0VDT05EUyA9IHsKICAgIGhvdXI6IG4sCiAgICBtaW51dGU6IG4sCiAgICBzZWNvbmQ6IG4sCiAgICBob3VyQ3ljbGU6ICJoMjMiLAogIH07CgogIGNvbnN0IFRJTUVfMjRfV0lUSF9TSE9SVF9PRkZTRVQgPSB7CiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgc2Vjb25kOiBuLAogICAgaG91ckN5Y2xlOiAiaDIzIiwKICAgIHRpbWVab25lTmFtZTogcywKICB9OwoKICBjb25zdCBUSU1FXzI0X1dJVEhfTE9OR19PRkZTRVQgPSB7CiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgc2Vjb25kOiBuLAogICAgaG91ckN5Y2xlOiAiaDIzIiwKICAgIHRpbWVab25lTmFtZTogbCwKICB9OwoKICBjb25zdCBEQVRFVElNRV9TSE9SVCA9IHsKICAgIHllYXI6IG4sCiAgICBtb250aDogbiwKICAgIGRheTogbiwKICAgIGhvdXI6IG4sCiAgICBtaW51dGU6IG4sCiAgfTsKCiAgY29uc3QgREFURVRJTUVfU0hPUlRfV0lUSF9TRUNPTkRTID0gewogICAgeWVhcjogbiwKICAgIG1vbnRoOiBuLAogICAgZGF5OiBuLAogICAgaG91cjogbiwKICAgIG1pbnV0ZTogbiwKICAgIHNlY29uZDogbiwKICB9OwoKICBjb25zdCBEQVRFVElNRV9NRUQgPSB7CiAgICB5ZWFyOiBuLAogICAgbW9udGg6IHMsCiAgICBkYXk6IG4sCiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogIH07CgogIGNvbnN0IERBVEVUSU1FX01FRF9XSVRIX1NFQ09ORFMgPSB7CiAgICB5ZWFyOiBuLAogICAgbW9udGg6IHMsCiAgICBkYXk6IG4sCiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgc2Vjb25kOiBuLAogIH07CgogIGNvbnN0IERBVEVUSU1FX01FRF9XSVRIX1dFRUtEQVkgPSB7CiAgICB5ZWFyOiBuLAogICAgbW9udGg6IHMsCiAgICBkYXk6IG4sCiAgICB3ZWVrZGF5OiBzLAogICAgaG91cjogbiwKICAgIG1pbnV0ZTogbiwKICB9OwoKICBjb25zdCBEQVRFVElNRV9GVUxMID0gewogICAgeWVhcjogbiwKICAgIG1vbnRoOiBsLAogICAgZGF5OiBuLAogICAgaG91cjogbiwKICAgIG1pbnV0ZTogbiwKICAgIHRpbWVab25lTmFtZTogcywKICB9OwoKICBjb25zdCBEQVRFVElNRV9GVUxMX1dJVEhfU0VDT05EUyA9IHsKICAgIHllYXI6IG4sCiAgICBtb250aDogbCwKICAgIGRheTogbiwKICAgIGhvdXI6IG4sCiAgICBtaW51dGU6IG4sCiAgICBzZWNvbmQ6IG4sCiAgICB0aW1lWm9uZU5hbWU6IHMsCiAgfTsKCiAgY29uc3QgREFURVRJTUVfSFVHRSA9IHsKICAgIHllYXI6IG4sCiAgICBtb250aDogbCwKICAgIGRheTogbiwKICAgIHdlZWtkYXk6IGwsCiAgICBob3VyOiBuLAogICAgbWludXRlOiBuLAogICAgdGltZVpvbmVOYW1lOiBsLAogIH07CgogIGNvbnN0IERBVEVUSU1FX0hVR0VfV0lUSF9TRUNPTkRTID0gewogICAgeWVhcjogbiwKICAgIG1vbnRoOiBsLAogICAgZGF5OiBuLAogICAgd2Vla2RheTogbCwKICAgIGhvdXI6IG4sCiAgICBtaW51dGU6IG4sCiAgICBzZWNvbmQ6IG4sCiAgICB0aW1lWm9uZU5hbWU6IGwsCiAgfTsKCiAgLyoqCiAgICogQGludGVyZmFjZQogICAqLwogIGNsYXNzIFpvbmUgewogICAgLyoqCiAgICAgKiBUaGUgdHlwZSBvZiB6b25lCiAgICAgKiBAYWJzdHJhY3QKICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCB0eXBlKCkgewogICAgICB0aHJvdyBuZXcgWm9uZUlzQWJzdHJhY3RFcnJvcigpOwogICAgfQoKICAgIC8qKgogICAgICogVGhlIG5hbWUgb2YgdGhpcyB6b25lLgogICAgICogQGFic3RyYWN0CiAgICAgKiBAdHlwZSB7c3RyaW5nfQogICAgICovCiAgICBnZXQgbmFtZSgpIHsKICAgICAgdGhyb3cgbmV3IFpvbmVJc0Fic3RyYWN0RXJyb3IoKTsKICAgIH0KCiAgICBnZXQgaWFuYU5hbWUoKSB7CiAgICAgIHJldHVybiB0aGlzLm5hbWU7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIG9mZnNldCBpcyBrbm93biB0byBiZSBmaXhlZCBmb3IgdGhlIHdob2xlIHllYXIuCiAgICAgKiBAYWJzdHJhY3QKICAgICAqIEB0eXBlIHtib29sZWFufQogICAgICovCiAgICBnZXQgaXNVbml2ZXJzYWwoKSB7CiAgICAgIHRocm93IG5ldyBab25lSXNBYnN0cmFjdEVycm9yKCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHRoZSBvZmZzZXQncyBjb21tb24gbmFtZSAoc3VjaCBhcyBFU1QpIGF0IHRoZSBzcGVjaWZpZWQgdGltZXN0YW1wCiAgICAgKiBAYWJzdHJhY3QKICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0cyAtIEVwb2NoIG1pbGxpc2Vjb25kcyBmb3Igd2hpY2ggdG8gZ2V0IHRoZSBuYW1lCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIE9wdGlvbnMgdG8gYWZmZWN0IHRoZSBmb3JtYXQKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLmZvcm1hdCAtIFdoYXQgc3R5bGUgb2Ygb2Zmc2V0IHRvIHJldHVybi4gQWNjZXB0cyAnbG9uZycgb3IgJ3Nob3J0Jy4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLmxvY2FsZSAtIFdoYXQgbG9jYWxlIHRvIHJldHVybiB0aGUgb2Zmc2V0IG5hbWUgaW4uCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIG9mZnNldE5hbWUodHMsIG9wdHMpIHsKICAgICAgdGhyb3cgbmV3IFpvbmVJc0Fic3RyYWN0RXJyb3IoKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgdGhlIG9mZnNldCdzIHZhbHVlIGFzIGEgc3RyaW5nCiAgICAgKiBAYWJzdHJhY3QKICAgICAqIEBwYXJhbSB7bnVtYmVyfSB0cyAtIEVwb2NoIG1pbGxpc2Vjb25kcyBmb3Igd2hpY2ggdG8gZ2V0IHRoZSBvZmZzZXQKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBmb3JtYXQgLSBXaGF0IHN0eWxlIG9mIG9mZnNldCB0byByZXR1cm4uCiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgQWNjZXB0cyAnbmFycm93JywgJ3Nob3J0Jywgb3IgJ3RlY2hpZScuIFJldHVybmluZyAnKzYnLCAnKzA2OjAwJywgb3IgJyswNjAwJyByZXNwZWN0aXZlbHkKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgZm9ybWF0T2Zmc2V0KHRzLCBmb3JtYXQpIHsKICAgICAgdGhyb3cgbmV3IFpvbmVJc0Fic3RyYWN0RXJyb3IoKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB0aGUgb2Zmc2V0IGluIG1pbnV0ZXMgZm9yIHRoaXMgem9uZSBhdCB0aGUgc3BlY2lmaWVkIHRpbWVzdGFtcC4KICAgICAqIEBhYnN0cmFjdAogICAgICogQHBhcmFtIHtudW1iZXJ9IHRzIC0gRXBvY2ggbWlsbGlzZWNvbmRzIGZvciB3aGljaCB0byBjb21wdXRlIHRoZSBvZmZzZXQKICAgICAqIEByZXR1cm4ge251bWJlcn0KICAgICAqLwogICAgb2Zmc2V0KHRzKSB7CiAgICAgIHRocm93IG5ldyBab25lSXNBYnN0cmFjdEVycm9yKCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gd2hldGhlciB0aGlzIFpvbmUgaXMgZXF1YWwgdG8gYW5vdGhlciB6b25lCiAgICAgKiBAYWJzdHJhY3QKICAgICAqIEBwYXJhbSB7Wm9uZX0gb3RoZXJab25lIC0gdGhlIHpvbmUgdG8gY29tcGFyZQogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgZXF1YWxzKG90aGVyWm9uZSkgewogICAgICB0aHJvdyBuZXcgWm9uZUlzQWJzdHJhY3RFcnJvcigpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHdoZXRoZXIgdGhpcyBab25lIGlzIHZhbGlkLgogICAgICogQGFic3RyYWN0CiAgICAgKiBAdHlwZSB7Ym9vbGVhbn0KICAgICAqLwogICAgZ2V0IGlzVmFsaWQoKSB7CiAgICAgIHRocm93IG5ldyBab25lSXNBYnN0cmFjdEVycm9yKCk7CiAgICB9CiAgfQoKICBsZXQgc2luZ2xldG9uJDEgPSBudWxsOwoKICAvKioKICAgKiBSZXByZXNlbnRzIHRoZSBsb2NhbCB6b25lIGZvciB0aGlzIEphdmFTY3JpcHQgZW52aXJvbm1lbnQuCiAgICogQGltcGxlbWVudHMge1pvbmV9CiAgICovCiAgY2xhc3MgU3lzdGVtWm9uZSBleHRlbmRzIFpvbmUgewogICAgLyoqCiAgICAgKiBHZXQgYSBzaW5nbGV0b24gaW5zdGFuY2Ugb2YgdGhlIGxvY2FsIHpvbmUKICAgICAqIEByZXR1cm4ge1N5c3RlbVpvbmV9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgaW5zdGFuY2UoKSB7CiAgICAgIGlmIChzaW5nbGV0b24kMSA9PT0gbnVsbCkgewogICAgICAgIHNpbmdsZXRvbiQxID0gbmV3IFN5c3RlbVpvbmUoKTsKICAgICAgfQogICAgICByZXR1cm4gc2luZ2xldG9uJDE7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCB0eXBlKCkgewogICAgICByZXR1cm4gInN5c3RlbSI7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCBuYW1lKCkgewogICAgICByZXR1cm4gbmV3IEludGwuRGF0ZVRpbWVGb3JtYXQoKS5yZXNvbHZlZE9wdGlvbnMoKS50aW1lWm9uZTsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZ2V0IGlzVW5pdmVyc2FsKCkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIG9mZnNldE5hbWUodHMsIHsgZm9ybWF0LCBsb2NhbGUgfSkgewogICAgICByZXR1cm4gcGFyc2Vab25lSW5mbyh0cywgZm9ybWF0LCBsb2NhbGUpOwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBmb3JtYXRPZmZzZXQodHMsIGZvcm1hdCkgewogICAgICByZXR1cm4gZm9ybWF0T2Zmc2V0KHRoaXMub2Zmc2V0KHRzKSwgZm9ybWF0KTsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgb2Zmc2V0KHRzKSB7CiAgICAgIHJldHVybiAtbmV3IERhdGUodHMpLmdldFRpbWV6b25lT2Zmc2V0KCk7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGVxdWFscyhvdGhlclpvbmUpIHsKICAgICAgcmV0dXJuIG90aGVyWm9uZS50eXBlID09PSAic3lzdGVtIjsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZ2V0IGlzVmFsaWQoKSB7CiAgICAgIHJldHVybiB0cnVlOwogICAgfQogIH0KCiAgbGV0IGR0ZkNhY2hlID0ge307CiAgZnVuY3Rpb24gbWFrZURURih6b25lKSB7CiAgICBpZiAoIWR0ZkNhY2hlW3pvbmVdKSB7CiAgICAgIGR0ZkNhY2hlW3pvbmVdID0gbmV3IEludGwuRGF0ZVRpbWVGb3JtYXQoImVuLVVTIiwgewogICAgICAgIGhvdXIxMjogZmFsc2UsCiAgICAgICAgdGltZVpvbmU6IHpvbmUsCiAgICAgICAgeWVhcjogIm51bWVyaWMiLAogICAgICAgIG1vbnRoOiAiMi1kaWdpdCIsCiAgICAgICAgZGF5OiAiMi1kaWdpdCIsCiAgICAgICAgaG91cjogIjItZGlnaXQiLAogICAgICAgIG1pbnV0ZTogIjItZGlnaXQiLAogICAgICAgIHNlY29uZDogIjItZGlnaXQiLAogICAgICAgIGVyYTogInNob3J0IiwKICAgICAgfSk7CiAgICB9CiAgICByZXR1cm4gZHRmQ2FjaGVbem9uZV07CiAgfQoKICBjb25zdCB0eXBlVG9Qb3MgPSB7CiAgICB5ZWFyOiAwLAogICAgbW9udGg6IDEsCiAgICBkYXk6IDIsCiAgICBlcmE6IDMsCiAgICBob3VyOiA0LAogICAgbWludXRlOiA1LAogICAgc2Vjb25kOiA2LAogIH07CgogIGZ1bmN0aW9uIGhhY2t5T2Zmc2V0KGR0ZiwgZGF0ZSkgewogICAgY29uc3QgZm9ybWF0dGVkID0gZHRmLmZvcm1hdChkYXRlKS5yZXBsYWNlKC9cdTIwMEUvZywgIiIpLAogICAgICBwYXJzZWQgPSAvKFxkKylcLyhcZCspXC8oXGQrKSAoQUR8QkMpLD8gKFxkKyk6KFxkKyk6KFxkKykvLmV4ZWMoZm9ybWF0dGVkKSwKICAgICAgWywgZk1vbnRoLCBmRGF5LCBmWWVhciwgZmFkT3JCYywgZkhvdXIsIGZNaW51dGUsIGZTZWNvbmRdID0gcGFyc2VkOwogICAgcmV0dXJuIFtmWWVhciwgZk1vbnRoLCBmRGF5LCBmYWRPckJjLCBmSG91ciwgZk1pbnV0ZSwgZlNlY29uZF07CiAgfQoKICBmdW5jdGlvbiBwYXJ0c09mZnNldChkdGYsIGRhdGUpIHsKICAgIGNvbnN0IGZvcm1hdHRlZCA9IGR0Zi5mb3JtYXRUb1BhcnRzKGRhdGUpOwogICAgY29uc3QgZmlsbGVkID0gW107CiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGZvcm1hdHRlZC5sZW5ndGg7IGkrKykgewogICAgICBjb25zdCB7IHR5cGUsIHZhbHVlIH0gPSBmb3JtYXR0ZWRbaV07CiAgICAgIGNvbnN0IHBvcyA9IHR5cGVUb1Bvc1t0eXBlXTsKCiAgICAgIGlmICh0eXBlID09PSAiZXJhIikgewogICAgICAgIGZpbGxlZFtwb3NdID0gdmFsdWU7CiAgICAgIH0gZWxzZSBpZiAoIWlzVW5kZWZpbmVkKHBvcykpIHsKICAgICAgICBmaWxsZWRbcG9zXSA9IHBhcnNlSW50KHZhbHVlLCAxMCk7CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBmaWxsZWQ7CiAgfQoKICBsZXQgaWFuYVpvbmVDYWNoZSA9IHt9OwogIC8qKgogICAqIEEgem9uZSBpZGVudGlmaWVkIGJ5IGFuIElBTkEgaWRlbnRpZmllciwgbGlrZSBBbWVyaWNhL05ld19Zb3JrCiAgICogQGltcGxlbWVudHMge1pvbmV9CiAgICovCiAgY2xhc3MgSUFOQVpvbmUgZXh0ZW5kcyBab25lIHsKICAgIC8qKgogICAgICogQHBhcmFtIHtzdHJpbmd9IG5hbWUgLSBab25lIG5hbWUKICAgICAqIEByZXR1cm4ge0lBTkFab25lfQogICAgICovCiAgICBzdGF0aWMgY3JlYXRlKG5hbWUpIHsKICAgICAgaWYgKCFpYW5hWm9uZUNhY2hlW25hbWVdKSB7CiAgICAgICAgaWFuYVpvbmVDYWNoZVtuYW1lXSA9IG5ldyBJQU5BWm9uZShuYW1lKTsKICAgICAgfQogICAgICByZXR1cm4gaWFuYVpvbmVDYWNoZVtuYW1lXTsKICAgIH0KCiAgICAvKioKICAgICAqIFJlc2V0IGxvY2FsIGNhY2hlcy4gU2hvdWxkIG9ubHkgYmUgbmVjZXNzYXJ5IGluIHRlc3Rpbmcgc2NlbmFyaW9zLgogICAgICogQHJldHVybiB7dm9pZH0KICAgICAqLwogICAgc3RhdGljIHJlc2V0Q2FjaGUoKSB7CiAgICAgIGlhbmFab25lQ2FjaGUgPSB7fTsKICAgICAgZHRmQ2FjaGUgPSB7fTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgd2hldGhlciB0aGUgcHJvdmlkZWQgc3RyaW5nIGlzIGEgdmFsaWQgc3BlY2lmaWVyLiBUaGlzIG9ubHkgY2hlY2tzIHRoZSBzdHJpbmcncyBmb3JtYXQsIG5vdCB0aGF0IHRoZSBzcGVjaWZpZXIgaWRlbnRpZmllcyBhIGtub3duIHpvbmU7IHNlZSBpc1ZhbGlkWm9uZSBmb3IgdGhhdC4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBzIC0gVGhlIHN0cmluZyB0byBjaGVjayB2YWxpZGl0eSBvbgogICAgICogQGV4YW1wbGUgSUFOQVpvbmUuaXNWYWxpZFNwZWNpZmllcigiQW1lcmljYS9OZXdfWW9yayIpIC8vPT4gdHJ1ZQogICAgICogQGV4YW1wbGUgSUFOQVpvbmUuaXNWYWxpZFNwZWNpZmllcigiU3BvcnR+fmJsb3JwIikgLy89PiBmYWxzZQogICAgICogQGRlcHJlY2F0ZWQgVGhpcyBtZXRob2QgcmV0dXJucyBmYWxzZSBmb3Igc29tZSB2YWxpZCBJQU5BIG5hbWVzLiBVc2UgaXNWYWxpZFpvbmUgaW5zdGVhZC4KICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIHN0YXRpYyBpc1ZhbGlkU3BlY2lmaWVyKHMpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZFpvbmUocyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIHByb3ZpZGVkIHN0cmluZyBpZGVudGlmaWVzIGEgcmVhbCB6b25lCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gem9uZSAtIFRoZSBzdHJpbmcgdG8gY2hlY2sKICAgICAqIEBleGFtcGxlIElBTkFab25lLmlzVmFsaWRab25lKCJBbWVyaWNhL05ld19Zb3JrIikgLy89PiB0cnVlCiAgICAgKiBAZXhhbXBsZSBJQU5BWm9uZS5pc1ZhbGlkWm9uZSgiRmFudGFzaWEvQ2FzdGxlIikgLy89PiBmYWxzZQogICAgICogQGV4YW1wbGUgSUFOQVpvbmUuaXNWYWxpZFpvbmUoIlNwb3J0fn5ibG9ycCIpIC8vPT4gZmFsc2UKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIHN0YXRpYyBpc1ZhbGlkWm9uZSh6b25lKSB7CiAgICAgIGlmICghem9uZSkgewogICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgfQogICAgICB0cnkgewogICAgICAgIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KCJlbi1VUyIsIHsgdGltZVpvbmU6IHpvbmUgfSkuZm9ybWF0KCk7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgIH0KICAgIH0KCiAgICBjb25zdHJ1Y3RvcihuYW1lKSB7CiAgICAgIHN1cGVyKCk7CiAgICAgIC8qKiBAcHJpdmF0ZSAqKi8KICAgICAgdGhpcy56b25lTmFtZSA9IG5hbWU7CiAgICAgIC8qKiBAcHJpdmF0ZSAqKi8KICAgICAgdGhpcy52YWxpZCA9IElBTkFab25lLmlzVmFsaWRab25lKG5hbWUpOwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBnZXQgdHlwZSgpIHsKICAgICAgcmV0dXJuICJpYW5hIjsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZ2V0IG5hbWUoKSB7CiAgICAgIHJldHVybiB0aGlzLnpvbmVOYW1lOwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBnZXQgaXNVbml2ZXJzYWwoKSB7CiAgICAgIHJldHVybiBmYWxzZTsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgb2Zmc2V0TmFtZSh0cywgeyBmb3JtYXQsIGxvY2FsZSB9KSB7CiAgICAgIHJldHVybiBwYXJzZVpvbmVJbmZvKHRzLCBmb3JtYXQsIGxvY2FsZSwgdGhpcy5uYW1lKTsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZm9ybWF0T2Zmc2V0KHRzLCBmb3JtYXQpIHsKICAgICAgcmV0dXJuIGZvcm1hdE9mZnNldCh0aGlzLm9mZnNldCh0cyksIGZvcm1hdCk7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIG9mZnNldCh0cykgewogICAgICBjb25zdCBkYXRlID0gbmV3IERhdGUodHMpOwoKICAgICAgaWYgKGlzTmFOKGRhdGUpKSByZXR1cm4gTmFOOwoKICAgICAgY29uc3QgZHRmID0gbWFrZURURih0aGlzLm5hbWUpOwogICAgICBsZXQgW3llYXIsIG1vbnRoLCBkYXksIGFkT3JCYywgaG91ciwgbWludXRlLCBzZWNvbmRdID0gZHRmLmZvcm1hdFRvUGFydHMKICAgICAgICA/IHBhcnRzT2Zmc2V0KGR0ZiwgZGF0ZSkKICAgICAgICA6IGhhY2t5T2Zmc2V0KGR0ZiwgZGF0ZSk7CgogICAgICBpZiAoYWRPckJjID09PSAiQkMiKSB7CiAgICAgICAgeWVhciA9IC1NYXRoLmFicyh5ZWFyKSArIDE7CiAgICAgIH0KCiAgICAgIC8vIGJlY2F1c2Ugd2UncmUgdXNpbmcgaG91cjEyIGFuZCBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0xMDI1NTY0JmNhbj0yJnE9JTIyMjQlM0EwMCUyMiUyMGRhdGV0aW1lZm9ybWF0CiAgICAgIGNvbnN0IGFkanVzdGVkSG91ciA9IGhvdXIgPT09IDI0ID8gMCA6IGhvdXI7CgogICAgICBjb25zdCBhc1VUQyA9IG9ialRvTG9jYWxUUyh7CiAgICAgICAgeWVhciwKICAgICAgICBtb250aCwKICAgICAgICBkYXksCiAgICAgICAgaG91cjogYWRqdXN0ZWRIb3VyLAogICAgICAgIG1pbnV0ZSwKICAgICAgICBzZWNvbmQsCiAgICAgICAgbWlsbGlzZWNvbmQ6IDAsCiAgICAgIH0pOwoKICAgICAgbGV0IGFzVFMgPSArZGF0ZTsKICAgICAgY29uc3Qgb3ZlciA9IGFzVFMgJSAxMDAwOwogICAgICBhc1RTIC09IG92ZXIgPj0gMCA/IG92ZXIgOiAxMDAwICsgb3ZlcjsKICAgICAgcmV0dXJuIChhc1VUQyAtIGFzVFMpIC8gKDYwICogMTAwMCk7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGVxdWFscyhvdGhlclpvbmUpIHsKICAgICAgcmV0dXJuIG90aGVyWm9uZS50eXBlID09PSAiaWFuYSIgJiYgb3RoZXJab25lLm5hbWUgPT09IHRoaXMubmFtZTsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZ2V0IGlzVmFsaWQoKSB7CiAgICAgIHJldHVybiB0aGlzLnZhbGlkOwogICAgfQogIH0KCiAgLy8gdG9kbyAtIHJlbWFwIGNhY2hpbmcKCiAgbGV0IGludGxMRkNhY2hlID0ge307CiAgZnVuY3Rpb24gZ2V0Q2FjaGVkTEYobG9jU3RyaW5nLCBvcHRzID0ge30pIHsKICAgIGNvbnN0IGtleSA9IEpTT04uc3RyaW5naWZ5KFtsb2NTdHJpbmcsIG9wdHNdKTsKICAgIGxldCBkdGYgPSBpbnRsTEZDYWNoZVtrZXldOwogICAgaWYgKCFkdGYpIHsKICAgICAgZHRmID0gbmV3IEludGwuTGlzdEZvcm1hdChsb2NTdHJpbmcsIG9wdHMpOwogICAgICBpbnRsTEZDYWNoZVtrZXldID0gZHRmOwogICAgfQogICAgcmV0dXJuIGR0ZjsKICB9CgogIGxldCBpbnRsRFRDYWNoZSA9IHt9OwogIGZ1bmN0aW9uIGdldENhY2hlZERURihsb2NTdHJpbmcsIG9wdHMgPSB7fSkgewogICAgY29uc3Qga2V5ID0gSlNPTi5zdHJpbmdpZnkoW2xvY1N0cmluZywgb3B0c10pOwogICAgbGV0IGR0ZiA9IGludGxEVENhY2hlW2tleV07CiAgICBpZiAoIWR0ZikgewogICAgICBkdGYgPSBuZXcgSW50bC5EYXRlVGltZUZvcm1hdChsb2NTdHJpbmcsIG9wdHMpOwogICAgICBpbnRsRFRDYWNoZVtrZXldID0gZHRmOwogICAgfQogICAgcmV0dXJuIGR0ZjsKICB9CgogIGxldCBpbnRsTnVtQ2FjaGUgPSB7fTsKICBmdW5jdGlvbiBnZXRDYWNoZWRJTkYobG9jU3RyaW5nLCBvcHRzID0ge30pIHsKICAgIGNvbnN0IGtleSA9IEpTT04uc3RyaW5naWZ5KFtsb2NTdHJpbmcsIG9wdHNdKTsKICAgIGxldCBpbmYgPSBpbnRsTnVtQ2FjaGVba2V5XTsKICAgIGlmICghaW5mKSB7CiAgICAgIGluZiA9IG5ldyBJbnRsLk51bWJlckZvcm1hdChsb2NTdHJpbmcsIG9wdHMpOwogICAgICBpbnRsTnVtQ2FjaGVba2V5XSA9IGluZjsKICAgIH0KICAgIHJldHVybiBpbmY7CiAgfQoKICBsZXQgaW50bFJlbENhY2hlID0ge307CiAgZnVuY3Rpb24gZ2V0Q2FjaGVkUlRGKGxvY1N0cmluZywgb3B0cyA9IHt9KSB7CiAgICBjb25zdCB7IGJhc2UsIC4uLmNhY2hlS2V5T3B0cyB9ID0gb3B0czsgLy8gZXhjbHVkZSBgYmFzZWAgZnJvbSB0aGUgb3B0aW9ucwogICAgY29uc3Qga2V5ID0gSlNPTi5zdHJpbmdpZnkoW2xvY1N0cmluZywgY2FjaGVLZXlPcHRzXSk7CiAgICBsZXQgaW5mID0gaW50bFJlbENhY2hlW2tleV07CiAgICBpZiAoIWluZikgewogICAgICBpbmYgPSBuZXcgSW50bC5SZWxhdGl2ZVRpbWVGb3JtYXQobG9jU3RyaW5nLCBvcHRzKTsKICAgICAgaW50bFJlbENhY2hlW2tleV0gPSBpbmY7CiAgICB9CiAgICByZXR1cm4gaW5mOwogIH0KCiAgbGV0IHN5c0xvY2FsZUNhY2hlID0gbnVsbDsKICBmdW5jdGlvbiBzeXN0ZW1Mb2NhbGUoKSB7CiAgICBpZiAoc3lzTG9jYWxlQ2FjaGUpIHsKICAgICAgcmV0dXJuIHN5c0xvY2FsZUNhY2hlOwogICAgfSBlbHNlIHsKICAgICAgc3lzTG9jYWxlQ2FjaGUgPSBuZXcgSW50bC5EYXRlVGltZUZvcm1hdCgpLnJlc29sdmVkT3B0aW9ucygpLmxvY2FsZTsKICAgICAgcmV0dXJuIHN5c0xvY2FsZUNhY2hlOwogICAgfQogIH0KCiAgZnVuY3Rpb24gcGFyc2VMb2NhbGVTdHJpbmcobG9jYWxlU3RyKSB7CiAgICAvLyBJIHJlYWxseSB3YW50IHRvIGF2b2lkIHdyaXRpbmcgYSBCQ1AgNDcgcGFyc2VyCiAgICAvLyBzZWUsIGUuZy4gaHR0cHM6Ly9naXRodWIuY29tL3dvb29ybS9iY3AtNDcKICAgIC8vIEluc3RlYWQsIHdlJ2xsIGRvIHRoaXM6CgogICAgLy8gYSkgaWYgdGhlIHN0cmluZyBoYXMgbm8gLXUgZXh0ZW5zaW9ucywganVzdCBsZWF2ZSBpdCBhbG9uZQogICAgLy8gYikgaWYgaXQgZG9lcywgdXNlIEludGwgdG8gcmVzb2x2ZSBldmVyeXRoaW5nCiAgICAvLyBjKSBpZiBJbnRsIGZhaWxzLCB0cnkgYWdhaW4gd2l0aG91dCB0aGUgLXUKCiAgICAvLyBwcml2YXRlIHN1YnRhZ3MgYW5kIHVuaWNvZGUgc3VidGFncyBoYXZlIG9yZGVyaW5nIHJlcXVpcmVtZW50cywKICAgIC8vIGFuZCB3ZSdyZSBub3QgcHJvcGVybHkgcGFyc2luZyB0aGlzLCBzbyBqdXN0IHN0cmlwIG91dCB0aGUKICAgIC8vIHByaXZhdGUgb25lcyBpZiB0aGV5IGV4aXN0LgogICAgY29uc3QgeEluZGV4ID0gbG9jYWxlU3RyLmluZGV4T2YoIi14LSIpOwogICAgaWYgKHhJbmRleCAhPT0gLTEpIHsKICAgICAgbG9jYWxlU3RyID0gbG9jYWxlU3RyLnN1YnN0cmluZygwLCB4SW5kZXgpOwogICAgfQoKICAgIGNvbnN0IHVJbmRleCA9IGxvY2FsZVN0ci5pbmRleE9mKCItdS0iKTsKICAgIGlmICh1SW5kZXggPT09IC0xKSB7CiAgICAgIHJldHVybiBbbG9jYWxlU3RyXTsKICAgIH0gZWxzZSB7CiAgICAgIGxldCBvcHRpb25zOwogICAgICBsZXQgc2VsZWN0ZWRTdHI7CiAgICAgIHRyeSB7CiAgICAgICAgb3B0aW9ucyA9IGdldENhY2hlZERURihsb2NhbGVTdHIpLnJlc29sdmVkT3B0aW9ucygpOwogICAgICAgIHNlbGVjdGVkU3RyID0gbG9jYWxlU3RyOwogICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgY29uc3Qgc21hbGxlciA9IGxvY2FsZVN0ci5zdWJzdHJpbmcoMCwgdUluZGV4KTsKICAgICAgICBvcHRpb25zID0gZ2V0Q2FjaGVkRFRGKHNtYWxsZXIpLnJlc29sdmVkT3B0aW9ucygpOwogICAgICAgIHNlbGVjdGVkU3RyID0gc21hbGxlcjsKICAgICAgfQoKICAgICAgY29uc3QgeyBudW1iZXJpbmdTeXN0ZW0sIGNhbGVuZGFyIH0gPSBvcHRpb25zOwogICAgICByZXR1cm4gW3NlbGVjdGVkU3RyLCBudW1iZXJpbmdTeXN0ZW0sIGNhbGVuZGFyXTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGludGxDb25maWdTdHJpbmcobG9jYWxlU3RyLCBudW1iZXJpbmdTeXN0ZW0sIG91dHB1dENhbGVuZGFyKSB7CiAgICBpZiAob3V0cHV0Q2FsZW5kYXIgfHwgbnVtYmVyaW5nU3lzdGVtKSB7CiAgICAgIGlmICghbG9jYWxlU3RyLmluY2x1ZGVzKCItdS0iKSkgewogICAgICAgIGxvY2FsZVN0ciArPSAiLXUiOwogICAgICB9CgogICAgICBpZiAob3V0cHV0Q2FsZW5kYXIpIHsKICAgICAgICBsb2NhbGVTdHIgKz0gYC1jYS0ke291dHB1dENhbGVuZGFyfWA7CiAgICAgIH0KCiAgICAgIGlmIChudW1iZXJpbmdTeXN0ZW0pIHsKICAgICAgICBsb2NhbGVTdHIgKz0gYC1udS0ke251bWJlcmluZ1N5c3RlbX1gOwogICAgICB9CiAgICAgIHJldHVybiBsb2NhbGVTdHI7CiAgICB9IGVsc2UgewogICAgICByZXR1cm4gbG9jYWxlU3RyOwogICAgfQogIH0KCiAgZnVuY3Rpb24gbWFwTW9udGhzKGYpIHsKICAgIGNvbnN0IG1zID0gW107CiAgICBmb3IgKGxldCBpID0gMTsgaSA8PSAxMjsgaSsrKSB7CiAgICAgIGNvbnN0IGR0ID0gRGF0ZVRpbWUudXRjKDIwMDksIGksIDEpOwogICAgICBtcy5wdXNoKGYoZHQpKTsKICAgIH0KICAgIHJldHVybiBtczsKICB9CgogIGZ1bmN0aW9uIG1hcFdlZWtkYXlzKGYpIHsKICAgIGNvbnN0IG1zID0gW107CiAgICBmb3IgKGxldCBpID0gMTsgaSA8PSA3OyBpKyspIHsKICAgICAgY29uc3QgZHQgPSBEYXRlVGltZS51dGMoMjAxNiwgMTEsIDEzICsgaSk7CiAgICAgIG1zLnB1c2goZihkdCkpOwogICAgfQogICAgcmV0dXJuIG1zOwogIH0KCiAgZnVuY3Rpb24gbGlzdFN0dWZmKGxvYywgbGVuZ3RoLCBlbmdsaXNoRm4sIGludGxGbikgewogICAgY29uc3QgbW9kZSA9IGxvYy5saXN0aW5nTW9kZSgpOwoKICAgIGlmIChtb2RlID09PSAiZXJyb3IiKSB7CiAgICAgIHJldHVybiBudWxsOwogICAgfSBlbHNlIGlmIChtb2RlID09PSAiZW4iKSB7CiAgICAgIHJldHVybiBlbmdsaXNoRm4obGVuZ3RoKTsKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybiBpbnRsRm4obGVuZ3RoKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIHN1cHBvcnRzRmFzdE51bWJlcnMobG9jKSB7CiAgICBpZiAobG9jLm51bWJlcmluZ1N5c3RlbSAmJiBsb2MubnVtYmVyaW5nU3lzdGVtICE9PSAibGF0biIpIHsKICAgICAgcmV0dXJuIGZhbHNlOwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuICgKICAgICAgICBsb2MubnVtYmVyaW5nU3lzdGVtID09PSAibGF0biIgfHwKICAgICAgICAhbG9jLmxvY2FsZSB8fAogICAgICAgIGxvYy5sb2NhbGUuc3RhcnRzV2l0aCgiZW4iKSB8fAogICAgICAgIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KGxvYy5pbnRsKS5yZXNvbHZlZE9wdGlvbnMoKS5udW1iZXJpbmdTeXN0ZW0gPT09ICJsYXRuIgogICAgICApOwogICAgfQogIH0KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KCiAgY2xhc3MgUG9seU51bWJlckZvcm1hdHRlciB7CiAgICBjb25zdHJ1Y3RvcihpbnRsLCBmb3JjZVNpbXBsZSwgb3B0cykgewogICAgICB0aGlzLnBhZFRvID0gb3B0cy5wYWRUbyB8fCAwOwogICAgICB0aGlzLmZsb29yID0gb3B0cy5mbG9vciB8fCBmYWxzZTsKCiAgICAgIGNvbnN0IHsgcGFkVG8sIGZsb29yLCAuLi5vdGhlck9wdHMgfSA9IG9wdHM7CgogICAgICBpZiAoIWZvcmNlU2ltcGxlIHx8IE9iamVjdC5rZXlzKG90aGVyT3B0cykubGVuZ3RoID4gMCkgewogICAgICAgIGNvbnN0IGludGxPcHRzID0geyB1c2VHcm91cGluZzogZmFsc2UsIC4uLm9wdHMgfTsKICAgICAgICBpZiAob3B0cy5wYWRUbyA+IDApIGludGxPcHRzLm1pbmltdW1JbnRlZ2VyRGlnaXRzID0gb3B0cy5wYWRUbzsKICAgICAgICB0aGlzLmluZiA9IGdldENhY2hlZElORihpbnRsLCBpbnRsT3B0cyk7CiAgICAgIH0KICAgIH0KCiAgICBmb3JtYXQoaSkgewogICAgICBpZiAodGhpcy5pbmYpIHsKICAgICAgICBjb25zdCBmaXhlZCA9IHRoaXMuZmxvb3IgPyBNYXRoLmZsb29yKGkpIDogaTsKICAgICAgICByZXR1cm4gdGhpcy5pbmYuZm9ybWF0KGZpeGVkKTsKICAgICAgfSBlbHNlIHsKICAgICAgICAvLyB0byBtYXRjaCB0aGUgYnJvd3NlcidzIG51bWJlcmZvcm1hdHRlciBkZWZhdWx0cwogICAgICAgIGNvbnN0IGZpeGVkID0gdGhpcy5mbG9vciA/IE1hdGguZmxvb3IoaSkgOiByb3VuZFRvKGksIDMpOwogICAgICAgIHJldHVybiBwYWRTdGFydChmaXhlZCwgdGhpcy5wYWRUbyk7CiAgICAgIH0KICAgIH0KICB9CgogIC8qKgogICAqIEBwcml2YXRlCiAgICovCgogIGNsYXNzIFBvbHlEYXRlRm9ybWF0dGVyIHsKICAgIGNvbnN0cnVjdG9yKGR0LCBpbnRsLCBvcHRzKSB7CiAgICAgIHRoaXMub3B0cyA9IG9wdHM7CiAgICAgIHRoaXMub3JpZ2luYWxab25lID0gdW5kZWZpbmVkOwoKICAgICAgbGV0IHogPSB1bmRlZmluZWQ7CiAgICAgIGlmICh0aGlzLm9wdHMudGltZVpvbmUpIHsKICAgICAgICAvLyBEb24ndCBhcHBseSBhbnkgd29ya2Fyb3VuZHMgaWYgYSB0aW1lWm9uZSBpcyBleHBsaWNpdGx5IHByb3ZpZGVkIGluIG9wdHMKICAgICAgICB0aGlzLmR0ID0gZHQ7CiAgICAgIH0gZWxzZSBpZiAoZHQuem9uZS50eXBlID09PSAiZml4ZWQiKSB7CiAgICAgICAgLy8gVVRDLTggb3IgRXRjL1VUQy04IGFyZSBub3QgcGFydCBvZiB0emRhdGEsIG9ubHkgRXRjL0dNVCs4IGFuZCB0aGUgbGlrZS4KICAgICAgICAvLyBUaGF0IGlzIHdoeSBmaXhlZC1vZmZzZXQgVFogaXMgc2V0IHRvIHRoYXQgdW5sZXNzIGl0IGlzOgogICAgICAgIC8vIDEuIFJlcHJlc2VudGluZyBvZmZzZXQgMCB3aGVuIFVUQyBpcyB1c2VkIHRvIG1haW50YWluIHByZXZpb3VzIGJlaGF2aW9yIGFuZCBkb2VzIG5vdCBiZWNvbWUgR01ULgogICAgICAgIC8vIDIuIFVuc3VwcG9ydGVkIGJ5IHRoZSBicm93c2VyOgogICAgICAgIC8vICAgIC0gc29tZSBkbyBub3Qgc3VwcG9ydCBFdGMvCiAgICAgICAgLy8gICAgLSA8IEV0Yy9HTVQtMTQsID4gRXRjL0dNVCsxMiwgYW5kIDMwLW1pbnV0ZSBvciA0NS1taW51dGUgb2Zmc2V0cyBhcmUgbm90IHBhcnQgb2YgdHpkYXRhCiAgICAgICAgY29uc3QgZ210T2Zmc2V0ID0gLTEgKiAoZHQub2Zmc2V0IC8gNjApOwogICAgICAgIGNvbnN0IG9mZnNldFogPSBnbXRPZmZzZXQgPj0gMCA/IGBFdGMvR01UKyR7Z210T2Zmc2V0fWAgOiBgRXRjL0dNVCR7Z210T2Zmc2V0fWA7CiAgICAgICAgaWYgKGR0Lm9mZnNldCAhPT0gMCAmJiBJQU5BWm9uZS5jcmVhdGUob2Zmc2V0WikudmFsaWQpIHsKICAgICAgICAgIHogPSBvZmZzZXRaOwogICAgICAgICAgdGhpcy5kdCA9IGR0OwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAvLyBOb3QgYWxsIGZpeGVkLW9mZnNldCB6b25lcyBsaWtlIEV0Yy8rNDozMCBhcmUgcHJlc2VudCBpbiB0emRhdGEgc28KICAgICAgICAgIC8vIHdlIG1hbnVhbGx5IGFwcGx5IHRoZSBvZmZzZXQgYW5kIHN1YnN0aXR1dGUgdGhlIHpvbmUgYXMgbmVlZGVkLgogICAgICAgICAgeiA9ICJVVEMiOwogICAgICAgICAgdGhpcy5kdCA9IGR0Lm9mZnNldCA9PT0gMCA/IGR0IDogZHQuc2V0Wm9uZSgiVVRDIikucGx1cyh7IG1pbnV0ZXM6IGR0Lm9mZnNldCB9KTsKICAgICAgICAgIHRoaXMub3JpZ2luYWxab25lID0gZHQuem9uZTsKICAgICAgICB9CiAgICAgIH0gZWxzZSBpZiAoZHQuem9uZS50eXBlID09PSAic3lzdGVtIikgewogICAgICAgIHRoaXMuZHQgPSBkdDsKICAgICAgfSBlbHNlIGlmIChkdC56b25lLnR5cGUgPT09ICJpYW5hIikgewogICAgICAgIHRoaXMuZHQgPSBkdDsKICAgICAgICB6ID0gZHQuem9uZS5uYW1lOwogICAgICB9IGVsc2UgewogICAgICAgIC8vIEN1c3RvbSB6b25lcyBjYW4gaGF2ZSBhbnkgb2Zmc2V0IC8gb2Zmc2V0TmFtZSBzbyB3ZSBqdXN0IG1hbnVhbGx5CiAgICAgICAgLy8gYXBwbHkgdGhlIG9mZnNldCBhbmQgc3Vic3RpdHV0ZSB0aGUgem9uZSBhcyBuZWVkZWQuCiAgICAgICAgeiA9ICJVVEMiOwogICAgICAgIHRoaXMuZHQgPSBkdC5zZXRab25lKCJVVEMiKS5wbHVzKHsgbWludXRlczogZHQub2Zmc2V0IH0pOwogICAgICAgIHRoaXMub3JpZ2luYWxab25lID0gZHQuem9uZTsKICAgICAgfQoKICAgICAgY29uc3QgaW50bE9wdHMgPSB7IC4uLnRoaXMub3B0cyB9OwogICAgICBpbnRsT3B0cy50aW1lWm9uZSA9IGludGxPcHRzLnRpbWVab25lIHx8IHo7CiAgICAgIHRoaXMuZHRmID0gZ2V0Q2FjaGVkRFRGKGludGwsIGludGxPcHRzKTsKICAgIH0KCiAgICBmb3JtYXQoKSB7CiAgICAgIGlmICh0aGlzLm9yaWdpbmFsWm9uZSkgewogICAgICAgIC8vIElmIHdlIGhhdmUgdG8gc3Vic3RpdHV0ZSBpbiB0aGUgYWN0dWFsIHpvbmUgbmFtZSwgd2UgaGF2ZSB0byB1c2UKICAgICAgICAvLyBmb3JtYXRUb1BhcnRzIHNvIHRoYXQgdGhlIHRpbWV6b25lIGNhbiBiZSByZXBsYWNlZC4KICAgICAgICByZXR1cm4gdGhpcy5mb3JtYXRUb1BhcnRzKCkKICAgICAgICAgIC5tYXAoKHsgdmFsdWUgfSkgPT4gdmFsdWUpCiAgICAgICAgICAuam9pbigiIik7CiAgICAgIH0KICAgICAgcmV0dXJuIHRoaXMuZHRmLmZvcm1hdCh0aGlzLmR0LnRvSlNEYXRlKCkpOwogICAgfQoKICAgIGZvcm1hdFRvUGFydHMoKSB7CiAgICAgIGNvbnN0IHBhcnRzID0gdGhpcy5kdGYuZm9ybWF0VG9QYXJ0cyh0aGlzLmR0LnRvSlNEYXRlKCkpOwogICAgICBpZiAodGhpcy5vcmlnaW5hbFpvbmUpIHsKICAgICAgICByZXR1cm4gcGFydHMubWFwKChwYXJ0KSA9PiB7CiAgICAgICAgICBpZiAocGFydC50eXBlID09PSAidGltZVpvbmVOYW1lIikgewogICAgICAgICAgICBjb25zdCBvZmZzZXROYW1lID0gdGhpcy5vcmlnaW5hbFpvbmUub2Zmc2V0TmFtZSh0aGlzLmR0LnRzLCB7CiAgICAgICAgICAgICAgbG9jYWxlOiB0aGlzLmR0LmxvY2FsZSwKICAgICAgICAgICAgICBmb3JtYXQ6IHRoaXMub3B0cy50aW1lWm9uZU5hbWUsCiAgICAgICAgICAgIH0pOwogICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgIC4uLnBhcnQsCiAgICAgICAgICAgICAgdmFsdWU6IG9mZnNldE5hbWUsCiAgICAgICAgICAgIH07CiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICByZXR1cm4gcGFydDsKICAgICAgICAgIH0KICAgICAgICB9KTsKICAgICAgfQogICAgICByZXR1cm4gcGFydHM7CiAgICB9CgogICAgcmVzb2x2ZWRPcHRpb25zKCkgewogICAgICByZXR1cm4gdGhpcy5kdGYucmVzb2x2ZWRPcHRpb25zKCk7CiAgICB9CiAgfQoKICAvKioKICAgKiBAcHJpdmF0ZQogICAqLwogIGNsYXNzIFBvbHlSZWxGb3JtYXR0ZXIgewogICAgY29uc3RydWN0b3IoaW50bCwgaXNFbmdsaXNoLCBvcHRzKSB7CiAgICAgIHRoaXMub3B0cyA9IHsgc3R5bGU6ICJsb25nIiwgLi4ub3B0cyB9OwogICAgICBpZiAoIWlzRW5nbGlzaCAmJiBoYXNSZWxhdGl2ZSgpKSB7CiAgICAgICAgdGhpcy5ydGYgPSBnZXRDYWNoZWRSVEYoaW50bCwgb3B0cyk7CiAgICAgIH0KICAgIH0KCiAgICBmb3JtYXQoY291bnQsIHVuaXQpIHsKICAgICAgaWYgKHRoaXMucnRmKSB7CiAgICAgICAgcmV0dXJuIHRoaXMucnRmLmZvcm1hdChjb3VudCwgdW5pdCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIGZvcm1hdFJlbGF0aXZlVGltZSh1bml0LCBjb3VudCwgdGhpcy5vcHRzLm51bWVyaWMsIHRoaXMub3B0cy5zdHlsZSAhPT0gImxvbmciKTsKICAgICAgfQogICAgfQoKICAgIGZvcm1hdFRvUGFydHMoY291bnQsIHVuaXQpIHsKICAgICAgaWYgKHRoaXMucnRmKSB7CiAgICAgICAgcmV0dXJuIHRoaXMucnRmLmZvcm1hdFRvUGFydHMoY291bnQsIHVuaXQpOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBbXTsKICAgICAgfQogICAgfQogIH0KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KCiAgY2xhc3MgTG9jYWxlIHsKICAgIHN0YXRpYyBmcm9tT3B0cyhvcHRzKSB7CiAgICAgIHJldHVybiBMb2NhbGUuY3JlYXRlKG9wdHMubG9jYWxlLCBvcHRzLm51bWJlcmluZ1N5c3RlbSwgb3B0cy5vdXRwdXRDYWxlbmRhciwgb3B0cy5kZWZhdWx0VG9FTik7CiAgICB9CgogICAgc3RhdGljIGNyZWF0ZShsb2NhbGUsIG51bWJlcmluZ1N5c3RlbSwgb3V0cHV0Q2FsZW5kYXIsIGRlZmF1bHRUb0VOID0gZmFsc2UpIHsKICAgICAgY29uc3Qgc3BlY2lmaWVkTG9jYWxlID0gbG9jYWxlIHx8IFNldHRpbmdzLmRlZmF1bHRMb2NhbGU7CiAgICAgIC8vIHRoZSBzeXN0ZW0gbG9jYWxlIGlzIHVzZWZ1bCBmb3IgaHVtYW4gcmVhZGFibGUgc3RyaW5ncyBidXQgYW5ub3lpbmcgZm9yIHBhcnNpbmcvZm9ybWF0dGluZyBrbm93biBmb3JtYXRzCiAgICAgIGNvbnN0IGxvY2FsZVIgPSBzcGVjaWZpZWRMb2NhbGUgfHwgKGRlZmF1bHRUb0VOID8gImVuLVVTIiA6IHN5c3RlbUxvY2FsZSgpKTsKICAgICAgY29uc3QgbnVtYmVyaW5nU3lzdGVtUiA9IG51bWJlcmluZ1N5c3RlbSB8fCBTZXR0aW5ncy5kZWZhdWx0TnVtYmVyaW5nU3lzdGVtOwogICAgICBjb25zdCBvdXRwdXRDYWxlbmRhclIgPSBvdXRwdXRDYWxlbmRhciB8fCBTZXR0aW5ncy5kZWZhdWx0T3V0cHV0Q2FsZW5kYXI7CiAgICAgIHJldHVybiBuZXcgTG9jYWxlKGxvY2FsZVIsIG51bWJlcmluZ1N5c3RlbVIsIG91dHB1dENhbGVuZGFyUiwgc3BlY2lmaWVkTG9jYWxlKTsKICAgIH0KCiAgICBzdGF0aWMgcmVzZXRDYWNoZSgpIHsKICAgICAgc3lzTG9jYWxlQ2FjaGUgPSBudWxsOwogICAgICBpbnRsRFRDYWNoZSA9IHt9OwogICAgICBpbnRsTnVtQ2FjaGUgPSB7fTsKICAgICAgaW50bFJlbENhY2hlID0ge307CiAgICB9CgogICAgc3RhdGljIGZyb21PYmplY3QoeyBsb2NhbGUsIG51bWJlcmluZ1N5c3RlbSwgb3V0cHV0Q2FsZW5kYXIgfSA9IHt9KSB7CiAgICAgIHJldHVybiBMb2NhbGUuY3JlYXRlKGxvY2FsZSwgbnVtYmVyaW5nU3lzdGVtLCBvdXRwdXRDYWxlbmRhcik7CiAgICB9CgogICAgY29uc3RydWN0b3IobG9jYWxlLCBudW1iZXJpbmcsIG91dHB1dENhbGVuZGFyLCBzcGVjaWZpZWRMb2NhbGUpIHsKICAgICAgY29uc3QgW3BhcnNlZExvY2FsZSwgcGFyc2VkTnVtYmVyaW5nU3lzdGVtLCBwYXJzZWRPdXRwdXRDYWxlbmRhcl0gPSBwYXJzZUxvY2FsZVN0cmluZyhsb2NhbGUpOwoKICAgICAgdGhpcy5sb2NhbGUgPSBwYXJzZWRMb2NhbGU7CiAgICAgIHRoaXMubnVtYmVyaW5nU3lzdGVtID0gbnVtYmVyaW5nIHx8IHBhcnNlZE51bWJlcmluZ1N5c3RlbSB8fCBudWxsOwogICAgICB0aGlzLm91dHB1dENhbGVuZGFyID0gb3V0cHV0Q2FsZW5kYXIgfHwgcGFyc2VkT3V0cHV0Q2FsZW5kYXIgfHwgbnVsbDsKICAgICAgdGhpcy5pbnRsID0gaW50bENvbmZpZ1N0cmluZyh0aGlzLmxvY2FsZSwgdGhpcy5udW1iZXJpbmdTeXN0ZW0sIHRoaXMub3V0cHV0Q2FsZW5kYXIpOwoKICAgICAgdGhpcy53ZWVrZGF5c0NhY2hlID0geyBmb3JtYXQ6IHt9LCBzdGFuZGFsb25lOiB7fSB9OwogICAgICB0aGlzLm1vbnRoc0NhY2hlID0geyBmb3JtYXQ6IHt9LCBzdGFuZGFsb25lOiB7fSB9OwogICAgICB0aGlzLm1lcmlkaWVtQ2FjaGUgPSBudWxsOwogICAgICB0aGlzLmVyYUNhY2hlID0ge307CgogICAgICB0aGlzLnNwZWNpZmllZExvY2FsZSA9IHNwZWNpZmllZExvY2FsZTsKICAgICAgdGhpcy5mYXN0TnVtYmVyc0NhY2hlZCA9IG51bGw7CiAgICB9CgogICAgZ2V0IGZhc3ROdW1iZXJzKCkgewogICAgICBpZiAodGhpcy5mYXN0TnVtYmVyc0NhY2hlZCA9PSBudWxsKSB7CiAgICAgICAgdGhpcy5mYXN0TnVtYmVyc0NhY2hlZCA9IHN1cHBvcnRzRmFzdE51bWJlcnModGhpcyk7CiAgICAgIH0KCiAgICAgIHJldHVybiB0aGlzLmZhc3ROdW1iZXJzQ2FjaGVkOwogICAgfQoKICAgIGxpc3RpbmdNb2RlKCkgewogICAgICBjb25zdCBpc0FjdHVhbGx5RW4gPSB0aGlzLmlzRW5nbGlzaCgpOwogICAgICBjb25zdCBoYXNOb1dlaXJkbmVzcyA9CiAgICAgICAgKHRoaXMubnVtYmVyaW5nU3lzdGVtID09PSBudWxsIHx8IHRoaXMubnVtYmVyaW5nU3lzdGVtID09PSAibGF0biIpICYmCiAgICAgICAgKHRoaXMub3V0cHV0Q2FsZW5kYXIgPT09IG51bGwgfHwgdGhpcy5vdXRwdXRDYWxlbmRhciA9PT0gImdyZWdvcnkiKTsKICAgICAgcmV0dXJuIGlzQWN0dWFsbHlFbiAmJiBoYXNOb1dlaXJkbmVzcyA/ICJlbiIgOiAiaW50bCI7CiAgICB9CgogICAgY2xvbmUoYWx0cykgewogICAgICBpZiAoIWFsdHMgfHwgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMoYWx0cykubGVuZ3RoID09PSAwKSB7CiAgICAgICAgcmV0dXJuIHRoaXM7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIExvY2FsZS5jcmVhdGUoCiAgICAgICAgICBhbHRzLmxvY2FsZSB8fCB0aGlzLnNwZWNpZmllZExvY2FsZSwKICAgICAgICAgIGFsdHMubnVtYmVyaW5nU3lzdGVtIHx8IHRoaXMubnVtYmVyaW5nU3lzdGVtLAogICAgICAgICAgYWx0cy5vdXRwdXRDYWxlbmRhciB8fCB0aGlzLm91dHB1dENhbGVuZGFyLAogICAgICAgICAgYWx0cy5kZWZhdWx0VG9FTiB8fCBmYWxzZQogICAgICAgICk7CiAgICAgIH0KICAgIH0KCiAgICByZWRlZmF1bHRUb0VOKGFsdHMgPSB7fSkgewogICAgICByZXR1cm4gdGhpcy5jbG9uZSh7IC4uLmFsdHMsIGRlZmF1bHRUb0VOOiB0cnVlIH0pOwogICAgfQoKICAgIHJlZGVmYXVsdFRvU3lzdGVtKGFsdHMgPSB7fSkgewogICAgICByZXR1cm4gdGhpcy5jbG9uZSh7IC4uLmFsdHMsIGRlZmF1bHRUb0VOOiBmYWxzZSB9KTsKICAgIH0KCiAgICBtb250aHMobGVuZ3RoLCBmb3JtYXQgPSBmYWxzZSkgewogICAgICByZXR1cm4gbGlzdFN0dWZmKHRoaXMsIGxlbmd0aCwgbW9udGhzLCAoKSA9PiB7CiAgICAgICAgY29uc3QgaW50bCA9IGZvcm1hdCA/IHsgbW9udGg6IGxlbmd0aCwgZGF5OiAibnVtZXJpYyIgfSA6IHsgbW9udGg6IGxlbmd0aCB9LAogICAgICAgICAgZm9ybWF0U3RyID0gZm9ybWF0ID8gImZvcm1hdCIgOiAic3RhbmRhbG9uZSI7CiAgICAgICAgaWYgKCF0aGlzLm1vbnRoc0NhY2hlW2Zvcm1hdFN0cl1bbGVuZ3RoXSkgewogICAgICAgICAgdGhpcy5tb250aHNDYWNoZVtmb3JtYXRTdHJdW2xlbmd0aF0gPSBtYXBNb250aHMoKGR0KSA9PiB0aGlzLmV4dHJhY3QoZHQsIGludGwsICJtb250aCIpKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIHRoaXMubW9udGhzQ2FjaGVbZm9ybWF0U3RyXVtsZW5ndGhdOwogICAgICB9KTsKICAgIH0KCiAgICB3ZWVrZGF5cyhsZW5ndGgsIGZvcm1hdCA9IGZhbHNlKSB7CiAgICAgIHJldHVybiBsaXN0U3R1ZmYodGhpcywgbGVuZ3RoLCB3ZWVrZGF5cywgKCkgPT4gewogICAgICAgIGNvbnN0IGludGwgPSBmb3JtYXQKICAgICAgICAgICAgPyB7IHdlZWtkYXk6IGxlbmd0aCwgeWVhcjogIm51bWVyaWMiLCBtb250aDogImxvbmciLCBkYXk6ICJudW1lcmljIiB9CiAgICAgICAgICAgIDogeyB3ZWVrZGF5OiBsZW5ndGggfSwKICAgICAgICAgIGZvcm1hdFN0ciA9IGZvcm1hdCA/ICJmb3JtYXQiIDogInN0YW5kYWxvbmUiOwogICAgICAgIGlmICghdGhpcy53ZWVrZGF5c0NhY2hlW2Zvcm1hdFN0cl1bbGVuZ3RoXSkgewogICAgICAgICAgdGhpcy53ZWVrZGF5c0NhY2hlW2Zvcm1hdFN0cl1bbGVuZ3RoXSA9IG1hcFdlZWtkYXlzKChkdCkgPT4KICAgICAgICAgICAgdGhpcy5leHRyYWN0KGR0LCBpbnRsLCAid2Vla2RheSIpCiAgICAgICAgICApOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdGhpcy53ZWVrZGF5c0NhY2hlW2Zvcm1hdFN0cl1bbGVuZ3RoXTsKICAgICAgfSk7CiAgICB9CgogICAgbWVyaWRpZW1zKCkgewogICAgICByZXR1cm4gbGlzdFN0dWZmKAogICAgICAgIHRoaXMsCiAgICAgICAgdW5kZWZpbmVkLAogICAgICAgICgpID0+IG1lcmlkaWVtcywKICAgICAgICAoKSA9PiB7CiAgICAgICAgICAvLyBJbiB0aGVvcnkgdGhlcmUgY291bGQgYmUgYXJpYml0cmFyeSBkYXkgcGVyaW9kcy4gV2UncmUgZ29ubmEgYXNzdW1lIHRoZXJlIGFyZSBleGFjdGx5IHR3bwogICAgICAgICAgLy8gZm9yIEFNIGFuZCBQTS4gVGhpcyBpcyBwcm9iYWJseSB3cm9uZywgYnV0IGl0J3MgbWFrZXMgcGFyc2luZyB3YXkgZWFzaWVyLgogICAgICAgICAgaWYgKCF0aGlzLm1lcmlkaWVtQ2FjaGUpIHsKICAgICAgICAgICAgY29uc3QgaW50bCA9IHsgaG91cjogIm51bWVyaWMiLCBob3VyQ3ljbGU6ICJoMTIiIH07CiAgICAgICAgICAgIHRoaXMubWVyaWRpZW1DYWNoZSA9IFtEYXRlVGltZS51dGMoMjAxNiwgMTEsIDEzLCA5KSwgRGF0ZVRpbWUudXRjKDIwMTYsIDExLCAxMywgMTkpXS5tYXAoCiAgICAgICAgICAgICAgKGR0KSA9PiB0aGlzLmV4dHJhY3QoZHQsIGludGwsICJkYXlwZXJpb2QiKQogICAgICAgICAgICApOwogICAgICAgICAgfQoKICAgICAgICAgIHJldHVybiB0aGlzLm1lcmlkaWVtQ2FjaGU7CiAgICAgICAgfQogICAgICApOwogICAgfQoKICAgIGVyYXMobGVuZ3RoKSB7CiAgICAgIHJldHVybiBsaXN0U3R1ZmYodGhpcywgbGVuZ3RoLCBlcmFzLCAoKSA9PiB7CiAgICAgICAgY29uc3QgaW50bCA9IHsgZXJhOiBsZW5ndGggfTsKCiAgICAgICAgLy8gVGhpcyBpcyBwcm9ibGVtYXRpYy4gRGlmZmVyZW50IGNhbGVuZGFycyBhcmUgZ29pbmcgdG8gZGVmaW5lIGVyYXMgdG90YWxseSBkaWZmZXJlbnRseS4gV2hhdCBJIG5lZWQgaXMgdGhlIG1pbmltdW0gc2V0IG9mIGRhdGVzCiAgICAgICAgLy8gdG8gZGVmaW5pdGVseSBlbnVtZXJhdGUgdGhlbS4KICAgICAgICBpZiAoIXRoaXMuZXJhQ2FjaGVbbGVuZ3RoXSkgewogICAgICAgICAgdGhpcy5lcmFDYWNoZVtsZW5ndGhdID0gW0RhdGVUaW1lLnV0YygtNDAsIDEsIDEpLCBEYXRlVGltZS51dGMoMjAxNywgMSwgMSldLm1hcCgoZHQpID0+CiAgICAgICAgICAgIHRoaXMuZXh0cmFjdChkdCwgaW50bCwgImVyYSIpCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgcmV0dXJuIHRoaXMuZXJhQ2FjaGVbbGVuZ3RoXTsKICAgICAgfSk7CiAgICB9CgogICAgZXh0cmFjdChkdCwgaW50bE9wdHMsIGZpZWxkKSB7CiAgICAgIGNvbnN0IGRmID0gdGhpcy5kdEZvcm1hdHRlcihkdCwgaW50bE9wdHMpLAogICAgICAgIHJlc3VsdHMgPSBkZi5mb3JtYXRUb1BhcnRzKCksCiAgICAgICAgbWF0Y2hpbmcgPSByZXN1bHRzLmZpbmQoKG0pID0+IG0udHlwZS50b0xvd2VyQ2FzZSgpID09PSBmaWVsZCk7CiAgICAgIHJldHVybiBtYXRjaGluZyA/IG1hdGNoaW5nLnZhbHVlIDogbnVsbDsKICAgIH0KCiAgICBudW1iZXJGb3JtYXR0ZXIob3B0cyA9IHt9KSB7CiAgICAgIC8vIHRoaXMgZm9yY2VzaW1wbGUgb3B0aW9uIGlzIG5ldmVyIHVzZWQgKHRoZSBvbmx5IGNhbGxlciBzaG9ydC1jaXJjdWl0cyBvbiBpdCwgYnV0IGl0IHNlZW1zIHNhZmVyIHRvIGxlYXZlKQogICAgICAvLyAoaW4gY29udHJhc3QsIHRoZSByZXN0IG9mIHRoZSBjb25kaXRpb24gaXMgdXNlZCBoZWF2aWx5KQogICAgICByZXR1cm4gbmV3IFBvbHlOdW1iZXJGb3JtYXR0ZXIodGhpcy5pbnRsLCBvcHRzLmZvcmNlU2ltcGxlIHx8IHRoaXMuZmFzdE51bWJlcnMsIG9wdHMpOwogICAgfQoKICAgIGR0Rm9ybWF0dGVyKGR0LCBpbnRsT3B0cyA9IHt9KSB7CiAgICAgIHJldHVybiBuZXcgUG9seURhdGVGb3JtYXR0ZXIoZHQsIHRoaXMuaW50bCwgaW50bE9wdHMpOwogICAgfQoKICAgIHJlbEZvcm1hdHRlcihvcHRzID0ge30pIHsKICAgICAgcmV0dXJuIG5ldyBQb2x5UmVsRm9ybWF0dGVyKHRoaXMuaW50bCwgdGhpcy5pc0VuZ2xpc2goKSwgb3B0cyk7CiAgICB9CgogICAgbGlzdEZvcm1hdHRlcihvcHRzID0ge30pIHsKICAgICAgcmV0dXJuIGdldENhY2hlZExGKHRoaXMuaW50bCwgb3B0cyk7CiAgICB9CgogICAgaXNFbmdsaXNoKCkgewogICAgICByZXR1cm4gKAogICAgICAgIHRoaXMubG9jYWxlID09PSAiZW4iIHx8CiAgICAgICAgdGhpcy5sb2NhbGUudG9Mb3dlckNhc2UoKSA9PT0gImVuLXVzIiB8fAogICAgICAgIG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KHRoaXMuaW50bCkucmVzb2x2ZWRPcHRpb25zKCkubG9jYWxlLnN0YXJ0c1dpdGgoImVuLXVzIikKICAgICAgKTsKICAgIH0KCiAgICBlcXVhbHMob3RoZXIpIHsKICAgICAgcmV0dXJuICgKICAgICAgICB0aGlzLmxvY2FsZSA9PT0gb3RoZXIubG9jYWxlICYmCiAgICAgICAgdGhpcy5udW1iZXJpbmdTeXN0ZW0gPT09IG90aGVyLm51bWJlcmluZ1N5c3RlbSAmJgogICAgICAgIHRoaXMub3V0cHV0Q2FsZW5kYXIgPT09IG90aGVyLm91dHB1dENhbGVuZGFyCiAgICAgICk7CiAgICB9CiAgfQoKICBsZXQgc2luZ2xldG9uID0gbnVsbDsKCiAgLyoqCiAgICogQSB6b25lIHdpdGggYSBmaXhlZCBvZmZzZXQgKG1lYW5pbmcgbm8gRFNUKQogICAqIEBpbXBsZW1lbnRzIHtab25lfQogICAqLwogIGNsYXNzIEZpeGVkT2Zmc2V0Wm9uZSBleHRlbmRzIFpvbmUgewogICAgLyoqCiAgICAgKiBHZXQgYSBzaW5nbGV0b24gaW5zdGFuY2Ugb2YgVVRDCiAgICAgKiBAcmV0dXJuIHtGaXhlZE9mZnNldFpvbmV9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgdXRjSW5zdGFuY2UoKSB7CiAgICAgIGlmIChzaW5nbGV0b24gPT09IG51bGwpIHsKICAgICAgICBzaW5nbGV0b24gPSBuZXcgRml4ZWRPZmZzZXRab25lKDApOwogICAgICB9CiAgICAgIHJldHVybiBzaW5nbGV0b247CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgYW4gaW5zdGFuY2Ugd2l0aCBhIHNwZWNpZmllZCBvZmZzZXQKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvZmZzZXQgLSBUaGUgb2Zmc2V0IGluIG1pbnV0ZXMKICAgICAqIEByZXR1cm4ge0ZpeGVkT2Zmc2V0Wm9uZX0KICAgICAqLwogICAgc3RhdGljIGluc3RhbmNlKG9mZnNldCkgewogICAgICByZXR1cm4gb2Zmc2V0ID09PSAwID8gRml4ZWRPZmZzZXRab25lLnV0Y0luc3RhbmNlIDogbmV3IEZpeGVkT2Zmc2V0Wm9uZShvZmZzZXQpOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IGFuIGluc3RhbmNlIG9mIEZpeGVkT2Zmc2V0Wm9uZSBmcm9tIGEgVVRDIG9mZnNldCBzdHJpbmcsIGxpa2UgIlVUQys2IgogICAgICogQHBhcmFtIHtzdHJpbmd9IHMgLSBUaGUgb2Zmc2V0IHN0cmluZyB0byBwYXJzZQogICAgICogQGV4YW1wbGUgRml4ZWRPZmZzZXRab25lLnBhcnNlU3BlY2lmaWVyKCJVVEMrNiIpCiAgICAgKiBAZXhhbXBsZSBGaXhlZE9mZnNldFpvbmUucGFyc2VTcGVjaWZpZXIoIlVUQyswNiIpCiAgICAgKiBAZXhhbXBsZSBGaXhlZE9mZnNldFpvbmUucGFyc2VTcGVjaWZpZXIoIlVUQy02OjAwIikKICAgICAqIEByZXR1cm4ge0ZpeGVkT2Zmc2V0Wm9uZX0KICAgICAqLwogICAgc3RhdGljIHBhcnNlU3BlY2lmaWVyKHMpIHsKICAgICAgaWYgKHMpIHsKICAgICAgICBjb25zdCByID0gcy5tYXRjaCgvXnV0Yyg/OihbKy1dXGR7MSwyfSkoPzo6KFxkezJ9KSk/KT8kL2kpOwogICAgICAgIGlmIChyKSB7CiAgICAgICAgICByZXR1cm4gbmV3IEZpeGVkT2Zmc2V0Wm9uZShzaWduZWRPZmZzZXQoclsxXSwgclsyXSkpOwogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICBjb25zdHJ1Y3RvcihvZmZzZXQpIHsKICAgICAgc3VwZXIoKTsKICAgICAgLyoqIEBwcml2YXRlICoqLwogICAgICB0aGlzLmZpeGVkID0gb2Zmc2V0OwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBnZXQgdHlwZSgpIHsKICAgICAgcmV0dXJuICJmaXhlZCI7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCBuYW1lKCkgewogICAgICByZXR1cm4gdGhpcy5maXhlZCA9PT0gMCA/ICJVVEMiIDogYFVUQyR7Zm9ybWF0T2Zmc2V0KHRoaXMuZml4ZWQsICJuYXJyb3ciKX1gOwogICAgfQoKICAgIGdldCBpYW5hTmFtZSgpIHsKICAgICAgaWYgKHRoaXMuZml4ZWQgPT09IDApIHsKICAgICAgICByZXR1cm4gIkV0Yy9VVEMiOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBgRXRjL0dNVCR7Zm9ybWF0T2Zmc2V0KC10aGlzLmZpeGVkLCAibmFycm93Iil9YDsKICAgICAgfQogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBvZmZzZXROYW1lKCkgewogICAgICByZXR1cm4gdGhpcy5uYW1lOwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBmb3JtYXRPZmZzZXQodHMsIGZvcm1hdCkgewogICAgICByZXR1cm4gZm9ybWF0T2Zmc2V0KHRoaXMuZml4ZWQsIGZvcm1hdCk7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCBpc1VuaXZlcnNhbCgpIHsKICAgICAgcmV0dXJuIHRydWU7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIG9mZnNldCgpIHsKICAgICAgcmV0dXJuIHRoaXMuZml4ZWQ7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGVxdWFscyhvdGhlclpvbmUpIHsKICAgICAgcmV0dXJuIG90aGVyWm9uZS50eXBlID09PSAiZml4ZWQiICYmIG90aGVyWm9uZS5maXhlZCA9PT0gdGhpcy5maXhlZDsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZ2V0IGlzVmFsaWQoKSB7CiAgICAgIHJldHVybiB0cnVlOwogICAgfQogIH0KCiAgLyoqCiAgICogQSB6b25lIHRoYXQgZmFpbGVkIHRvIHBhcnNlLiBZb3Ugc2hvdWxkIG5ldmVyIG5lZWQgdG8gaW5zdGFudGlhdGUgdGhpcy4KICAgKiBAaW1wbGVtZW50cyB7Wm9uZX0KICAgKi8KICBjbGFzcyBJbnZhbGlkWm9uZSBleHRlbmRzIFpvbmUgewogICAgY29uc3RydWN0b3Ioem9uZU5hbWUpIHsKICAgICAgc3VwZXIoKTsKICAgICAgLyoqICBAcHJpdmF0ZSAqLwogICAgICB0aGlzLnpvbmVOYW1lID0gem9uZU5hbWU7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCB0eXBlKCkgewogICAgICByZXR1cm4gImludmFsaWQiOwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBnZXQgbmFtZSgpIHsKICAgICAgcmV0dXJuIHRoaXMuem9uZU5hbWU7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCBpc1VuaXZlcnNhbCgpIHsKICAgICAgcmV0dXJuIGZhbHNlOwogICAgfQoKICAgIC8qKiBAb3ZlcnJpZGUgKiovCiAgICBvZmZzZXROYW1lKCkgewogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZm9ybWF0T2Zmc2V0KCkgewogICAgICByZXR1cm4gIiI7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIG9mZnNldCgpIHsKICAgICAgcmV0dXJuIE5hTjsKICAgIH0KCiAgICAvKiogQG92ZXJyaWRlICoqLwogICAgZXF1YWxzKCkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CgogICAgLyoqIEBvdmVycmlkZSAqKi8KICAgIGdldCBpc1ZhbGlkKCkgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgfQoKICAvKioKICAgKiBAcHJpdmF0ZQogICAqLwoKICBmdW5jdGlvbiBub3JtYWxpemVab25lKGlucHV0LCBkZWZhdWx0Wm9uZSkgewogICAgaWYgKGlzVW5kZWZpbmVkKGlucHV0KSB8fCBpbnB1dCA9PT0gbnVsbCkgewogICAgICByZXR1cm4gZGVmYXVsdFpvbmU7CiAgICB9IGVsc2UgaWYgKGlucHV0IGluc3RhbmNlb2YgWm9uZSkgewogICAgICByZXR1cm4gaW5wdXQ7CiAgICB9IGVsc2UgaWYgKGlzU3RyaW5nKGlucHV0KSkgewogICAgICBjb25zdCBsb3dlcmVkID0gaW5wdXQudG9Mb3dlckNhc2UoKTsKICAgICAgaWYgKGxvd2VyZWQgPT09ICJkZWZhdWx0IikgcmV0dXJuIGRlZmF1bHRab25lOwogICAgICBlbHNlIGlmIChsb3dlcmVkID09PSAibG9jYWwiIHx8IGxvd2VyZWQgPT09ICJzeXN0ZW0iKSByZXR1cm4gU3lzdGVtWm9uZS5pbnN0YW5jZTsKICAgICAgZWxzZSBpZiAobG93ZXJlZCA9PT0gInV0YyIgfHwgbG93ZXJlZCA9PT0gImdtdCIpIHJldHVybiBGaXhlZE9mZnNldFpvbmUudXRjSW5zdGFuY2U7CiAgICAgIGVsc2UgcmV0dXJuIEZpeGVkT2Zmc2V0Wm9uZS5wYXJzZVNwZWNpZmllcihsb3dlcmVkKSB8fCBJQU5BWm9uZS5jcmVhdGUoaW5wdXQpOwogICAgfSBlbHNlIGlmIChpc051bWJlcihpbnB1dCkpIHsKICAgICAgcmV0dXJuIEZpeGVkT2Zmc2V0Wm9uZS5pbnN0YW5jZShpbnB1dCk7CiAgICB9IGVsc2UgaWYgKHR5cGVvZiBpbnB1dCA9PT0gIm9iamVjdCIgJiYgIm9mZnNldCIgaW4gaW5wdXQgJiYgdHlwZW9mIGlucHV0Lm9mZnNldCA9PT0gImZ1bmN0aW9uIikgewogICAgICAvLyBUaGlzIGlzIGR1bWIsIGJ1dCB0aGUgaW5zdGFuY2VvZiBjaGVjayBhYm92ZSBkb2Vzbid0IHNlZW0gdG8gcmVhbGx5IHdvcmsKICAgICAgLy8gc28gd2UncmUgZHVjayBjaGVja2luZyBpdAogICAgICByZXR1cm4gaW5wdXQ7CiAgICB9IGVsc2UgewogICAgICByZXR1cm4gbmV3IEludmFsaWRab25lKGlucHV0KTsKICAgIH0KICB9CgogIGxldCBub3cgPSAoKSA9PiBEYXRlLm5vdygpLAogICAgZGVmYXVsdFpvbmUgPSAic3lzdGVtIiwKICAgIGRlZmF1bHRMb2NhbGUgPSBudWxsLAogICAgZGVmYXVsdE51bWJlcmluZ1N5c3RlbSA9IG51bGwsCiAgICBkZWZhdWx0T3V0cHV0Q2FsZW5kYXIgPSBudWxsLAogICAgdHdvRGlnaXRDdXRvZmZZZWFyID0gNjAsCiAgICB0aHJvd09uSW52YWxpZDsKCiAgLyoqCiAgICogU2V0dGluZ3MgY29udGFpbnMgc3RhdGljIGdldHRlcnMgYW5kIHNldHRlcnMgdGhhdCBjb250cm9sIEx1eG9uJ3Mgb3ZlcmFsbCBiZWhhdmlvci4gTHV4b24gaXMgYSBzaW1wbGUgbGlicmFyeSB3aXRoIGZldyBvcHRpb25zLCBidXQgdGhlIG9uZXMgaXQgZG9lcyBoYXZlIGxpdmUgaGVyZS4KICAgKi8KICBjbGFzcyBTZXR0aW5ncyB7CiAgICAvKioKICAgICAqIEdldCB0aGUgY2FsbGJhY2sgZm9yIHJldHVybmluZyB0aGUgY3VycmVudCB0aW1lc3RhbXAuCiAgICAgKiBAdHlwZSB7ZnVuY3Rpb259CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgbm93KCkgewogICAgICByZXR1cm4gbm93OwogICAgfQoKICAgIC8qKgogICAgICogU2V0IHRoZSBjYWxsYmFjayBmb3IgcmV0dXJuaW5nIHRoZSBjdXJyZW50IHRpbWVzdGFtcC4KICAgICAqIFRoZSBmdW5jdGlvbiBzaG91bGQgcmV0dXJuIGEgbnVtYmVyLCB3aGljaCB3aWxsIGJlIGludGVycHJldGVkIGFzIGFuIEVwb2NoIG1pbGxpc2Vjb25kIGNvdW50CiAgICAgKiBAdHlwZSB7ZnVuY3Rpb259CiAgICAgKiBAZXhhbXBsZSBTZXR0aW5ncy5ub3cgPSAoKSA9PiBEYXRlLm5vdygpICsgMzAwMCAvLyBwcmV0ZW5kIGl0IGlzIDMgc2Vjb25kcyBpbiB0aGUgZnV0dXJlCiAgICAgKiBAZXhhbXBsZSBTZXR0aW5ncy5ub3cgPSAoKSA9PiAwIC8vIGFsd2F5cyBwcmV0ZW5kIGl0J3MgSmFuIDEsIDE5NzAgYXQgbWlkbmlnaHQgaW4gVVRDIHRpbWUKICAgICAqLwogICAgc3RhdGljIHNldCBub3cobikgewogICAgICBub3cgPSBuOwogICAgfQoKICAgIC8qKgogICAgICogU2V0IHRoZSBkZWZhdWx0IHRpbWUgem9uZSB0byBjcmVhdGUgRGF0ZVRpbWVzIGluLiBEb2VzIG5vdCBhZmZlY3QgZXhpc3RpbmcgaW5zdGFuY2VzLgogICAgICogVXNlIHRoZSB2YWx1ZSAic3lzdGVtIiB0byByZXNldCB0aGlzIHZhbHVlIHRvIHRoZSBzeXN0ZW0ncyB0aW1lIHpvbmUuCiAgICAgKiBAdHlwZSB7c3RyaW5nfQogICAgICovCiAgICBzdGF0aWMgc2V0IGRlZmF1bHRab25lKHpvbmUpIHsKICAgICAgZGVmYXVsdFpvbmUgPSB6b25lOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBkZWZhdWx0IHRpbWUgem9uZSBvYmplY3QgY3VycmVudGx5IHVzZWQgdG8gY3JlYXRlIERhdGVUaW1lcy4gRG9lcyBub3QgYWZmZWN0IGV4aXN0aW5nIGluc3RhbmNlcy4KICAgICAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRoZSBzeXN0ZW0ncyB0aW1lIHpvbmUgKHRoZSBvbmUgc2V0IG9uIHRoZSBtYWNoaW5lIHRoYXQgcnVucyB0aGlzIGNvZGUpLgogICAgICogQHR5cGUge1pvbmV9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgZGVmYXVsdFpvbmUoKSB7CiAgICAgIHJldHVybiBub3JtYWxpemVab25lKGRlZmF1bHRab25lLCBTeXN0ZW1ab25lLmluc3RhbmNlKTsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgZGVmYXVsdCBsb2NhbGUgdG8gY3JlYXRlIERhdGVUaW1lcyB3aXRoLiBEb2VzIG5vdCBhZmZlY3QgZXhpc3RpbmcgaW5zdGFuY2VzLgogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgc3RhdGljIGdldCBkZWZhdWx0TG9jYWxlKCkgewogICAgICByZXR1cm4gZGVmYXVsdExvY2FsZTsKICAgIH0KCiAgICAvKioKICAgICAqIFNldCB0aGUgZGVmYXVsdCBsb2NhbGUgdG8gY3JlYXRlIERhdGVUaW1lcyB3aXRoLiBEb2VzIG5vdCBhZmZlY3QgZXhpc3RpbmcgaW5zdGFuY2VzLgogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgc3RhdGljIHNldCBkZWZhdWx0TG9jYWxlKGxvY2FsZSkgewogICAgICBkZWZhdWx0TG9jYWxlID0gbG9jYWxlOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBkZWZhdWx0IG51bWJlcmluZyBzeXN0ZW0gdG8gY3JlYXRlIERhdGVUaW1lcyB3aXRoLiBEb2VzIG5vdCBhZmZlY3QgZXhpc3RpbmcgaW5zdGFuY2VzLgogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgc3RhdGljIGdldCBkZWZhdWx0TnVtYmVyaW5nU3lzdGVtKCkgewogICAgICByZXR1cm4gZGVmYXVsdE51bWJlcmluZ1N5c3RlbTsKICAgIH0KCiAgICAvKioKICAgICAqIFNldCB0aGUgZGVmYXVsdCBudW1iZXJpbmcgc3lzdGVtIHRvIGNyZWF0ZSBEYXRlVGltZXMgd2l0aC4gRG9lcyBub3QgYWZmZWN0IGV4aXN0aW5nIGluc3RhbmNlcy4KICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIHN0YXRpYyBzZXQgZGVmYXVsdE51bWJlcmluZ1N5c3RlbShudW1iZXJpbmdTeXN0ZW0pIHsKICAgICAgZGVmYXVsdE51bWJlcmluZ1N5c3RlbSA9IG51bWJlcmluZ1N5c3RlbTsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgZGVmYXVsdCBvdXRwdXQgY2FsZW5kYXIgdG8gY3JlYXRlIERhdGVUaW1lcyB3aXRoLiBEb2VzIG5vdCBhZmZlY3QgZXhpc3RpbmcgaW5zdGFuY2VzLgogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgc3RhdGljIGdldCBkZWZhdWx0T3V0cHV0Q2FsZW5kYXIoKSB7CiAgICAgIHJldHVybiBkZWZhdWx0T3V0cHV0Q2FsZW5kYXI7CiAgICB9CgogICAgLyoqCiAgICAgKiBTZXQgdGhlIGRlZmF1bHQgb3V0cHV0IGNhbGVuZGFyIHRvIGNyZWF0ZSBEYXRlVGltZXMgd2l0aC4gRG9lcyBub3QgYWZmZWN0IGV4aXN0aW5nIGluc3RhbmNlcy4KICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIHN0YXRpYyBzZXQgZGVmYXVsdE91dHB1dENhbGVuZGFyKG91dHB1dENhbGVuZGFyKSB7CiAgICAgIGRlZmF1bHRPdXRwdXRDYWxlbmRhciA9IG91dHB1dENhbGVuZGFyOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBjdXRvZmYgeWVhciBhZnRlciB3aGljaCBhIHN0cmluZyBlbmNvZGluZyBhIHllYXIgYXMgdHdvIGRpZ2l0cyBpcyBpbnRlcnByZXRlZCB0byBvY2N1ciBpbiB0aGUgY3VycmVudCBjZW50dXJ5LgogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgc3RhdGljIGdldCB0d29EaWdpdEN1dG9mZlllYXIoKSB7CiAgICAgIHJldHVybiB0d29EaWdpdEN1dG9mZlllYXI7CiAgICB9CgogICAgLyoqCiAgICAgKiBTZXQgdGhlIGN1dG9mZiB5ZWFyIGFmdGVyIHdoaWNoIGEgc3RyaW5nIGVuY29kaW5nIGEgeWVhciBhcyB0d28gZGlnaXRzIGlzIGludGVycHJldGVkIHRvIG9jY3VyIGluIHRoZSBjdXJyZW50IGNlbnR1cnkuCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICogQGV4YW1wbGUgU2V0dGluZ3MudHdvRGlnaXRDdXRvZmZZZWFyID0gMCAvLyBjdXQtb2ZmIHllYXIgaXMgMCwgc28gYWxsICd5eScgYXJlIGludGVycHJldGVkIGFzIGN1cnJlbnQgY2VudHVyeQogICAgICogQGV4YW1wbGUgU2V0dGluZ3MudHdvRGlnaXRDdXRvZmZZZWFyID0gNTAgLy8gJzQ5JyAtPiAxOTQ5OyAnNTAnIC0+IDIwNTAKICAgICAqIEBleGFtcGxlIFNldHRpbmdzLnR3b0RpZ2l0Q3V0b2ZmWWVhciA9IDE5NTAgLy8gaW50ZXJwcmV0ZWQgYXMgNTAKICAgICAqIEBleGFtcGxlIFNldHRpbmdzLnR3b0RpZ2l0Q3V0b2ZmWWVhciA9IDIwNTAgLy8gQUxTTyBpbnRlcnByZXRlZCBhcyA1MAogICAgICovCiAgICBzdGF0aWMgc2V0IHR3b0RpZ2l0Q3V0b2ZmWWVhcihjdXRvZmZZZWFyKSB7CiAgICAgIHR3b0RpZ2l0Q3V0b2ZmWWVhciA9IGN1dG9mZlllYXIgJSAxMDA7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgd2hldGhlciBMdXhvbiB3aWxsIHRocm93IHdoZW4gaXQgZW5jb3VudGVycyBpbnZhbGlkIERhdGVUaW1lcywgRHVyYXRpb25zLCBvciBJbnRlcnZhbHMKICAgICAqIEB0eXBlIHtib29sZWFufQogICAgICovCiAgICBzdGF0aWMgZ2V0IHRocm93T25JbnZhbGlkKCkgewogICAgICByZXR1cm4gdGhyb3dPbkludmFsaWQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBTZXQgd2hldGhlciBMdXhvbiB3aWxsIHRocm93IHdoZW4gaXQgZW5jb3VudGVycyBpbnZhbGlkIERhdGVUaW1lcywgRHVyYXRpb25zLCBvciBJbnRlcnZhbHMKICAgICAqIEB0eXBlIHtib29sZWFufQogICAgICovCiAgICBzdGF0aWMgc2V0IHRocm93T25JbnZhbGlkKHQpIHsKICAgICAgdGhyb3dPbkludmFsaWQgPSB0OwogICAgfQoKICAgIC8qKgogICAgICogUmVzZXQgTHV4b24ncyBnbG9iYWwgY2FjaGVzLiBTaG91bGQgb25seSBiZSBuZWNlc3NhcnkgaW4gdGVzdGluZyBzY2VuYXJpb3MuCiAgICAgKiBAcmV0dXJuIHt2b2lkfQogICAgICovCiAgICBzdGF0aWMgcmVzZXRDYWNoZXMoKSB7CiAgICAgIExvY2FsZS5yZXNldENhY2hlKCk7CiAgICAgIElBTkFab25lLnJlc2V0Q2FjaGUoKTsKICAgIH0KICB9CgogIC8qCiAgICBUaGlzIGlzIGp1c3QgYSBqdW5rIGRyYXdlciwgY29udGFpbmluZyBhbnl0aGluZyB1c2VkIGFjcm9zcyBtdWx0aXBsZSBjbGFzc2VzLgogICAgQmVjYXVzZSBMdXhvbiBpcyBzbWFsbChpc2gpLCB0aGlzIHNob3VsZCBzdGF5IHNtYWxsIGFuZCB3ZSB3b24ndCB3b3JyeSBhYm91dCBzcGxpdHRpbmcKICAgIGl0IHVwIGludG8sIHNheSwgcGFyc2luZ1V0aWwuanMgYW5kIGJhc2ljVXRpbC5qcyBhbmQgc28gb24uIEJ1dCB0aGV5IGFyZSBkaXZpZGVkIHVwIGJ5IGZlYXR1cmUgYXJlYS4KICAqLwoKICAvKioKICAgKiBAcHJpdmF0ZQogICAqLwoKICAvLyBUWVBFUwoKICBmdW5jdGlvbiBpc1VuZGVmaW5lZChvKSB7CiAgICByZXR1cm4gdHlwZW9mIG8gPT09ICJ1bmRlZmluZWQiOwogIH0KCiAgZnVuY3Rpb24gaXNOdW1iZXIobykgewogICAgcmV0dXJuIHR5cGVvZiBvID09PSAibnVtYmVyIjsKICB9CgogIGZ1bmN0aW9uIGlzSW50ZWdlcihvKSB7CiAgICByZXR1cm4gdHlwZW9mIG8gPT09ICJudW1iZXIiICYmIG8gJSAxID09PSAwOwogIH0KCiAgZnVuY3Rpb24gaXNTdHJpbmcobykgewogICAgcmV0dXJuIHR5cGVvZiBvID09PSAic3RyaW5nIjsKICB9CgogIGZ1bmN0aW9uIGlzRGF0ZShvKSB7CiAgICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG8pID09PSAiW29iamVjdCBEYXRlXSI7CiAgfQoKICAvLyBDQVBBQklMSVRJRVMKCiAgZnVuY3Rpb24gaGFzUmVsYXRpdmUoKSB7CiAgICB0cnkgewogICAgICByZXR1cm4gdHlwZW9mIEludGwgIT09ICJ1bmRlZmluZWQiICYmICEhSW50bC5SZWxhdGl2ZVRpbWVGb3JtYXQ7CiAgICB9IGNhdGNoIChlKSB7CiAgICAgIHJldHVybiBmYWxzZTsKICAgIH0KICB9CgogIC8vIE9CSkVDVFMgQU5EIEFSUkFZUwoKICBmdW5jdGlvbiBtYXliZUFycmF5KHRoaW5nKSB7CiAgICByZXR1cm4gQXJyYXkuaXNBcnJheSh0aGluZykgPyB0aGluZyA6IFt0aGluZ107CiAgfQoKICBmdW5jdGlvbiBiZXN0QnkoYXJyLCBieSwgY29tcGFyZSkgewogICAgaWYgKGFyci5sZW5ndGggPT09IDApIHsKICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgIH0KICAgIHJldHVybiBhcnIucmVkdWNlKChiZXN0LCBuZXh0KSA9PiB7CiAgICAgIGNvbnN0IHBhaXIgPSBbYnkobmV4dCksIG5leHRdOwogICAgICBpZiAoIWJlc3QpIHsKICAgICAgICByZXR1cm4gcGFpcjsKICAgICAgfSBlbHNlIGlmIChjb21wYXJlKGJlc3RbMF0sIHBhaXJbMF0pID09PSBiZXN0WzBdKSB7CiAgICAgICAgcmV0dXJuIGJlc3Q7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIHBhaXI7CiAgICAgIH0KICAgIH0sIG51bGwpWzFdOwogIH0KCiAgZnVuY3Rpb24gcGljayhvYmosIGtleXMpIHsKICAgIHJldHVybiBrZXlzLnJlZHVjZSgoYSwgaykgPT4gewogICAgICBhW2tdID0gb2JqW2tdOwogICAgICByZXR1cm4gYTsKICAgIH0sIHt9KTsKICB9CgogIGZ1bmN0aW9uIGhhc093blByb3BlcnR5KG9iaiwgcHJvcCkgewogICAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApOwogIH0KCiAgLy8gTlVNQkVSUyBBTkQgU1RSSU5HUwoKICBmdW5jdGlvbiBpbnRlZ2VyQmV0d2Vlbih0aGluZywgYm90dG9tLCB0b3ApIHsKICAgIHJldHVybiBpc0ludGVnZXIodGhpbmcpICYmIHRoaW5nID49IGJvdHRvbSAmJiB0aGluZyA8PSB0b3A7CiAgfQoKICAvLyB4ICUgbiBidXQgdGFrZXMgdGhlIHNpZ24gb2YgbiBpbnN0ZWFkIG9mIHgKICBmdW5jdGlvbiBmbG9vck1vZCh4LCBuKSB7CiAgICByZXR1cm4geCAtIG4gKiBNYXRoLmZsb29yKHggLyBuKTsKICB9CgogIGZ1bmN0aW9uIHBhZFN0YXJ0KGlucHV0LCBuID0gMikgewogICAgY29uc3QgaXNOZWcgPSBpbnB1dCA8IDA7CiAgICBsZXQgcGFkZGVkOwogICAgaWYgKGlzTmVnKSB7CiAgICAgIHBhZGRlZCA9ICItIiArICgiIiArIC1pbnB1dCkucGFkU3RhcnQobiwgIjAiKTsKICAgIH0gZWxzZSB7CiAgICAgIHBhZGRlZCA9ICgiIiArIGlucHV0KS5wYWRTdGFydChuLCAiMCIpOwogICAgfQogICAgcmV0dXJuIHBhZGRlZDsKICB9CgogIGZ1bmN0aW9uIHBhcnNlSW50ZWdlcihzdHJpbmcpIHsKICAgIGlmIChpc1VuZGVmaW5lZChzdHJpbmcpIHx8IHN0cmluZyA9PT0gbnVsbCB8fCBzdHJpbmcgPT09ICIiKSB7CiAgICAgIHJldHVybiB1bmRlZmluZWQ7CiAgICB9IGVsc2UgewogICAgICByZXR1cm4gcGFyc2VJbnQoc3RyaW5nLCAxMCk7CiAgICB9CiAgfQoKICBmdW5jdGlvbiBwYXJzZUZsb2F0aW5nKHN0cmluZykgewogICAgaWYgKGlzVW5kZWZpbmVkKHN0cmluZykgfHwgc3RyaW5nID09PSBudWxsIHx8IHN0cmluZyA9PT0gIiIpIHsKICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybiBwYXJzZUZsb2F0KHN0cmluZyk7CiAgICB9CiAgfQoKICBmdW5jdGlvbiBwYXJzZU1pbGxpcyhmcmFjdGlvbikgewogICAgLy8gUmV0dXJuIHVuZGVmaW5lZCAoaW5zdGVhZCBvZiAwKSBpbiB0aGVzZSBjYXNlcywgd2hlcmUgZnJhY3Rpb24gaXMgbm90IHNldAogICAgaWYgKGlzVW5kZWZpbmVkKGZyYWN0aW9uKSB8fCBmcmFjdGlvbiA9PT0gbnVsbCB8fCBmcmFjdGlvbiA9PT0gIiIpIHsKICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgIH0gZWxzZSB7CiAgICAgIGNvbnN0IGYgPSBwYXJzZUZsb2F0KCIwLiIgKyBmcmFjdGlvbikgKiAxMDAwOwogICAgICByZXR1cm4gTWF0aC5mbG9vcihmKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIHJvdW5kVG8obnVtYmVyLCBkaWdpdHMsIHRvd2FyZFplcm8gPSBmYWxzZSkgewogICAgY29uc3QgZmFjdG9yID0gMTAgKiogZGlnaXRzLAogICAgICByb3VuZGVyID0gdG93YXJkWmVybyA/IE1hdGgudHJ1bmMgOiBNYXRoLnJvdW5kOwogICAgcmV0dXJuIHJvdW5kZXIobnVtYmVyICogZmFjdG9yKSAvIGZhY3RvcjsKICB9CgogIC8vIERBVEUgQkFTSUNTCgogIGZ1bmN0aW9uIGlzTGVhcFllYXIoeWVhcikgewogICAgcmV0dXJuIHllYXIgJSA0ID09PSAwICYmICh5ZWFyICUgMTAwICE9PSAwIHx8IHllYXIgJSA0MDAgPT09IDApOwogIH0KCiAgZnVuY3Rpb24gZGF5c0luWWVhcih5ZWFyKSB7CiAgICByZXR1cm4gaXNMZWFwWWVhcih5ZWFyKSA/IDM2NiA6IDM2NTsKICB9CgogIGZ1bmN0aW9uIGRheXNJbk1vbnRoKHllYXIsIG1vbnRoKSB7CiAgICBjb25zdCBtb2RNb250aCA9IGZsb29yTW9kKG1vbnRoIC0gMSwgMTIpICsgMSwKICAgICAgbW9kWWVhciA9IHllYXIgKyAobW9udGggLSBtb2RNb250aCkgLyAxMjsKCiAgICBpZiAobW9kTW9udGggPT09IDIpIHsKICAgICAgcmV0dXJuIGlzTGVhcFllYXIobW9kWWVhcikgPyAyOSA6IDI4OwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIFszMSwgbnVsbCwgMzEsIDMwLCAzMSwgMzAsIDMxLCAzMSwgMzAsIDMxLCAzMCwgMzFdW21vZE1vbnRoIC0gMV07CiAgICB9CiAgfQoKICAvLyBjb252ZXJ0IGEgY2FsZW5kYXIgb2JqZWN0IHRvIGEgbG9jYWwgdGltZXN0YW1wIChlcG9jaCwgYnV0IHdpdGggdGhlIG9mZnNldCBiYWtlZCBpbikKICBmdW5jdGlvbiBvYmpUb0xvY2FsVFMob2JqKSB7CiAgICBsZXQgZCA9IERhdGUuVVRDKAogICAgICBvYmoueWVhciwKICAgICAgb2JqLm1vbnRoIC0gMSwKICAgICAgb2JqLmRheSwKICAgICAgb2JqLmhvdXIsCiAgICAgIG9iai5taW51dGUsCiAgICAgIG9iai5zZWNvbmQsCiAgICAgIG9iai5taWxsaXNlY29uZAogICAgKTsKCiAgICAvLyBmb3IgbGVnYWN5IHJlYXNvbnMsIHllYXJzIGJldHdlZW4gMCBhbmQgOTkgYXJlIGludGVycHJldGVkIGFzIDE5WFg7IHJldmVydCB0aGF0CiAgICBpZiAob2JqLnllYXIgPCAxMDAgJiYgb2JqLnllYXIgPj0gMCkgewogICAgICBkID0gbmV3IERhdGUoZCk7CiAgICAgIC8vIHNldCB0aGUgbW9udGggYW5kIGRheSBhZ2FpbiwgdGhpcyBpcyBuZWNlc3NhcnkgYmVjYXVzZSB5ZWFyIDIwMDAgaXMgYSBsZWFwIHllYXIsIGJ1dCB5ZWFyIDEwMCBpcyBub3QKICAgICAgLy8gc28gaWYgb2JqLnllYXIgaXMgaW4gOTksIGJ1dCBvYmouZGF5IG1ha2VzIGl0IHJvbGwgb3ZlciBpbnRvIHllYXIgMTAwLAogICAgICAvLyB0aGUgY2FsY3VsYXRpb25zIGRvbmUgYnkgRGF0ZS5VVEMgYXJlIHVzaW5nIHllYXIgMjAwMCAtIHdoaWNoIGlzIGluY29ycmVjdAogICAgICBkLnNldFVUQ0Z1bGxZZWFyKG9iai55ZWFyLCBvYmoubW9udGggLSAxLCBvYmouZGF5KTsKICAgIH0KICAgIHJldHVybiArZDsKICB9CgogIGZ1bmN0aW9uIHdlZWtzSW5XZWVrWWVhcih3ZWVrWWVhcikgewogICAgY29uc3QgcDEgPQogICAgICAgICh3ZWVrWWVhciArCiAgICAgICAgICBNYXRoLmZsb29yKHdlZWtZZWFyIC8gNCkgLQogICAgICAgICAgTWF0aC5mbG9vcih3ZWVrWWVhciAvIDEwMCkgKwogICAgICAgICAgTWF0aC5mbG9vcih3ZWVrWWVhciAvIDQwMCkpICUKICAgICAgICA3LAogICAgICBsYXN0ID0gd2Vla1llYXIgLSAxLAogICAgICBwMiA9IChsYXN0ICsgTWF0aC5mbG9vcihsYXN0IC8gNCkgLSBNYXRoLmZsb29yKGxhc3QgLyAxMDApICsgTWF0aC5mbG9vcihsYXN0IC8gNDAwKSkgJSA3OwogICAgcmV0dXJuIHAxID09PSA0IHx8IHAyID09PSAzID8gNTMgOiA1MjsKICB9CgogIGZ1bmN0aW9uIHVudHJ1bmNhdGVZZWFyKHllYXIpIHsKICAgIGlmICh5ZWFyID4gOTkpIHsKICAgICAgcmV0dXJuIHllYXI7CiAgICB9IGVsc2UgcmV0dXJuIHllYXIgPiBTZXR0aW5ncy50d29EaWdpdEN1dG9mZlllYXIgPyAxOTAwICsgeWVhciA6IDIwMDAgKyB5ZWFyOwogIH0KCiAgLy8gUEFSU0lORwoKICBmdW5jdGlvbiBwYXJzZVpvbmVJbmZvKHRzLCBvZmZzZXRGb3JtYXQsIGxvY2FsZSwgdGltZVpvbmUgPSBudWxsKSB7CiAgICBjb25zdCBkYXRlID0gbmV3IERhdGUodHMpLAogICAgICBpbnRsT3B0cyA9IHsKICAgICAgICBob3VyQ3ljbGU6ICJoMjMiLAogICAgICAgIHllYXI6ICJudW1lcmljIiwKICAgICAgICBtb250aDogIjItZGlnaXQiLAogICAgICAgIGRheTogIjItZGlnaXQiLAogICAgICAgIGhvdXI6ICIyLWRpZ2l0IiwKICAgICAgICBtaW51dGU6ICIyLWRpZ2l0IiwKICAgICAgfTsKCiAgICBpZiAodGltZVpvbmUpIHsKICAgICAgaW50bE9wdHMudGltZVpvbmUgPSB0aW1lWm9uZTsKICAgIH0KCiAgICBjb25zdCBtb2RpZmllZCA9IHsgdGltZVpvbmVOYW1lOiBvZmZzZXRGb3JtYXQsIC4uLmludGxPcHRzIH07CgogICAgY29uc3QgcGFyc2VkID0gbmV3IEludGwuRGF0ZVRpbWVGb3JtYXQobG9jYWxlLCBtb2RpZmllZCkKICAgICAgLmZvcm1hdFRvUGFydHMoZGF0ZSkKICAgICAgLmZpbmQoKG0pID0+IG0udHlwZS50b0xvd2VyQ2FzZSgpID09PSAidGltZXpvbmVuYW1lIik7CiAgICByZXR1cm4gcGFyc2VkID8gcGFyc2VkLnZhbHVlIDogbnVsbDsKICB9CgogIC8vIHNpZ25lZE9mZnNldCgnLTUnLCAnMzAnKSAtPiAtMzMwCiAgZnVuY3Rpb24gc2lnbmVkT2Zmc2V0KG9mZkhvdXJTdHIsIG9mZk1pbnV0ZVN0cikgewogICAgbGV0IG9mZkhvdXIgPSBwYXJzZUludChvZmZIb3VyU3RyLCAxMCk7CgogICAgLy8gZG9uJ3QgfHwgdGhpcyBiZWNhdXNlIHdlIHdhbnQgdG8gcHJlc2VydmUgLTAKICAgIGlmIChOdW1iZXIuaXNOYU4ob2ZmSG91cikpIHsKICAgICAgb2ZmSG91ciA9IDA7CiAgICB9CgogICAgY29uc3Qgb2ZmTWluID0gcGFyc2VJbnQob2ZmTWludXRlU3RyLCAxMCkgfHwgMCwKICAgICAgb2ZmTWluU2lnbmVkID0gb2ZmSG91ciA8IDAgfHwgT2JqZWN0LmlzKG9mZkhvdXIsIC0wKSA/IC1vZmZNaW4gOiBvZmZNaW47CiAgICByZXR1cm4gb2ZmSG91ciAqIDYwICsgb2ZmTWluU2lnbmVkOwogIH0KCiAgLy8gQ09FUkNJT04KCiAgZnVuY3Rpb24gYXNOdW1iZXIodmFsdWUpIHsKICAgIGNvbnN0IG51bWVyaWNWYWx1ZSA9IE51bWJlcih2YWx1ZSk7CiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAiYm9vbGVhbiIgfHwgdmFsdWUgPT09ICIiIHx8IE51bWJlci5pc05hTihudW1lcmljVmFsdWUpKQogICAgICB0aHJvdyBuZXcgSW52YWxpZEFyZ3VtZW50RXJyb3IoYEludmFsaWQgdW5pdCB2YWx1ZSAke3ZhbHVlfWApOwogICAgcmV0dXJuIG51bWVyaWNWYWx1ZTsKICB9CgogIGZ1bmN0aW9uIG5vcm1hbGl6ZU9iamVjdChvYmosIG5vcm1hbGl6ZXIpIHsKICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSB7fTsKICAgIGZvciAoY29uc3QgdSBpbiBvYmopIHsKICAgICAgaWYgKGhhc093blByb3BlcnR5KG9iaiwgdSkpIHsKICAgICAgICBjb25zdCB2ID0gb2JqW3VdOwogICAgICAgIGlmICh2ID09PSB1bmRlZmluZWQgfHwgdiA9PT0gbnVsbCkgY29udGludWU7CiAgICAgICAgbm9ybWFsaXplZFtub3JtYWxpemVyKHUpXSA9IGFzTnVtYmVyKHYpOwogICAgICB9CiAgICB9CiAgICByZXR1cm4gbm9ybWFsaXplZDsKICB9CgogIGZ1bmN0aW9uIGZvcm1hdE9mZnNldChvZmZzZXQsIGZvcm1hdCkgewogICAgY29uc3QgaG91cnMgPSBNYXRoLnRydW5jKE1hdGguYWJzKG9mZnNldCAvIDYwKSksCiAgICAgIG1pbnV0ZXMgPSBNYXRoLnRydW5jKE1hdGguYWJzKG9mZnNldCAlIDYwKSksCiAgICAgIHNpZ24gPSBvZmZzZXQgPj0gMCA/ICIrIiA6ICItIjsKCiAgICBzd2l0Y2ggKGZvcm1hdCkgewogICAgICBjYXNlICJzaG9ydCI6CiAgICAgICAgcmV0dXJuIGAke3NpZ259JHtwYWRTdGFydChob3VycywgMil9OiR7cGFkU3RhcnQobWludXRlcywgMil9YDsKICAgICAgY2FzZSAibmFycm93IjoKICAgICAgICByZXR1cm4gYCR7c2lnbn0ke2hvdXJzfSR7bWludXRlcyA+IDAgPyBgOiR7bWludXRlc31gIDogIiJ9YDsKICAgICAgY2FzZSAidGVjaGllIjoKICAgICAgICByZXR1cm4gYCR7c2lnbn0ke3BhZFN0YXJ0KGhvdXJzLCAyKX0ke3BhZFN0YXJ0KG1pbnV0ZXMsIDIpfWA7CiAgICAgIGRlZmF1bHQ6CiAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoYFZhbHVlIGZvcm1hdCAke2Zvcm1hdH0gaXMgb3V0IG9mIHJhbmdlIGZvciBwcm9wZXJ0eSBmb3JtYXRgKTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIHRpbWVPYmplY3Qob2JqKSB7CiAgICByZXR1cm4gcGljayhvYmosIFsiaG91ciIsICJtaW51dGUiLCAic2Vjb25kIiwgIm1pbGxpc2Vjb25kIl0pOwogIH0KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KCiAgY29uc3QgbW9udGhzTG9uZyA9IFsKICAgICJKYW51YXJ5IiwKICAgICJGZWJydWFyeSIsCiAgICAiTWFyY2giLAogICAgIkFwcmlsIiwKICAgICJNYXkiLAogICAgIkp1bmUiLAogICAgIkp1bHkiLAogICAgIkF1Z3VzdCIsCiAgICAiU2VwdGVtYmVyIiwKICAgICJPY3RvYmVyIiwKICAgICJOb3ZlbWJlciIsCiAgICAiRGVjZW1iZXIiLAogIF07CgogIGNvbnN0IG1vbnRoc1Nob3J0ID0gWwogICAgIkphbiIsCiAgICAiRmViIiwKICAgICJNYXIiLAogICAgIkFwciIsCiAgICAiTWF5IiwKICAgICJKdW4iLAogICAgIkp1bCIsCiAgICAiQXVnIiwKICAgICJTZXAiLAogICAgIk9jdCIsCiAgICAiTm92IiwKICAgICJEZWMiLAogIF07CgogIGNvbnN0IG1vbnRoc05hcnJvdyA9IFsiSiIsICJGIiwgIk0iLCAiQSIsICJNIiwgIkoiLCAiSiIsICJBIiwgIlMiLCAiTyIsICJOIiwgIkQiXTsKCiAgZnVuY3Rpb24gbW9udGhzKGxlbmd0aCkgewogICAgc3dpdGNoIChsZW5ndGgpIHsKICAgICAgY2FzZSAibmFycm93IjoKICAgICAgICByZXR1cm4gWy4uLm1vbnRoc05hcnJvd107CiAgICAgIGNhc2UgInNob3J0IjoKICAgICAgICByZXR1cm4gWy4uLm1vbnRoc1Nob3J0XTsKICAgICAgY2FzZSAibG9uZyI6CiAgICAgICAgcmV0dXJuIFsuLi5tb250aHNMb25nXTsKICAgICAgY2FzZSAibnVtZXJpYyI6CiAgICAgICAgcmV0dXJuIFsiMSIsICIyIiwgIjMiLCAiNCIsICI1IiwgIjYiLCAiNyIsICI4IiwgIjkiLCAiMTAiLCAiMTEiLCAiMTIiXTsKICAgICAgY2FzZSAiMi1kaWdpdCI6CiAgICAgICAgcmV0dXJuIFsiMDEiLCAiMDIiLCAiMDMiLCAiMDQiLCAiMDUiLCAiMDYiLCAiMDciLCAiMDgiLCAiMDkiLCAiMTAiLCAiMTEiLCAiMTIiXTsKICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4gbnVsbDsKICAgIH0KICB9CgogIGNvbnN0IHdlZWtkYXlzTG9uZyA9IFsKICAgICJNb25kYXkiLAogICAgIlR1ZXNkYXkiLAogICAgIldlZG5lc2RheSIsCiAgICAiVGh1cnNkYXkiLAogICAgIkZyaWRheSIsCiAgICAiU2F0dXJkYXkiLAogICAgIlN1bmRheSIsCiAgXTsKCiAgY29uc3Qgd2Vla2RheXNTaG9ydCA9IFsiTW9uIiwgIlR1ZSIsICJXZWQiLCAiVGh1IiwgIkZyaSIsICJTYXQiLCAiU3VuIl07CgogIGNvbnN0IHdlZWtkYXlzTmFycm93ID0gWyJNIiwgIlQiLCAiVyIsICJUIiwgIkYiLCAiUyIsICJTIl07CgogIGZ1bmN0aW9uIHdlZWtkYXlzKGxlbmd0aCkgewogICAgc3dpdGNoIChsZW5ndGgpIHsKICAgICAgY2FzZSAibmFycm93IjoKICAgICAgICByZXR1cm4gWy4uLndlZWtkYXlzTmFycm93XTsKICAgICAgY2FzZSAic2hvcnQiOgogICAgICAgIHJldHVybiBbLi4ud2Vla2RheXNTaG9ydF07CiAgICAgIGNhc2UgImxvbmciOgogICAgICAgIHJldHVybiBbLi4ud2Vla2RheXNMb25nXTsKICAgICAgY2FzZSAibnVtZXJpYyI6CiAgICAgICAgcmV0dXJuIFsiMSIsICIyIiwgIjMiLCAiNCIsICI1IiwgIjYiLCAiNyJdOwogICAgICBkZWZhdWx0OgogICAgICAgIHJldHVybiBudWxsOwogICAgfQogIH0KCiAgY29uc3QgbWVyaWRpZW1zID0gWyJBTSIsICJQTSJdOwoKICBjb25zdCBlcmFzTG9uZyA9IFsiQmVmb3JlIENocmlzdCIsICJBbm5vIERvbWluaSJdOwoKICBjb25zdCBlcmFzU2hvcnQgPSBbIkJDIiwgIkFEIl07CgogIGNvbnN0IGVyYXNOYXJyb3cgPSBbIkIiLCAiQSJdOwoKICBmdW5jdGlvbiBlcmFzKGxlbmd0aCkgewogICAgc3dpdGNoIChsZW5ndGgpIHsKICAgICAgY2FzZSAibmFycm93IjoKICAgICAgICByZXR1cm4gWy4uLmVyYXNOYXJyb3ddOwogICAgICBjYXNlICJzaG9ydCI6CiAgICAgICAgcmV0dXJuIFsuLi5lcmFzU2hvcnRdOwogICAgICBjYXNlICJsb25nIjoKICAgICAgICByZXR1cm4gWy4uLmVyYXNMb25nXTsKICAgICAgZGVmYXVsdDoKICAgICAgICByZXR1cm4gbnVsbDsKICAgIH0KICB9CgogIGZ1bmN0aW9uIG1lcmlkaWVtRm9yRGF0ZVRpbWUoZHQpIHsKICAgIHJldHVybiBtZXJpZGllbXNbZHQuaG91ciA8IDEyID8gMCA6IDFdOwogIH0KCiAgZnVuY3Rpb24gd2Vla2RheUZvckRhdGVUaW1lKGR0LCBsZW5ndGgpIHsKICAgIHJldHVybiB3ZWVrZGF5cyhsZW5ndGgpW2R0LndlZWtkYXkgLSAxXTsKICB9CgogIGZ1bmN0aW9uIG1vbnRoRm9yRGF0ZVRpbWUoZHQsIGxlbmd0aCkgewogICAgcmV0dXJuIG1vbnRocyhsZW5ndGgpW2R0Lm1vbnRoIC0gMV07CiAgfQoKICBmdW5jdGlvbiBlcmFGb3JEYXRlVGltZShkdCwgbGVuZ3RoKSB7CiAgICByZXR1cm4gZXJhcyhsZW5ndGgpW2R0LnllYXIgPCAwID8gMCA6IDFdOwogIH0KCiAgZnVuY3Rpb24gZm9ybWF0UmVsYXRpdmVUaW1lKHVuaXQsIGNvdW50LCBudW1lcmljID0gImFsd2F5cyIsIG5hcnJvdyA9IGZhbHNlKSB7CiAgICBjb25zdCB1bml0cyA9IHsKICAgICAgeWVhcnM6IFsieWVhciIsICJ5ci4iXSwKICAgICAgcXVhcnRlcnM6IFsicXVhcnRlciIsICJxdHIuIl0sCiAgICAgIG1vbnRoczogWyJtb250aCIsICJtby4iXSwKICAgICAgd2Vla3M6IFsid2VlayIsICJ3ay4iXSwKICAgICAgZGF5czogWyJkYXkiLCAiZGF5IiwgImRheXMiXSwKICAgICAgaG91cnM6IFsiaG91ciIsICJoci4iXSwKICAgICAgbWludXRlczogWyJtaW51dGUiLCAibWluLiJdLAogICAgICBzZWNvbmRzOiBbInNlY29uZCIsICJzZWMuIl0sCiAgICB9OwoKICAgIGNvbnN0IGxhc3RhYmxlID0gWyJob3VycyIsICJtaW51dGVzIiwgInNlY29uZHMiXS5pbmRleE9mKHVuaXQpID09PSAtMTsKCiAgICBpZiAobnVtZXJpYyA9PT0gImF1dG8iICYmIGxhc3RhYmxlKSB7CiAgICAgIGNvbnN0IGlzRGF5ID0gdW5pdCA9PT0gImRheXMiOwogICAgICBzd2l0Y2ggKGNvdW50KSB7CiAgICAgICAgY2FzZSAxOgogICAgICAgICAgcmV0dXJuIGlzRGF5ID8gInRvbW9ycm93IiA6IGBuZXh0ICR7dW5pdHNbdW5pdF1bMF19YDsKICAgICAgICBjYXNlIC0xOgogICAgICAgICAgcmV0dXJuIGlzRGF5ID8gInllc3RlcmRheSIgOiBgbGFzdCAke3VuaXRzW3VuaXRdWzBdfWA7CiAgICAgICAgY2FzZSAwOgogICAgICAgICAgcmV0dXJuIGlzRGF5ID8gInRvZGF5IiA6IGB0aGlzICR7dW5pdHNbdW5pdF1bMF19YDsKICAgICAgfQogICAgfQoKICAgIGNvbnN0IGlzSW5QYXN0ID0gT2JqZWN0LmlzKGNvdW50LCAtMCkgfHwgY291bnQgPCAwLAogICAgICBmbXRWYWx1ZSA9IE1hdGguYWJzKGNvdW50KSwKICAgICAgc2luZ3VsYXIgPSBmbXRWYWx1ZSA9PT0gMSwKICAgICAgbGlsVW5pdHMgPSB1bml0c1t1bml0XSwKICAgICAgZm10VW5pdCA9IG5hcnJvdwogICAgICAgID8gc2luZ3VsYXIKICAgICAgICAgID8gbGlsVW5pdHNbMV0KICAgICAgICAgIDogbGlsVW5pdHNbMl0gfHwgbGlsVW5pdHNbMV0KICAgICAgICA6IHNpbmd1bGFyCiAgICAgICAgPyB1bml0c1t1bml0XVswXQogICAgICAgIDogdW5pdDsKICAgIHJldHVybiBpc0luUGFzdCA/IGAke2ZtdFZhbHVlfSAke2ZtdFVuaXR9IGFnb2AgOiBgaW4gJHtmbXRWYWx1ZX0gJHtmbXRVbml0fWA7CiAgfQoKICBmdW5jdGlvbiBzdHJpbmdpZnlUb2tlbnMoc3BsaXRzLCB0b2tlblRvU3RyaW5nKSB7CiAgICBsZXQgcyA9ICIiOwogICAgZm9yIChjb25zdCB0b2tlbiBvZiBzcGxpdHMpIHsKICAgICAgaWYgKHRva2VuLmxpdGVyYWwpIHsKICAgICAgICBzICs9IHRva2VuLnZhbDsKICAgICAgfSBlbHNlIHsKICAgICAgICBzICs9IHRva2VuVG9TdHJpbmcodG9rZW4udmFsKTsKICAgICAgfQogICAgfQogICAgcmV0dXJuIHM7CiAgfQoKICBjb25zdCBtYWNyb1Rva2VuVG9Gb3JtYXRPcHRzID0gewogICAgRDogREFURV9TSE9SVCwKICAgIEREOiBEQVRFX01FRCwKICAgIERERDogREFURV9GVUxMLAogICAgRERERDogREFURV9IVUdFLAogICAgdDogVElNRV9TSU1QTEUsCiAgICB0dDogVElNRV9XSVRIX1NFQ09ORFMsCiAgICB0dHQ6IFRJTUVfV0lUSF9TSE9SVF9PRkZTRVQsCiAgICB0dHR0OiBUSU1FX1dJVEhfTE9OR19PRkZTRVQsCiAgICBUOiBUSU1FXzI0X1NJTVBMRSwKICAgIFRUOiBUSU1FXzI0X1dJVEhfU0VDT05EUywKICAgIFRUVDogVElNRV8yNF9XSVRIX1NIT1JUX09GRlNFVCwKICAgIFRUVFQ6IFRJTUVfMjRfV0lUSF9MT05HX09GRlNFVCwKICAgIGY6IERBVEVUSU1FX1NIT1JULAogICAgZmY6IERBVEVUSU1FX01FRCwKICAgIGZmZjogREFURVRJTUVfRlVMTCwKICAgIGZmZmY6IERBVEVUSU1FX0hVR0UsCiAgICBGOiBEQVRFVElNRV9TSE9SVF9XSVRIX1NFQ09ORFMsCiAgICBGRjogREFURVRJTUVfTUVEX1dJVEhfU0VDT05EUywKICAgIEZGRjogREFURVRJTUVfRlVMTF9XSVRIX1NFQ09ORFMsCiAgICBGRkZGOiBEQVRFVElNRV9IVUdFX1dJVEhfU0VDT05EUywKICB9OwoKICAvKioKICAgKiBAcHJpdmF0ZQogICAqLwoKICBjbGFzcyBGb3JtYXR0ZXIgewogICAgc3RhdGljIGNyZWF0ZShsb2NhbGUsIG9wdHMgPSB7fSkgewogICAgICByZXR1cm4gbmV3IEZvcm1hdHRlcihsb2NhbGUsIG9wdHMpOwogICAgfQoKICAgIHN0YXRpYyBwYXJzZUZvcm1hdChmbXQpIHsKICAgICAgLy8gd2hpdGUtc3BhY2UgaXMgYWx3YXlzIGNvbnNpZGVyZWQgYSBsaXRlcmFsIGluIHVzZXItcHJvdmlkZWQgZm9ybWF0cwogICAgICAvLyB0aGUgIiAiIHRva2VuIGhhcyBhIHNwZWNpYWwgbWVhbmluZyAoc2VlIHVuaXRGb3JUb2tlbikKCiAgICAgIGxldCBjdXJyZW50ID0gbnVsbCwKICAgICAgICBjdXJyZW50RnVsbCA9ICIiLAogICAgICAgIGJyYWNrZXRlZCA9IGZhbHNlOwogICAgICBjb25zdCBzcGxpdHMgPSBbXTsKICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBmbXQubGVuZ3RoOyBpKyspIHsKICAgICAgICBjb25zdCBjID0gZm10LmNoYXJBdChpKTsKICAgICAgICBpZiAoYyA9PT0gIiciKSB7CiAgICAgICAgICBpZiAoY3VycmVudEZ1bGwubGVuZ3RoID4gMCkgewogICAgICAgICAgICBzcGxpdHMucHVzaCh7IGxpdGVyYWw6IGJyYWNrZXRlZCB8fCAvXlxzKyQvLnRlc3QoY3VycmVudEZ1bGwpLCB2YWw6IGN1cnJlbnRGdWxsIH0pOwogICAgICAgICAgfQogICAgICAgICAgY3VycmVudCA9IG51bGw7CiAgICAgICAgICBjdXJyZW50RnVsbCA9ICIiOwogICAgICAgICAgYnJhY2tldGVkID0gIWJyYWNrZXRlZDsKICAgICAgICB9IGVsc2UgaWYgKGJyYWNrZXRlZCkgewogICAgICAgICAgY3VycmVudEZ1bGwgKz0gYzsKICAgICAgICB9IGVsc2UgaWYgKGMgPT09IGN1cnJlbnQpIHsKICAgICAgICAgIGN1cnJlbnRGdWxsICs9IGM7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGlmIChjdXJyZW50RnVsbC5sZW5ndGggPiAwKSB7CiAgICAgICAgICAgIHNwbGl0cy5wdXNoKHsgbGl0ZXJhbDogL15ccyskLy50ZXN0KGN1cnJlbnRGdWxsKSwgdmFsOiBjdXJyZW50RnVsbCB9KTsKICAgICAgICAgIH0KICAgICAgICAgIGN1cnJlbnRGdWxsID0gYzsKICAgICAgICAgIGN1cnJlbnQgPSBjOwogICAgICAgIH0KICAgICAgfQoKICAgICAgaWYgKGN1cnJlbnRGdWxsLmxlbmd0aCA+IDApIHsKICAgICAgICBzcGxpdHMucHVzaCh7IGxpdGVyYWw6IGJyYWNrZXRlZCB8fCAvXlxzKyQvLnRlc3QoY3VycmVudEZ1bGwpLCB2YWw6IGN1cnJlbnRGdWxsIH0pOwogICAgICB9CgogICAgICByZXR1cm4gc3BsaXRzOwogICAgfQoKICAgIHN0YXRpYyBtYWNyb1Rva2VuVG9Gb3JtYXRPcHRzKHRva2VuKSB7CiAgICAgIHJldHVybiBtYWNyb1Rva2VuVG9Gb3JtYXRPcHRzW3Rva2VuXTsKICAgIH0KCiAgICBjb25zdHJ1Y3Rvcihsb2NhbGUsIGZvcm1hdE9wdHMpIHsKICAgICAgdGhpcy5vcHRzID0gZm9ybWF0T3B0czsKICAgICAgdGhpcy5sb2MgPSBsb2NhbGU7CiAgICAgIHRoaXMuc3lzdGVtTG9jID0gbnVsbDsKICAgIH0KCiAgICBmb3JtYXRXaXRoU3lzdGVtRGVmYXVsdChkdCwgb3B0cykgewogICAgICBpZiAodGhpcy5zeXN0ZW1Mb2MgPT09IG51bGwpIHsKICAgICAgICB0aGlzLnN5c3RlbUxvYyA9IHRoaXMubG9jLnJlZGVmYXVsdFRvU3lzdGVtKCk7CiAgICAgIH0KICAgICAgY29uc3QgZGYgPSB0aGlzLnN5c3RlbUxvYy5kdEZvcm1hdHRlcihkdCwgeyAuLi50aGlzLm9wdHMsIC4uLm9wdHMgfSk7CiAgICAgIHJldHVybiBkZi5mb3JtYXQoKTsKICAgIH0KCiAgICBkdEZvcm1hdHRlcihkdCwgb3B0cyA9IHt9KSB7CiAgICAgIHJldHVybiB0aGlzLmxvYy5kdEZvcm1hdHRlcihkdCwgeyAuLi50aGlzLm9wdHMsIC4uLm9wdHMgfSk7CiAgICB9CgogICAgZm9ybWF0RGF0ZVRpbWUoZHQsIG9wdHMpIHsKICAgICAgcmV0dXJuIHRoaXMuZHRGb3JtYXR0ZXIoZHQsIG9wdHMpLmZvcm1hdCgpOwogICAgfQoKICAgIGZvcm1hdERhdGVUaW1lUGFydHMoZHQsIG9wdHMpIHsKICAgICAgcmV0dXJuIHRoaXMuZHRGb3JtYXR0ZXIoZHQsIG9wdHMpLmZvcm1hdFRvUGFydHMoKTsKICAgIH0KCiAgICBmb3JtYXRJbnRlcnZhbChpbnRlcnZhbCwgb3B0cykgewogICAgICBjb25zdCBkZiA9IHRoaXMuZHRGb3JtYXR0ZXIoaW50ZXJ2YWwuc3RhcnQsIG9wdHMpOwogICAgICByZXR1cm4gZGYuZHRmLmZvcm1hdFJhbmdlKGludGVydmFsLnN0YXJ0LnRvSlNEYXRlKCksIGludGVydmFsLmVuZC50b0pTRGF0ZSgpKTsKICAgIH0KCiAgICByZXNvbHZlZE9wdGlvbnMoZHQsIG9wdHMpIHsKICAgICAgcmV0dXJuIHRoaXMuZHRGb3JtYXR0ZXIoZHQsIG9wdHMpLnJlc29sdmVkT3B0aW9ucygpOwogICAgfQoKICAgIG51bShuLCBwID0gMCkgewogICAgICAvLyB3ZSBnZXQgc29tZSBwZXJmIG91dCBvZiBkb2luZyB0aGlzIGhlcmUsIGFubm95aW5nbHkKICAgICAgaWYgKHRoaXMub3B0cy5mb3JjZVNpbXBsZSkgewogICAgICAgIHJldHVybiBwYWRTdGFydChuLCBwKTsKICAgICAgfQoKICAgICAgY29uc3Qgb3B0cyA9IHsgLi4udGhpcy5vcHRzIH07CgogICAgICBpZiAocCA+IDApIHsKICAgICAgICBvcHRzLnBhZFRvID0gcDsKICAgICAgfQoKICAgICAgcmV0dXJuIHRoaXMubG9jLm51bWJlckZvcm1hdHRlcihvcHRzKS5mb3JtYXQobik7CiAgICB9CgogICAgZm9ybWF0RGF0ZVRpbWVGcm9tU3RyaW5nKGR0LCBmbXQpIHsKICAgICAgY29uc3Qga25vd25FbmdsaXNoID0gdGhpcy5sb2MubGlzdGluZ01vZGUoKSA9PT0gImVuIiwKICAgICAgICB1c2VEYXRlVGltZUZvcm1hdHRlciA9IHRoaXMubG9jLm91dHB1dENhbGVuZGFyICYmIHRoaXMubG9jLm91dHB1dENhbGVuZGFyICE9PSAiZ3JlZ29yeSIsCiAgICAgICAgc3RyaW5nID0gKG9wdHMsIGV4dHJhY3QpID0+IHRoaXMubG9jLmV4dHJhY3QoZHQsIG9wdHMsIGV4dHJhY3QpLAogICAgICAgIGZvcm1hdE9mZnNldCA9IChvcHRzKSA9PiB7CiAgICAgICAgICBpZiAoZHQuaXNPZmZzZXRGaXhlZCAmJiBkdC5vZmZzZXQgPT09IDAgJiYgb3B0cy5hbGxvd1opIHsKICAgICAgICAgICAgcmV0dXJuICJaIjsKICAgICAgICAgIH0KCiAgICAgICAgICByZXR1cm4gZHQuaXNWYWxpZCA/IGR0LnpvbmUuZm9ybWF0T2Zmc2V0KGR0LnRzLCBvcHRzLmZvcm1hdCkgOiAiIjsKICAgICAgICB9LAogICAgICAgIG1lcmlkaWVtID0gKCkgPT4KICAgICAgICAgIGtub3duRW5nbGlzaAogICAgICAgICAgICA/IG1lcmlkaWVtRm9yRGF0ZVRpbWUoZHQpCiAgICAgICAgICAgIDogc3RyaW5nKHsgaG91cjogIm51bWVyaWMiLCBob3VyQ3ljbGU6ICJoMTIiIH0sICJkYXlwZXJpb2QiKSwKICAgICAgICBtb250aCA9IChsZW5ndGgsIHN0YW5kYWxvbmUpID0+CiAgICAgICAgICBrbm93bkVuZ2xpc2gKICAgICAgICAgICAgPyBtb250aEZvckRhdGVUaW1lKGR0LCBsZW5ndGgpCiAgICAgICAgICAgIDogc3RyaW5nKHN0YW5kYWxvbmUgPyB7IG1vbnRoOiBsZW5ndGggfSA6IHsgbW9udGg6IGxlbmd0aCwgZGF5OiAibnVtZXJpYyIgfSwgIm1vbnRoIiksCiAgICAgICAgd2Vla2RheSA9IChsZW5ndGgsIHN0YW5kYWxvbmUpID0+CiAgICAgICAgICBrbm93bkVuZ2xpc2gKICAgICAgICAgICAgPyB3ZWVrZGF5Rm9yRGF0ZVRpbWUoZHQsIGxlbmd0aCkKICAgICAgICAgICAgOiBzdHJpbmcoCiAgICAgICAgICAgICAgICBzdGFuZGFsb25lID8geyB3ZWVrZGF5OiBsZW5ndGggfSA6IHsgd2Vla2RheTogbGVuZ3RoLCBtb250aDogImxvbmciLCBkYXk6ICJudW1lcmljIiB9LAogICAgICAgICAgICAgICAgIndlZWtkYXkiCiAgICAgICAgICAgICAgKSwKICAgICAgICBtYXliZU1hY3JvID0gKHRva2VuKSA9PiB7CiAgICAgICAgICBjb25zdCBmb3JtYXRPcHRzID0gRm9ybWF0dGVyLm1hY3JvVG9rZW5Ub0Zvcm1hdE9wdHModG9rZW4pOwogICAgICAgICAgaWYgKGZvcm1hdE9wdHMpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMuZm9ybWF0V2l0aFN5c3RlbURlZmF1bHQoZHQsIGZvcm1hdE9wdHMpOwogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgcmV0dXJuIHRva2VuOwogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgZXJhID0gKGxlbmd0aCkgPT4KICAgICAgICAgIGtub3duRW5nbGlzaCA/IGVyYUZvckRhdGVUaW1lKGR0LCBsZW5ndGgpIDogc3RyaW5nKHsgZXJhOiBsZW5ndGggfSwgImVyYSIpLAogICAgICAgIHRva2VuVG9TdHJpbmcgPSAodG9rZW4pID0+IHsKICAgICAgICAgIC8vIFdoZXJlIHBvc3NpYmxlOiBodHRwczovL2NsZHIudW5pY29kZS5vcmcvdHJhbnNsYXRpb24vZGF0ZS10aW1lL2RhdGUtdGltZS1zeW1ib2xzCiAgICAgICAgICBzd2l0Y2ggKHRva2VuKSB7CiAgICAgICAgICAgIC8vIG1zCiAgICAgICAgICAgIGNhc2UgIlMiOgogICAgICAgICAgICAgIHJldHVybiB0aGlzLm51bShkdC5taWxsaXNlY29uZCk7CiAgICAgICAgICAgIGNhc2UgInUiOgogICAgICAgICAgICAvLyBmYWxscyB0aHJvdWdoCiAgICAgICAgICAgIGNhc2UgIlNTUyI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0Lm1pbGxpc2Vjb25kLCAzKTsKICAgICAgICAgICAgLy8gc2Vjb25kcwogICAgICAgICAgICBjYXNlICJzIjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQuc2Vjb25kKTsKICAgICAgICAgICAgY2FzZSAic3MiOgogICAgICAgICAgICAgIHJldHVybiB0aGlzLm51bShkdC5zZWNvbmQsIDIpOwogICAgICAgICAgICAvLyBmcmFjdGlvbmFsIHNlY29uZHMKICAgICAgICAgICAgY2FzZSAidXUiOgogICAgICAgICAgICAgIHJldHVybiB0aGlzLm51bShNYXRoLmZsb29yKGR0Lm1pbGxpc2Vjb25kIC8gMTApLCAyKTsKICAgICAgICAgICAgY2FzZSAidXV1IjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oTWF0aC5mbG9vcihkdC5taWxsaXNlY29uZCAvIDEwMCkpOwogICAgICAgICAgICAvLyBtaW51dGVzCiAgICAgICAgICAgIGNhc2UgIm0iOgogICAgICAgICAgICAgIHJldHVybiB0aGlzLm51bShkdC5taW51dGUpOwogICAgICAgICAgICBjYXNlICJtbSI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0Lm1pbnV0ZSwgMik7CiAgICAgICAgICAgIC8vIGhvdXJzCiAgICAgICAgICAgIGNhc2UgImgiOgogICAgICAgICAgICAgIHJldHVybiB0aGlzLm51bShkdC5ob3VyICUgMTIgPT09IDAgPyAxMiA6IGR0LmhvdXIgJSAxMik7CiAgICAgICAgICAgIGNhc2UgImhoIjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQuaG91ciAlIDEyID09PSAwID8gMTIgOiBkdC5ob3VyICUgMTIsIDIpOwogICAgICAgICAgICBjYXNlICJIIjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQuaG91cik7CiAgICAgICAgICAgIGNhc2UgIkhIIjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQuaG91ciwgMik7CiAgICAgICAgICAgIC8vIG9mZnNldAogICAgICAgICAgICBjYXNlICJaIjoKICAgICAgICAgICAgICAvLyBsaWtlICs2CiAgICAgICAgICAgICAgcmV0dXJuIGZvcm1hdE9mZnNldCh7IGZvcm1hdDogIm5hcnJvdyIsIGFsbG93WjogdGhpcy5vcHRzLmFsbG93WiB9KTsKICAgICAgICAgICAgY2FzZSAiWloiOgogICAgICAgICAgICAgIC8vIGxpa2UgKzA2OjAwCiAgICAgICAgICAgICAgcmV0dXJuIGZvcm1hdE9mZnNldCh7IGZvcm1hdDogInNob3J0IiwgYWxsb3daOiB0aGlzLm9wdHMuYWxsb3daIH0pOwogICAgICAgICAgICBjYXNlICJaWloiOgogICAgICAgICAgICAgIC8vIGxpa2UgKzA2MDAKICAgICAgICAgICAgICByZXR1cm4gZm9ybWF0T2Zmc2V0KHsgZm9ybWF0OiAidGVjaGllIiwgYWxsb3daOiB0aGlzLm9wdHMuYWxsb3daIH0pOwogICAgICAgICAgICBjYXNlICJaWlpaIjoKICAgICAgICAgICAgICAvLyBsaWtlIEVTVAogICAgICAgICAgICAgIHJldHVybiBkdC56b25lLm9mZnNldE5hbWUoZHQudHMsIHsgZm9ybWF0OiAic2hvcnQiLCBsb2NhbGU6IHRoaXMubG9jLmxvY2FsZSB9KTsKICAgICAgICAgICAgY2FzZSAiWlpaWloiOgogICAgICAgICAgICAgIC8vIGxpa2UgRWFzdGVybiBTdGFuZGFyZCBUaW1lCiAgICAgICAgICAgICAgcmV0dXJuIGR0LnpvbmUub2Zmc2V0TmFtZShkdC50cywgeyBmb3JtYXQ6ICJsb25nIiwgbG9jYWxlOiB0aGlzLmxvYy5sb2NhbGUgfSk7CiAgICAgICAgICAgIC8vIHpvbmUKICAgICAgICAgICAgY2FzZSAieiI6CiAgICAgICAgICAgICAgLy8gbGlrZSBBbWVyaWNhL05ld19Zb3JrCiAgICAgICAgICAgICAgcmV0dXJuIGR0LnpvbmVOYW1lOwogICAgICAgICAgICAvLyBtZXJpZGllbXMKICAgICAgICAgICAgY2FzZSAiYSI6CiAgICAgICAgICAgICAgcmV0dXJuIG1lcmlkaWVtKCk7CiAgICAgICAgICAgIC8vIGRhdGVzCiAgICAgICAgICAgIGNhc2UgImQiOgogICAgICAgICAgICAgIHJldHVybiB1c2VEYXRlVGltZUZvcm1hdHRlciA/IHN0cmluZyh7IGRheTogIm51bWVyaWMiIH0sICJkYXkiKSA6IHRoaXMubnVtKGR0LmRheSk7CiAgICAgICAgICAgIGNhc2UgImRkIjoKICAgICAgICAgICAgICByZXR1cm4gdXNlRGF0ZVRpbWVGb3JtYXR0ZXIgPyBzdHJpbmcoeyBkYXk6ICIyLWRpZ2l0IiB9LCAiZGF5IikgOiB0aGlzLm51bShkdC5kYXksIDIpOwogICAgICAgICAgICAvLyB3ZWVrZGF5cyAtIHN0YW5kYWxvbmUKICAgICAgICAgICAgY2FzZSAiYyI6CiAgICAgICAgICAgICAgLy8gbGlrZSAxCiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LndlZWtkYXkpOwogICAgICAgICAgICBjYXNlICJjY2MiOgogICAgICAgICAgICAgIC8vIGxpa2UgJ1R1ZXMnCiAgICAgICAgICAgICAgcmV0dXJuIHdlZWtkYXkoInNob3J0IiwgdHJ1ZSk7CiAgICAgICAgICAgIGNhc2UgImNjY2MiOgogICAgICAgICAgICAgIC8vIGxpa2UgJ1R1ZXNkYXknCiAgICAgICAgICAgICAgcmV0dXJuIHdlZWtkYXkoImxvbmciLCB0cnVlKTsKICAgICAgICAgICAgY2FzZSAiY2NjY2MiOgogICAgICAgICAgICAgIC8vIGxpa2UgJ1QnCiAgICAgICAgICAgICAgcmV0dXJuIHdlZWtkYXkoIm5hcnJvdyIsIHRydWUpOwogICAgICAgICAgICAvLyB3ZWVrZGF5cyAtIGZvcm1hdAogICAgICAgICAgICBjYXNlICJFIjoKICAgICAgICAgICAgICAvLyBsaWtlIDEKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQud2Vla2RheSk7CiAgICAgICAgICAgIGNhc2UgIkVFRSI6CiAgICAgICAgICAgICAgLy8gbGlrZSAnVHVlcycKICAgICAgICAgICAgICByZXR1cm4gd2Vla2RheSgic2hvcnQiLCBmYWxzZSk7CiAgICAgICAgICAgIGNhc2UgIkVFRUUiOgogICAgICAgICAgICAgIC8vIGxpa2UgJ1R1ZXNkYXknCiAgICAgICAgICAgICAgcmV0dXJuIHdlZWtkYXkoImxvbmciLCBmYWxzZSk7CiAgICAgICAgICAgIGNhc2UgIkVFRUVFIjoKICAgICAgICAgICAgICAvLyBsaWtlICdUJwogICAgICAgICAgICAgIHJldHVybiB3ZWVrZGF5KCJuYXJyb3ciLCBmYWxzZSk7CiAgICAgICAgICAgIC8vIG1vbnRocyAtIHN0YW5kYWxvbmUKICAgICAgICAgICAgY2FzZSAiTCI6CiAgICAgICAgICAgICAgLy8gbGlrZSAxCiAgICAgICAgICAgICAgcmV0dXJuIHVzZURhdGVUaW1lRm9ybWF0dGVyCiAgICAgICAgICAgICAgICA/IHN0cmluZyh7IG1vbnRoOiAibnVtZXJpYyIsIGRheTogIm51bWVyaWMiIH0sICJtb250aCIpCiAgICAgICAgICAgICAgICA6IHRoaXMubnVtKGR0Lm1vbnRoKTsKICAgICAgICAgICAgY2FzZSAiTEwiOgogICAgICAgICAgICAgIC8vIGxpa2UgMDEsIGRvZXNuJ3Qgc2VlbSB0byB3b3JrCiAgICAgICAgICAgICAgcmV0dXJuIHVzZURhdGVUaW1lRm9ybWF0dGVyCiAgICAgICAgICAgICAgICA/IHN0cmluZyh7IG1vbnRoOiAiMi1kaWdpdCIsIGRheTogIm51bWVyaWMiIH0sICJtb250aCIpCiAgICAgICAgICAgICAgICA6IHRoaXMubnVtKGR0Lm1vbnRoLCAyKTsKICAgICAgICAgICAgY2FzZSAiTExMIjoKICAgICAgICAgICAgICAvLyBsaWtlIEphbgogICAgICAgICAgICAgIHJldHVybiBtb250aCgic2hvcnQiLCB0cnVlKTsKICAgICAgICAgICAgY2FzZSAiTExMTCI6CiAgICAgICAgICAgICAgLy8gbGlrZSBKYW51YXJ5CiAgICAgICAgICAgICAgcmV0dXJuIG1vbnRoKCJsb25nIiwgdHJ1ZSk7CiAgICAgICAgICAgIGNhc2UgIkxMTExMIjoKICAgICAgICAgICAgICAvLyBsaWtlIEoKICAgICAgICAgICAgICByZXR1cm4gbW9udGgoIm5hcnJvdyIsIHRydWUpOwogICAgICAgICAgICAvLyBtb250aHMgLSBmb3JtYXQKICAgICAgICAgICAgY2FzZSAiTSI6CiAgICAgICAgICAgICAgLy8gbGlrZSAxCiAgICAgICAgICAgICAgcmV0dXJuIHVzZURhdGVUaW1lRm9ybWF0dGVyCiAgICAgICAgICAgICAgICA/IHN0cmluZyh7IG1vbnRoOiAibnVtZXJpYyIgfSwgIm1vbnRoIikKICAgICAgICAgICAgICAgIDogdGhpcy5udW0oZHQubW9udGgpOwogICAgICAgICAgICBjYXNlICJNTSI6CiAgICAgICAgICAgICAgLy8gbGlrZSAwMQogICAgICAgICAgICAgIHJldHVybiB1c2VEYXRlVGltZUZvcm1hdHRlcgogICAgICAgICAgICAgICAgPyBzdHJpbmcoeyBtb250aDogIjItZGlnaXQiIH0sICJtb250aCIpCiAgICAgICAgICAgICAgICA6IHRoaXMubnVtKGR0Lm1vbnRoLCAyKTsKICAgICAgICAgICAgY2FzZSAiTU1NIjoKICAgICAgICAgICAgICAvLyBsaWtlIEphbgogICAgICAgICAgICAgIHJldHVybiBtb250aCgic2hvcnQiLCBmYWxzZSk7CiAgICAgICAgICAgIGNhc2UgIk1NTU0iOgogICAgICAgICAgICAgIC8vIGxpa2UgSmFudWFyeQogICAgICAgICAgICAgIHJldHVybiBtb250aCgibG9uZyIsIGZhbHNlKTsKICAgICAgICAgICAgY2FzZSAiTU1NTU0iOgogICAgICAgICAgICAgIC8vIGxpa2UgSgogICAgICAgICAgICAgIHJldHVybiBtb250aCgibmFycm93IiwgZmFsc2UpOwogICAgICAgICAgICAvLyB5ZWFycwogICAgICAgICAgICBjYXNlICJ5IjoKICAgICAgICAgICAgICAvLyBsaWtlIDIwMTQKICAgICAgICAgICAgICByZXR1cm4gdXNlRGF0ZVRpbWVGb3JtYXR0ZXIgPyBzdHJpbmcoeyB5ZWFyOiAibnVtZXJpYyIgfSwgInllYXIiKSA6IHRoaXMubnVtKGR0LnllYXIpOwogICAgICAgICAgICBjYXNlICJ5eSI6CiAgICAgICAgICAgICAgLy8gbGlrZSAxNAogICAgICAgICAgICAgIHJldHVybiB1c2VEYXRlVGltZUZvcm1hdHRlcgogICAgICAgICAgICAgICAgPyBzdHJpbmcoeyB5ZWFyOiAiMi1kaWdpdCIgfSwgInllYXIiKQogICAgICAgICAgICAgICAgOiB0aGlzLm51bShkdC55ZWFyLnRvU3RyaW5nKCkuc2xpY2UoLTIpLCAyKTsKICAgICAgICAgICAgY2FzZSAieXl5eSI6CiAgICAgICAgICAgICAgLy8gbGlrZSAwMDEyCiAgICAgICAgICAgICAgcmV0dXJuIHVzZURhdGVUaW1lRm9ybWF0dGVyCiAgICAgICAgICAgICAgICA/IHN0cmluZyh7IHllYXI6ICJudW1lcmljIiB9LCAieWVhciIpCiAgICAgICAgICAgICAgICA6IHRoaXMubnVtKGR0LnllYXIsIDQpOwogICAgICAgICAgICBjYXNlICJ5eXl5eXkiOgogICAgICAgICAgICAgIC8vIGxpa2UgMDAwMDEyCiAgICAgICAgICAgICAgcmV0dXJuIHVzZURhdGVUaW1lRm9ybWF0dGVyCiAgICAgICAgICAgICAgICA/IHN0cmluZyh7IHllYXI6ICJudW1lcmljIiB9LCAieWVhciIpCiAgICAgICAgICAgICAgICA6IHRoaXMubnVtKGR0LnllYXIsIDYpOwogICAgICAgICAgICAvLyBlcmFzCiAgICAgICAgICAgIGNhc2UgIkciOgogICAgICAgICAgICAgIC8vIGxpa2UgQUQKICAgICAgICAgICAgICByZXR1cm4gZXJhKCJzaG9ydCIpOwogICAgICAgICAgICBjYXNlICJHRyI6CiAgICAgICAgICAgICAgLy8gbGlrZSBBbm5vIERvbWluaQogICAgICAgICAgICAgIHJldHVybiBlcmEoImxvbmciKTsKICAgICAgICAgICAgY2FzZSAiR0dHR0ciOgogICAgICAgICAgICAgIHJldHVybiBlcmEoIm5hcnJvdyIpOwogICAgICAgICAgICBjYXNlICJrayI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LndlZWtZZWFyLnRvU3RyaW5nKCkuc2xpY2UoLTIpLCAyKTsKICAgICAgICAgICAgY2FzZSAia2trayI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LndlZWtZZWFyLCA0KTsKICAgICAgICAgICAgY2FzZSAiVyI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LndlZWtOdW1iZXIpOwogICAgICAgICAgICBjYXNlICJXVyI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LndlZWtOdW1iZXIsIDIpOwogICAgICAgICAgICBjYXNlICJvIjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQub3JkaW5hbCk7CiAgICAgICAgICAgIGNhc2UgIm9vbyI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0Lm9yZGluYWwsIDMpOwogICAgICAgICAgICBjYXNlICJxIjoKICAgICAgICAgICAgICAvLyBsaWtlIDEKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oZHQucXVhcnRlcik7CiAgICAgICAgICAgIGNhc2UgInFxIjoKICAgICAgICAgICAgICAvLyBsaWtlIDAxCiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LnF1YXJ0ZXIsIDIpOwogICAgICAgICAgICBjYXNlICJYIjoKICAgICAgICAgICAgICByZXR1cm4gdGhpcy5udW0oTWF0aC5mbG9vcihkdC50cyAvIDEwMDApKTsKICAgICAgICAgICAgY2FzZSAieCI6CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGR0LnRzKTsKICAgICAgICAgICAgZGVmYXVsdDoKICAgICAgICAgICAgICByZXR1cm4gbWF5YmVNYWNybyh0b2tlbik7CiAgICAgICAgICB9CiAgICAgICAgfTsKCiAgICAgIHJldHVybiBzdHJpbmdpZnlUb2tlbnMoRm9ybWF0dGVyLnBhcnNlRm9ybWF0KGZtdCksIHRva2VuVG9TdHJpbmcpOwogICAgfQoKICAgIGZvcm1hdER1cmF0aW9uRnJvbVN0cmluZyhkdXIsIGZtdCkgewogICAgICBjb25zdCB0b2tlblRvRmllbGQgPSAodG9rZW4pID0+IHsKICAgICAgICAgIHN3aXRjaCAodG9rZW5bMF0pIHsKICAgICAgICAgICAgY2FzZSAiUyI6CiAgICAgICAgICAgICAgcmV0dXJuICJtaWxsaXNlY29uZCI7CiAgICAgICAgICAgIGNhc2UgInMiOgogICAgICAgICAgICAgIHJldHVybiAic2Vjb25kIjsKICAgICAgICAgICAgY2FzZSAibSI6CiAgICAgICAgICAgICAgcmV0dXJuICJtaW51dGUiOwogICAgICAgICAgICBjYXNlICJoIjoKICAgICAgICAgICAgICByZXR1cm4gImhvdXIiOwogICAgICAgICAgICBjYXNlICJkIjoKICAgICAgICAgICAgICByZXR1cm4gImRheSI7CiAgICAgICAgICAgIGNhc2UgInciOgogICAgICAgICAgICAgIHJldHVybiAid2VlayI7CiAgICAgICAgICAgIGNhc2UgIk0iOgogICAgICAgICAgICAgIHJldHVybiAibW9udGgiOwogICAgICAgICAgICBjYXNlICJ5IjoKICAgICAgICAgICAgICByZXR1cm4gInllYXIiOwogICAgICAgICAgICBkZWZhdWx0OgogICAgICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgdG9rZW5Ub1N0cmluZyA9IChsaWxkdXIpID0+ICh0b2tlbikgPT4gewogICAgICAgICAgY29uc3QgbWFwcGVkID0gdG9rZW5Ub0ZpZWxkKHRva2VuKTsKICAgICAgICAgIGlmIChtYXBwZWQpIHsKICAgICAgICAgICAgcmV0dXJuIHRoaXMubnVtKGxpbGR1ci5nZXQobWFwcGVkKSwgdG9rZW4ubGVuZ3RoKTsKICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgIHJldHVybiB0b2tlbjsKICAgICAgICAgIH0KICAgICAgICB9LAogICAgICAgIHRva2VucyA9IEZvcm1hdHRlci5wYXJzZUZvcm1hdChmbXQpLAogICAgICAgIHJlYWxUb2tlbnMgPSB0b2tlbnMucmVkdWNlKAogICAgICAgICAgKGZvdW5kLCB7IGxpdGVyYWwsIHZhbCB9KSA9PiAobGl0ZXJhbCA/IGZvdW5kIDogZm91bmQuY29uY2F0KHZhbCkpLAogICAgICAgICAgW10KICAgICAgICApLAogICAgICAgIGNvbGxhcHNlZCA9IGR1ci5zaGlmdFRvKC4uLnJlYWxUb2tlbnMubWFwKHRva2VuVG9GaWVsZCkuZmlsdGVyKCh0KSA9PiB0KSk7CiAgICAgIHJldHVybiBzdHJpbmdpZnlUb2tlbnModG9rZW5zLCB0b2tlblRvU3RyaW5nKGNvbGxhcHNlZCkpOwogICAgfQogIH0KCiAgY2xhc3MgSW52YWxpZCB7CiAgICBjb25zdHJ1Y3RvcihyZWFzb24sIGV4cGxhbmF0aW9uKSB7CiAgICAgIHRoaXMucmVhc29uID0gcmVhc29uOwogICAgICB0aGlzLmV4cGxhbmF0aW9uID0gZXhwbGFuYXRpb247CiAgICB9CgogICAgdG9NZXNzYWdlKCkgewogICAgICBpZiAodGhpcy5leHBsYW5hdGlvbikgewogICAgICAgIHJldHVybiBgJHt0aGlzLnJlYXNvbn06ICR7dGhpcy5leHBsYW5hdGlvbn1gOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiB0aGlzLnJlYXNvbjsKICAgICAgfQogICAgfQogIH0KCiAgLyoKICAgKiBUaGlzIGZpbGUgaGFuZGxlcyBwYXJzaW5nIGZvciB3ZWxsLXNwZWNpZmllZCBmb3JtYXRzLiBIZXJlJ3MgaG93IGl0IHdvcmtzOgogICAqIFR3byB0aGluZ3MgZ28gaW50byBwYXJzaW5nOiBhIHJlZ2V4IHRvIG1hdGNoIHdpdGggYW5kIGFuIGV4dHJhY3RvciB0byB0YWtlIGFwYXJ0IHRoZSBncm91cHMgaW4gdGhlIG1hdGNoLgogICAqIEFuIGV4dHJhY3RvciBpcyBqdXN0IGEgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIHJlZ2V4IG1hdGNoIGFycmF5IGFuZCByZXR1cm5zIGEgeyB5ZWFyOiAuLi4sIG1vbnRoOiAuLi4gfSBvYmplY3QKICAgKiBwYXJzZSgpIGRvZXMgdGhlIHdvcmsgb2YgZXhlY3V0aW5nIHRoZSByZWdleCBhbmQgYXBwbHlpbmcgdGhlIGV4dHJhY3Rvci4gSXQgdGFrZXMgbXVsdGlwbGUgcmVnZXgvZXh0cmFjdG9yIHBhaXJzIHRvIHRyeSBpbiBzZXF1ZW5jZS4KICAgKiBFeHRyYWN0b3JzIGNhbiB0YWtlIGEgImN1cnNvciIgcmVwcmVzZW50aW5nIHRoZSBvZmZzZXQgaW4gdGhlIG1hdGNoIHRvIGxvb2sgYXQuIFRoaXMgbWFrZXMgaXQgZWFzeSB0byBjb21iaW5lIGV4dHJhY3RvcnMuCiAgICogY29tYmluZUV4dHJhY3RvcnMoKSBkb2VzIHRoZSB3b3JrIG9mIGNvbWJpbmluZyB0aGVtLCBrZWVwaW5nIHRyYWNrIG9mIHRoZSBjdXJzb3IgdGhyb3VnaCBtdWx0aXBsZSBleHRyYWN0aW9ucy4KICAgKiBTb21lIGV4dHJhY3Rpb25zIGFyZSBzdXBlciBkdW1iIGFuZCBzaW1wbGVQYXJzZSBhbmQgZnJvbVN0cmluZ3MgaGVscCBEUlkgdGhlbS4KICAgKi8KCiAgY29uc3QgaWFuYVJlZ2V4ID0gL1tBLVphLXpfKy1dezEsMjU2fSg/Ojo/XC9bQS1aYS16MC05XystXXsxLDI1Nn0oPzpcL1tBLVphLXowLTlfKy1dezEsMjU2fSk/KT8vOwoKICBmdW5jdGlvbiBjb21iaW5lUmVnZXhlcyguLi5yZWdleGVzKSB7CiAgICBjb25zdCBmdWxsID0gcmVnZXhlcy5yZWR1Y2UoKGYsIHIpID0+IGYgKyByLnNvdXJjZSwgIiIpOwogICAgcmV0dXJuIFJlZ0V4cChgXiR7ZnVsbH0kYCk7CiAgfQoKICBmdW5jdGlvbiBjb21iaW5lRXh0cmFjdG9ycyguLi5leHRyYWN0b3JzKSB7CiAgICByZXR1cm4gKG0pID0+CiAgICAgIGV4dHJhY3RvcnMKICAgICAgICAucmVkdWNlKAogICAgICAgICAgKFttZXJnZWRWYWxzLCBtZXJnZWRab25lLCBjdXJzb3JdLCBleCkgPT4gewogICAgICAgICAgICBjb25zdCBbdmFsLCB6b25lLCBuZXh0XSA9IGV4KG0sIGN1cnNvcik7CiAgICAgICAgICAgIHJldHVybiBbeyAuLi5tZXJnZWRWYWxzLCAuLi52YWwgfSwgem9uZSB8fCBtZXJnZWRab25lLCBuZXh0XTsKICAgICAgICAgIH0sCiAgICAgICAgICBbe30sIG51bGwsIDFdCiAgICAgICAgKQogICAgICAgIC5zbGljZSgwLCAyKTsKICB9CgogIGZ1bmN0aW9uIHBhcnNlKHMsIC4uLnBhdHRlcm5zKSB7CiAgICBpZiAocyA9PSBudWxsKSB7CiAgICAgIHJldHVybiBbbnVsbCwgbnVsbF07CiAgICB9CgogICAgZm9yIChjb25zdCBbcmVnZXgsIGV4dHJhY3Rvcl0gb2YgcGF0dGVybnMpIHsKICAgICAgY29uc3QgbSA9IHJlZ2V4LmV4ZWMocyk7CiAgICAgIGlmIChtKSB7CiAgICAgICAgcmV0dXJuIGV4dHJhY3RvcihtKTsKICAgICAgfQogICAgfQogICAgcmV0dXJuIFtudWxsLCBudWxsXTsKICB9CgogIGZ1bmN0aW9uIHNpbXBsZVBhcnNlKC4uLmtleXMpIHsKICAgIHJldHVybiAobWF0Y2gsIGN1cnNvcikgPT4gewogICAgICBjb25zdCByZXQgPSB7fTsKICAgICAgbGV0IGk7CgogICAgICBmb3IgKGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykgewogICAgICAgIHJldFtrZXlzW2ldXSA9IHBhcnNlSW50ZWdlcihtYXRjaFtjdXJzb3IgKyBpXSk7CiAgICAgIH0KICAgICAgcmV0dXJuIFtyZXQsIG51bGwsIGN1cnNvciArIGldOwogICAgfTsKICB9CgogIC8vIElTTyBhbmQgU1FMIHBhcnNpbmcKICBjb25zdCBvZmZzZXRSZWdleCA9IC8oPzooWil8KFsrLV1cZFxkKSg/Ojo/KFxkXGQpKT8pLzsKICBjb25zdCBpc29FeHRlbmRlZFpvbmUgPSBgKD86JHtvZmZzZXRSZWdleC5zb3VyY2V9Pyg/OlxcWygke2lhbmFSZWdleC5zb3VyY2V9KVxcXSk/KT9gOwogIGNvbnN0IGlzb1RpbWVCYXNlUmVnZXggPSAvKFxkXGQpKD86Oj8oXGRcZCkoPzo6PyhcZFxkKSg/OlsuLF0oXGR7MSwzMH0pKT8pPyk/LzsKICBjb25zdCBpc29UaW1lUmVnZXggPSBSZWdFeHAoYCR7aXNvVGltZUJhc2VSZWdleC5zb3VyY2V9JHtpc29FeHRlbmRlZFpvbmV9YCk7CiAgY29uc3QgaXNvVGltZUV4dGVuc2lvblJlZ2V4ID0gUmVnRXhwKGAoPzpUJHtpc29UaW1lUmVnZXguc291cmNlfSk/YCk7CiAgY29uc3QgaXNvWW1kUmVnZXggPSAvKFsrLV1cZHs2fXxcZHs0fSkoPzotPyhcZFxkKSg/Oi0/KFxkXGQpKT8pPy87CiAgY29uc3QgaXNvV2Vla1JlZ2V4ID0gLyhcZHs0fSktP1coXGRcZCkoPzotPyhcZCkpPy87CiAgY29uc3QgaXNvT3JkaW5hbFJlZ2V4ID0gLyhcZHs0fSktPyhcZHszfSkvOwogIGNvbnN0IGV4dHJhY3RJU09XZWVrRGF0YSA9IHNpbXBsZVBhcnNlKCJ3ZWVrWWVhciIsICJ3ZWVrTnVtYmVyIiwgIndlZWtEYXkiKTsKICBjb25zdCBleHRyYWN0SVNPT3JkaW5hbERhdGEgPSBzaW1wbGVQYXJzZSgieWVhciIsICJvcmRpbmFsIik7CiAgY29uc3Qgc3FsWW1kUmVnZXggPSAvKFxkezR9KS0oXGRcZCktKFxkXGQpLzsgLy8gZHVtYmVkLWRvd24gdmVyc2lvbiBvZiB0aGUgSVNPIG9uZQogIGNvbnN0IHNxbFRpbWVSZWdleCA9IFJlZ0V4cCgKICAgIGAke2lzb1RpbWVCYXNlUmVnZXguc291cmNlfSA/KD86JHtvZmZzZXRSZWdleC5zb3VyY2V9fCgke2lhbmFSZWdleC5zb3VyY2V9KSk/YAogICk7CiAgY29uc3Qgc3FsVGltZUV4dGVuc2lvblJlZ2V4ID0gUmVnRXhwKGAoPzogJHtzcWxUaW1lUmVnZXguc291cmNlfSk/YCk7CgogIGZ1bmN0aW9uIGludChtYXRjaCwgcG9zLCBmYWxsYmFjaykgewogICAgY29uc3QgbSA9IG1hdGNoW3Bvc107CiAgICByZXR1cm4gaXNVbmRlZmluZWQobSkgPyBmYWxsYmFjayA6IHBhcnNlSW50ZWdlcihtKTsKICB9CgogIGZ1bmN0aW9uIGV4dHJhY3RJU09ZbWQobWF0Y2gsIGN1cnNvcikgewogICAgY29uc3QgaXRlbSA9IHsKICAgICAgeWVhcjogaW50KG1hdGNoLCBjdXJzb3IpLAogICAgICBtb250aDogaW50KG1hdGNoLCBjdXJzb3IgKyAxLCAxKSwKICAgICAgZGF5OiBpbnQobWF0Y2gsIGN1cnNvciArIDIsIDEpLAogICAgfTsKCiAgICByZXR1cm4gW2l0ZW0sIG51bGwsIGN1cnNvciArIDNdOwogIH0KCiAgZnVuY3Rpb24gZXh0cmFjdElTT1RpbWUobWF0Y2gsIGN1cnNvcikgewogICAgY29uc3QgaXRlbSA9IHsKICAgICAgaG91cnM6IGludChtYXRjaCwgY3Vyc29yLCAwKSwKICAgICAgbWludXRlczogaW50KG1hdGNoLCBjdXJzb3IgKyAxLCAwKSwKICAgICAgc2Vjb25kczogaW50KG1hdGNoLCBjdXJzb3IgKyAyLCAwKSwKICAgICAgbWlsbGlzZWNvbmRzOiBwYXJzZU1pbGxpcyhtYXRjaFtjdXJzb3IgKyAzXSksCiAgICB9OwoKICAgIHJldHVybiBbaXRlbSwgbnVsbCwgY3Vyc29yICsgNF07CiAgfQoKICBmdW5jdGlvbiBleHRyYWN0SVNPT2Zmc2V0KG1hdGNoLCBjdXJzb3IpIHsKICAgIGNvbnN0IGxvY2FsID0gIW1hdGNoW2N1cnNvcl0gJiYgIW1hdGNoW2N1cnNvciArIDFdLAogICAgICBmdWxsT2Zmc2V0ID0gc2lnbmVkT2Zmc2V0KG1hdGNoW2N1cnNvciArIDFdLCBtYXRjaFtjdXJzb3IgKyAyXSksCiAgICAgIHpvbmUgPSBsb2NhbCA/IG51bGwgOiBGaXhlZE9mZnNldFpvbmUuaW5zdGFuY2UoZnVsbE9mZnNldCk7CiAgICByZXR1cm4gW3t9LCB6b25lLCBjdXJzb3IgKyAzXTsKICB9CgogIGZ1bmN0aW9uIGV4dHJhY3RJQU5BWm9uZShtYXRjaCwgY3Vyc29yKSB7CiAgICBjb25zdCB6b25lID0gbWF0Y2hbY3Vyc29yXSA/IElBTkFab25lLmNyZWF0ZShtYXRjaFtjdXJzb3JdKSA6IG51bGw7CiAgICByZXR1cm4gW3t9LCB6b25lLCBjdXJzb3IgKyAxXTsKICB9CgogIC8vIElTTyB0aW1lIHBhcnNpbmcKCiAgY29uc3QgaXNvVGltZU9ubHkgPSBSZWdFeHAoYF5UPyR7aXNvVGltZUJhc2VSZWdleC5zb3VyY2V9JGApOwoKICAvLyBJU08gZHVyYXRpb24gcGFyc2luZwoKICBjb25zdCBpc29EdXJhdGlvbiA9CiAgICAvXi0/UCg/Oig/OigtP1xkezEsMjB9KD86XC5cZHsxLDIwfSk/KVkpPyg/OigtP1xkezEsMjB9KD86XC5cZHsxLDIwfSk/KU0pPyg/OigtP1xkezEsMjB9KD86XC5cZHsxLDIwfSk/KVcpPyg/OigtP1xkezEsMjB9KD86XC5cZHsxLDIwfSk/KUQpPyg/OlQoPzooLT9cZHsxLDIwfSg/OlwuXGR7MSwyMH0pPylIKT8oPzooLT9cZHsxLDIwfSg/OlwuXGR7MSwyMH0pPylNKT8oPzooLT9cZHsxLDIwfSkoPzpbLixdKC0/XGR7MSwyMH0pKT9TKT8pPykkLzsKCiAgZnVuY3Rpb24gZXh0cmFjdElTT0R1cmF0aW9uKG1hdGNoKSB7CiAgICBjb25zdCBbcywgeWVhclN0ciwgbW9udGhTdHIsIHdlZWtTdHIsIGRheVN0ciwgaG91clN0ciwgbWludXRlU3RyLCBzZWNvbmRTdHIsIG1pbGxpc2Vjb25kc1N0cl0gPQogICAgICBtYXRjaDsKCiAgICBjb25zdCBoYXNOZWdhdGl2ZVByZWZpeCA9IHNbMF0gPT09ICItIjsKICAgIGNvbnN0IG5lZ2F0aXZlU2Vjb25kcyA9IHNlY29uZFN0ciAmJiBzZWNvbmRTdHJbMF0gPT09ICItIjsKCiAgICBjb25zdCBtYXliZU5lZ2F0ZSA9IChudW0sIGZvcmNlID0gZmFsc2UpID0+CiAgICAgIG51bSAhPT0gdW5kZWZpbmVkICYmIChmb3JjZSB8fCAobnVtICYmIGhhc05lZ2F0aXZlUHJlZml4KSkgPyAtbnVtIDogbnVtOwoKICAgIHJldHVybiBbCiAgICAgIHsKICAgICAgICB5ZWFyczogbWF5YmVOZWdhdGUocGFyc2VGbG9hdGluZyh5ZWFyU3RyKSksCiAgICAgICAgbW9udGhzOiBtYXliZU5lZ2F0ZShwYXJzZUZsb2F0aW5nKG1vbnRoU3RyKSksCiAgICAgICAgd2Vla3M6IG1heWJlTmVnYXRlKHBhcnNlRmxvYXRpbmcod2Vla1N0cikpLAogICAgICAgIGRheXM6IG1heWJlTmVnYXRlKHBhcnNlRmxvYXRpbmcoZGF5U3RyKSksCiAgICAgICAgaG91cnM6IG1heWJlTmVnYXRlKHBhcnNlRmxvYXRpbmcoaG91clN0cikpLAogICAgICAgIG1pbnV0ZXM6IG1heWJlTmVnYXRlKHBhcnNlRmxvYXRpbmcobWludXRlU3RyKSksCiAgICAgICAgc2Vjb25kczogbWF5YmVOZWdhdGUocGFyc2VGbG9hdGluZyhzZWNvbmRTdHIpLCBzZWNvbmRTdHIgPT09ICItMCIpLAogICAgICAgIG1pbGxpc2Vjb25kczogbWF5YmVOZWdhdGUocGFyc2VNaWxsaXMobWlsbGlzZWNvbmRzU3RyKSwgbmVnYXRpdmVTZWNvbmRzKSwKICAgICAgfSwKICAgIF07CiAgfQoKICAvLyBUaGVzZSBhcmUgYSBsaXR0bGUgYnJhaW5kZWFkLiBFRFQgKnNob3VsZCogdGVsbCB1cyB0aGF0IHdlJ3JlIGluLCBzYXksIEFtZXJpY2EvTmV3X1lvcmsKICAvLyBhbmQgbm90IGp1c3QgdGhhdCB3ZSdyZSBpbiAtMjQwICpyaWdodCBub3cqLiBCdXQgc2luY2UgSSBkb24ndCB0aGluayB0aGVzZSBhcmUgdXNlZCB0aGF0IG9mdGVuCiAgLy8gSSdtIGp1c3QgZ29pbmcgdG8gaWdub3JlIHRoYXQKICBjb25zdCBvYnNPZmZzZXRzID0gewogICAgR01UOiAwLAogICAgRURUOiAtNCAqIDYwLAogICAgRVNUOiAtNSAqIDYwLAogICAgQ0RUOiAtNSAqIDYwLAogICAgQ1NUOiAtNiAqIDYwLAogICAgTURUOiAtNiAqIDYwLAogICAgTVNUOiAtNyAqIDYwLAogICAgUERUOiAtNyAqIDYwLAogICAgUFNUOiAtOCAqIDYwLAogIH07CgogIGZ1bmN0aW9uIGZyb21TdHJpbmdzKHdlZWtkYXlTdHIsIHllYXJTdHIsIG1vbnRoU3RyLCBkYXlTdHIsIGhvdXJTdHIsIG1pbnV0ZVN0ciwgc2Vjb25kU3RyKSB7CiAgICBjb25zdCByZXN1bHQgPSB7CiAgICAgIHllYXI6IHllYXJTdHIubGVuZ3RoID09PSAyID8gdW50cnVuY2F0ZVllYXIocGFyc2VJbnRlZ2VyKHllYXJTdHIpKSA6IHBhcnNlSW50ZWdlcih5ZWFyU3RyKSwKICAgICAgbW9udGg6IG1vbnRoc1Nob3J0LmluZGV4T2YobW9udGhTdHIpICsgMSwKICAgICAgZGF5OiBwYXJzZUludGVnZXIoZGF5U3RyKSwKICAgICAgaG91cjogcGFyc2VJbnRlZ2VyKGhvdXJTdHIpLAogICAgICBtaW51dGU6IHBhcnNlSW50ZWdlcihtaW51dGVTdHIpLAogICAgfTsKCiAgICBpZiAoc2Vjb25kU3RyKSByZXN1bHQuc2Vjb25kID0gcGFyc2VJbnRlZ2VyKHNlY29uZFN0cik7CiAgICBpZiAod2Vla2RheVN0cikgewogICAgICByZXN1bHQud2Vla2RheSA9CiAgICAgICAgd2Vla2RheVN0ci5sZW5ndGggPiAzCiAgICAgICAgICA/IHdlZWtkYXlzTG9uZy5pbmRleE9mKHdlZWtkYXlTdHIpICsgMQogICAgICAgICAgOiB3ZWVrZGF5c1Nob3J0LmluZGV4T2Yod2Vla2RheVN0cikgKyAxOwogICAgfQoKICAgIHJldHVybiByZXN1bHQ7CiAgfQoKICAvLyBSRkMgMjgyMi81MzIyCiAgY29uc3QgcmZjMjgyMiA9CiAgICAvXig/OihNb258VHVlfFdlZHxUaHV8RnJpfFNhdHxTdW4pLFxzKT8oXGR7MSwyfSlccyhKYW58RmVifE1hcnxBcHJ8TWF5fEp1bnxKdWx8QXVnfFNlcHxPY3R8Tm92fERlYylccyhcZHsyLDR9KVxzKFxkXGQpOihcZFxkKSg/OjooXGRcZCkpP1xzKD86KFVUfEdNVHxbRUNNUF1bU0RdVCl8KFtael0pfCg/OihbKy1dXGRcZCkoXGRcZCkpKSQvOwoKICBmdW5jdGlvbiBleHRyYWN0UkZDMjgyMihtYXRjaCkgewogICAgY29uc3QgWwogICAgICAgICwKICAgICAgICB3ZWVrZGF5U3RyLAogICAgICAgIGRheVN0ciwKICAgICAgICBtb250aFN0ciwKICAgICAgICB5ZWFyU3RyLAogICAgICAgIGhvdXJTdHIsCiAgICAgICAgbWludXRlU3RyLAogICAgICAgIHNlY29uZFN0ciwKICAgICAgICBvYnNPZmZzZXQsCiAgICAgICAgbWlsT2Zmc2V0LAogICAgICAgIG9mZkhvdXJTdHIsCiAgICAgICAgb2ZmTWludXRlU3RyLAogICAgICBdID0gbWF0Y2gsCiAgICAgIHJlc3VsdCA9IGZyb21TdHJpbmdzKHdlZWtkYXlTdHIsIHllYXJTdHIsIG1vbnRoU3RyLCBkYXlTdHIsIGhvdXJTdHIsIG1pbnV0ZVN0ciwgc2Vjb25kU3RyKTsKCiAgICBsZXQgb2Zmc2V0OwogICAgaWYgKG9ic09mZnNldCkgewogICAgICBvZmZzZXQgPSBvYnNPZmZzZXRzW29ic09mZnNldF07CiAgICB9IGVsc2UgaWYgKG1pbE9mZnNldCkgewogICAgICBvZmZzZXQgPSAwOwogICAgfSBlbHNlIHsKICAgICAgb2Zmc2V0ID0gc2lnbmVkT2Zmc2V0KG9mZkhvdXJTdHIsIG9mZk1pbnV0ZVN0cik7CiAgICB9CgogICAgcmV0dXJuIFtyZXN1bHQsIG5ldyBGaXhlZE9mZnNldFpvbmUob2Zmc2V0KV07CiAgfQoKICBmdW5jdGlvbiBwcmVwcm9jZXNzUkZDMjgyMihzKSB7CiAgICAvLyBSZW1vdmUgY29tbWVudHMgYW5kIGZvbGRpbmcgd2hpdGVzcGFjZSBhbmQgcmVwbGFjZSBtdWx0aXBsZS1zcGFjZXMgd2l0aCBhIHNpbmdsZSBzcGFjZQogICAgcmV0dXJuIHMKICAgICAgLnJlcGxhY2UoL1woW14oKV0qXCl8W1xuXHRdL2csICIgIikKICAgICAgLnJlcGxhY2UoLyhcc1xzKykvZywgIiAiKQogICAgICAudHJpbSgpOwogIH0KCiAgLy8gaHR0cCBkYXRlCgogIGNvbnN0IHJmYzExMjMgPQogICAgICAvXihNb258VHVlfFdlZHxUaHV8RnJpfFNhdHxTdW4pLCAoXGRcZCkgKEphbnxGZWJ8TWFyfEFwcnxNYXl8SnVufEp1bHxBdWd8U2VwfE9jdHxOb3Z8RGVjKSAoXGR7NH0pIChcZFxkKTooXGRcZCk6KFxkXGQpIEdNVCQvLAogICAgcmZjODUwID0KICAgICAgL14oTW9uZGF5fFR1ZXNkYXl8V2VkbmVzZGF5fFRodXJzZGF5fEZyaWRheXxTYXR1cmRheXxTdW5kYXkpLCAoXGRcZCktKEphbnxGZWJ8TWFyfEFwcnxNYXl8SnVufEp1bHxBdWd8U2VwfE9jdHxOb3Z8RGVjKS0oXGRcZCkgKFxkXGQpOihcZFxkKTooXGRcZCkgR01UJC8sCiAgICBhc2NpaSA9CiAgICAgIC9eKE1vbnxUdWV8V2VkfFRodXxGcml8U2F0fFN1bikgKEphbnxGZWJ8TWFyfEFwcnxNYXl8SnVufEp1bHxBdWd8U2VwfE9jdHxOb3Z8RGVjKSAoIFxkfFxkXGQpIChcZFxkKTooXGRcZCk6KFxkXGQpIChcZHs0fSkkLzsKCiAgZnVuY3Rpb24gZXh0cmFjdFJGQzExMjNPcjg1MChtYXRjaCkgewogICAgY29uc3QgWywgd2Vla2RheVN0ciwgZGF5U3RyLCBtb250aFN0ciwgeWVhclN0ciwgaG91clN0ciwgbWludXRlU3RyLCBzZWNvbmRTdHJdID0gbWF0Y2gsCiAgICAgIHJlc3VsdCA9IGZyb21TdHJpbmdzKHdlZWtkYXlTdHIsIHllYXJTdHIsIG1vbnRoU3RyLCBkYXlTdHIsIGhvdXJTdHIsIG1pbnV0ZVN0ciwgc2Vjb25kU3RyKTsKICAgIHJldHVybiBbcmVzdWx0LCBGaXhlZE9mZnNldFpvbmUudXRjSW5zdGFuY2VdOwogIH0KCiAgZnVuY3Rpb24gZXh0cmFjdEFTQ0lJKG1hdGNoKSB7CiAgICBjb25zdCBbLCB3ZWVrZGF5U3RyLCBtb250aFN0ciwgZGF5U3RyLCBob3VyU3RyLCBtaW51dGVTdHIsIHNlY29uZFN0ciwgeWVhclN0cl0gPSBtYXRjaCwKICAgICAgcmVzdWx0ID0gZnJvbVN0cmluZ3Mod2Vla2RheVN0ciwgeWVhclN0ciwgbW9udGhTdHIsIGRheVN0ciwgaG91clN0ciwgbWludXRlU3RyLCBzZWNvbmRTdHIpOwogICAgcmV0dXJuIFtyZXN1bHQsIEZpeGVkT2Zmc2V0Wm9uZS51dGNJbnN0YW5jZV07CiAgfQoKICBjb25zdCBpc29ZbWRXaXRoVGltZUV4dGVuc2lvblJlZ2V4ID0gY29tYmluZVJlZ2V4ZXMoaXNvWW1kUmVnZXgsIGlzb1RpbWVFeHRlbnNpb25SZWdleCk7CiAgY29uc3QgaXNvV2Vla1dpdGhUaW1lRXh0ZW5zaW9uUmVnZXggPSBjb21iaW5lUmVnZXhlcyhpc29XZWVrUmVnZXgsIGlzb1RpbWVFeHRlbnNpb25SZWdleCk7CiAgY29uc3QgaXNvT3JkaW5hbFdpdGhUaW1lRXh0ZW5zaW9uUmVnZXggPSBjb21iaW5lUmVnZXhlcyhpc29PcmRpbmFsUmVnZXgsIGlzb1RpbWVFeHRlbnNpb25SZWdleCk7CiAgY29uc3QgaXNvVGltZUNvbWJpbmVkUmVnZXggPSBjb21iaW5lUmVnZXhlcyhpc29UaW1lUmVnZXgpOwoKICBjb25zdCBleHRyYWN0SVNPWW1kVGltZUFuZE9mZnNldCA9IGNvbWJpbmVFeHRyYWN0b3JzKAogICAgZXh0cmFjdElTT1ltZCwKICAgIGV4dHJhY3RJU09UaW1lLAogICAgZXh0cmFjdElTT09mZnNldCwKICAgIGV4dHJhY3RJQU5BWm9uZQogICk7CiAgY29uc3QgZXh0cmFjdElTT1dlZWtUaW1lQW5kT2Zmc2V0ID0gY29tYmluZUV4dHJhY3RvcnMoCiAgICBleHRyYWN0SVNPV2Vla0RhdGEsCiAgICBleHRyYWN0SVNPVGltZSwKICAgIGV4dHJhY3RJU09PZmZzZXQsCiAgICBleHRyYWN0SUFOQVpvbmUKICApOwogIGNvbnN0IGV4dHJhY3RJU09PcmRpbmFsRGF0ZUFuZFRpbWUgPSBjb21iaW5lRXh0cmFjdG9ycygKICAgIGV4dHJhY3RJU09PcmRpbmFsRGF0YSwKICAgIGV4dHJhY3RJU09UaW1lLAogICAgZXh0cmFjdElTT09mZnNldCwKICAgIGV4dHJhY3RJQU5BWm9uZQogICk7CiAgY29uc3QgZXh0cmFjdElTT1RpbWVBbmRPZmZzZXQgPSBjb21iaW5lRXh0cmFjdG9ycygKICAgIGV4dHJhY3RJU09UaW1lLAogICAgZXh0cmFjdElTT09mZnNldCwKICAgIGV4dHJhY3RJQU5BWm9uZQogICk7CgogIC8qCiAgICogQHByaXZhdGUKICAgKi8KCiAgZnVuY3Rpb24gcGFyc2VJU09EYXRlKHMpIHsKICAgIHJldHVybiBwYXJzZSgKICAgICAgcywKICAgICAgW2lzb1ltZFdpdGhUaW1lRXh0ZW5zaW9uUmVnZXgsIGV4dHJhY3RJU09ZbWRUaW1lQW5kT2Zmc2V0XSwKICAgICAgW2lzb1dlZWtXaXRoVGltZUV4dGVuc2lvblJlZ2V4LCBleHRyYWN0SVNPV2Vla1RpbWVBbmRPZmZzZXRdLAogICAgICBbaXNvT3JkaW5hbFdpdGhUaW1lRXh0ZW5zaW9uUmVnZXgsIGV4dHJhY3RJU09PcmRpbmFsRGF0ZUFuZFRpbWVdLAogICAgICBbaXNvVGltZUNvbWJpbmVkUmVnZXgsIGV4dHJhY3RJU09UaW1lQW5kT2Zmc2V0XQogICAgKTsKICB9CgogIGZ1bmN0aW9uIHBhcnNlUkZDMjgyMkRhdGUocykgewogICAgcmV0dXJuIHBhcnNlKHByZXByb2Nlc3NSRkMyODIyKHMpLCBbcmZjMjgyMiwgZXh0cmFjdFJGQzI4MjJdKTsKICB9CgogIGZ1bmN0aW9uIHBhcnNlSFRUUERhdGUocykgewogICAgcmV0dXJuIHBhcnNlKAogICAgICBzLAogICAgICBbcmZjMTEyMywgZXh0cmFjdFJGQzExMjNPcjg1MF0sCiAgICAgIFtyZmM4NTAsIGV4dHJhY3RSRkMxMTIzT3I4NTBdLAogICAgICBbYXNjaWksIGV4dHJhY3RBU0NJSV0KICAgICk7CiAgfQoKICBmdW5jdGlvbiBwYXJzZUlTT0R1cmF0aW9uKHMpIHsKICAgIHJldHVybiBwYXJzZShzLCBbaXNvRHVyYXRpb24sIGV4dHJhY3RJU09EdXJhdGlvbl0pOwogIH0KCiAgY29uc3QgZXh0cmFjdElTT1RpbWVPbmx5ID0gY29tYmluZUV4dHJhY3RvcnMoZXh0cmFjdElTT1RpbWUpOwoKICBmdW5jdGlvbiBwYXJzZUlTT1RpbWVPbmx5KHMpIHsKICAgIHJldHVybiBwYXJzZShzLCBbaXNvVGltZU9ubHksIGV4dHJhY3RJU09UaW1lT25seV0pOwogIH0KCiAgY29uc3Qgc3FsWW1kV2l0aFRpbWVFeHRlbnNpb25SZWdleCA9IGNvbWJpbmVSZWdleGVzKHNxbFltZFJlZ2V4LCBzcWxUaW1lRXh0ZW5zaW9uUmVnZXgpOwogIGNvbnN0IHNxbFRpbWVDb21iaW5lZFJlZ2V4ID0gY29tYmluZVJlZ2V4ZXMoc3FsVGltZVJlZ2V4KTsKCiAgY29uc3QgZXh0cmFjdElTT1RpbWVPZmZzZXRBbmRJQU5BWm9uZSA9IGNvbWJpbmVFeHRyYWN0b3JzKAogICAgZXh0cmFjdElTT1RpbWUsCiAgICBleHRyYWN0SVNPT2Zmc2V0LAogICAgZXh0cmFjdElBTkFab25lCiAgKTsKCiAgZnVuY3Rpb24gcGFyc2VTUUwocykgewogICAgcmV0dXJuIHBhcnNlKAogICAgICBzLAogICAgICBbc3FsWW1kV2l0aFRpbWVFeHRlbnNpb25SZWdleCwgZXh0cmFjdElTT1ltZFRpbWVBbmRPZmZzZXRdLAogICAgICBbc3FsVGltZUNvbWJpbmVkUmVnZXgsIGV4dHJhY3RJU09UaW1lT2Zmc2V0QW5kSUFOQVpvbmVdCiAgICApOwogIH0KCiAgY29uc3QgSU5WQUxJRCQyID0gIkludmFsaWQgRHVyYXRpb24iOwoKICAvLyB1bml0IGNvbnZlcnNpb24gY29uc3RhbnRzCiAgY29uc3QgbG93T3JkZXJNYXRyaXggPSB7CiAgICAgIHdlZWtzOiB7CiAgICAgICAgZGF5czogNywKICAgICAgICBob3VyczogNyAqIDI0LAogICAgICAgIG1pbnV0ZXM6IDcgKiAyNCAqIDYwLAogICAgICAgIHNlY29uZHM6IDcgKiAyNCAqIDYwICogNjAsCiAgICAgICAgbWlsbGlzZWNvbmRzOiA3ICogMjQgKiA2MCAqIDYwICogMTAwMCwKICAgICAgfSwKICAgICAgZGF5czogewogICAgICAgIGhvdXJzOiAyNCwKICAgICAgICBtaW51dGVzOiAyNCAqIDYwLAogICAgICAgIHNlY29uZHM6IDI0ICogNjAgKiA2MCwKICAgICAgICBtaWxsaXNlY29uZHM6IDI0ICogNjAgKiA2MCAqIDEwMDAsCiAgICAgIH0sCiAgICAgIGhvdXJzOiB7IG1pbnV0ZXM6IDYwLCBzZWNvbmRzOiA2MCAqIDYwLCBtaWxsaXNlY29uZHM6IDYwICogNjAgKiAxMDAwIH0sCiAgICAgIG1pbnV0ZXM6IHsgc2Vjb25kczogNjAsIG1pbGxpc2Vjb25kczogNjAgKiAxMDAwIH0sCiAgICAgIHNlY29uZHM6IHsgbWlsbGlzZWNvbmRzOiAxMDAwIH0sCiAgICB9LAogICAgY2FzdWFsTWF0cml4ID0gewogICAgICB5ZWFyczogewogICAgICAgIHF1YXJ0ZXJzOiA0LAogICAgICAgIG1vbnRoczogMTIsCiAgICAgICAgd2Vla3M6IDUyLAogICAgICAgIGRheXM6IDM2NSwKICAgICAgICBob3VyczogMzY1ICogMjQsCiAgICAgICAgbWludXRlczogMzY1ICogMjQgKiA2MCwKICAgICAgICBzZWNvbmRzOiAzNjUgKiAyNCAqIDYwICogNjAsCiAgICAgICAgbWlsbGlzZWNvbmRzOiAzNjUgKiAyNCAqIDYwICogNjAgKiAxMDAwLAogICAgICB9LAogICAgICBxdWFydGVyczogewogICAgICAgIG1vbnRoczogMywKICAgICAgICB3ZWVrczogMTMsCiAgICAgICAgZGF5czogOTEsCiAgICAgICAgaG91cnM6IDkxICogMjQsCiAgICAgICAgbWludXRlczogOTEgKiAyNCAqIDYwLAogICAgICAgIHNlY29uZHM6IDkxICogMjQgKiA2MCAqIDYwLAogICAgICAgIG1pbGxpc2Vjb25kczogOTEgKiAyNCAqIDYwICogNjAgKiAxMDAwLAogICAgICB9LAogICAgICBtb250aHM6IHsKICAgICAgICB3ZWVrczogNCwKICAgICAgICBkYXlzOiAzMCwKICAgICAgICBob3VyczogMzAgKiAyNCwKICAgICAgICBtaW51dGVzOiAzMCAqIDI0ICogNjAsCiAgICAgICAgc2Vjb25kczogMzAgKiAyNCAqIDYwICogNjAsCiAgICAgICAgbWlsbGlzZWNvbmRzOiAzMCAqIDI0ICogNjAgKiA2MCAqIDEwMDAsCiAgICAgIH0sCgogICAgICAuLi5sb3dPcmRlck1hdHJpeCwKICAgIH0sCiAgICBkYXlzSW5ZZWFyQWNjdXJhdGUgPSAxNDYwOTcuMCAvIDQwMCwKICAgIGRheXNJbk1vbnRoQWNjdXJhdGUgPSAxNDYwOTcuMCAvIDQ4MDAsCiAgICBhY2N1cmF0ZU1hdHJpeCA9IHsKICAgICAgeWVhcnM6IHsKICAgICAgICBxdWFydGVyczogNCwKICAgICAgICBtb250aHM6IDEyLAogICAgICAgIHdlZWtzOiBkYXlzSW5ZZWFyQWNjdXJhdGUgLyA3LAogICAgICAgIGRheXM6IGRheXNJblllYXJBY2N1cmF0ZSwKICAgICAgICBob3VyczogZGF5c0luWWVhckFjY3VyYXRlICogMjQsCiAgICAgICAgbWludXRlczogZGF5c0luWWVhckFjY3VyYXRlICogMjQgKiA2MCwKICAgICAgICBzZWNvbmRzOiBkYXlzSW5ZZWFyQWNjdXJhdGUgKiAyNCAqIDYwICogNjAsCiAgICAgICAgbWlsbGlzZWNvbmRzOiBkYXlzSW5ZZWFyQWNjdXJhdGUgKiAyNCAqIDYwICogNjAgKiAxMDAwLAogICAgICB9LAogICAgICBxdWFydGVyczogewogICAgICAgIG1vbnRoczogMywKICAgICAgICB3ZWVrczogZGF5c0luWWVhckFjY3VyYXRlIC8gMjgsCiAgICAgICAgZGF5czogZGF5c0luWWVhckFjY3VyYXRlIC8gNCwKICAgICAgICBob3VyczogKGRheXNJblllYXJBY2N1cmF0ZSAqIDI0KSAvIDQsCiAgICAgICAgbWludXRlczogKGRheXNJblllYXJBY2N1cmF0ZSAqIDI0ICogNjApIC8gNCwKICAgICAgICBzZWNvbmRzOiAoZGF5c0luWWVhckFjY3VyYXRlICogMjQgKiA2MCAqIDYwKSAvIDQsCiAgICAgICAgbWlsbGlzZWNvbmRzOiAoZGF5c0luWWVhckFjY3VyYXRlICogMjQgKiA2MCAqIDYwICogMTAwMCkgLyA0LAogICAgICB9LAogICAgICBtb250aHM6IHsKICAgICAgICB3ZWVrczogZGF5c0luTW9udGhBY2N1cmF0ZSAvIDcsCiAgICAgICAgZGF5czogZGF5c0luTW9udGhBY2N1cmF0ZSwKICAgICAgICBob3VyczogZGF5c0luTW9udGhBY2N1cmF0ZSAqIDI0LAogICAgICAgIG1pbnV0ZXM6IGRheXNJbk1vbnRoQWNjdXJhdGUgKiAyNCAqIDYwLAogICAgICAgIHNlY29uZHM6IGRheXNJbk1vbnRoQWNjdXJhdGUgKiAyNCAqIDYwICogNjAsCiAgICAgICAgbWlsbGlzZWNvbmRzOiBkYXlzSW5Nb250aEFjY3VyYXRlICogMjQgKiA2MCAqIDYwICogMTAwMCwKICAgICAgfSwKICAgICAgLi4ubG93T3JkZXJNYXRyaXgsCiAgICB9OwoKICAvLyB1bml0cyBvcmRlcmVkIGJ5IHNpemUKICBjb25zdCBvcmRlcmVkVW5pdHMkMSA9IFsKICAgICJ5ZWFycyIsCiAgICAicXVhcnRlcnMiLAogICAgIm1vbnRocyIsCiAgICAid2Vla3MiLAogICAgImRheXMiLAogICAgImhvdXJzIiwKICAgICJtaW51dGVzIiwKICAgICJzZWNvbmRzIiwKICAgICJtaWxsaXNlY29uZHMiLAogIF07CgogIGNvbnN0IHJldmVyc2VVbml0cyA9IG9yZGVyZWRVbml0cyQxLnNsaWNlKDApLnJldmVyc2UoKTsKCiAgLy8gY2xvbmUgcmVhbGx5IG1lYW5zICJjcmVhdGUgYW5vdGhlciBpbnN0YW5jZSBqdXN0IGxpa2UgdGhpcyBvbmUsIGJ1dCB3aXRoIHRoZXNlIGNoYW5nZXMiCiAgZnVuY3Rpb24gY2xvbmUkMShkdXIsIGFsdHMsIGNsZWFyID0gZmFsc2UpIHsKICAgIC8vIGRlZXAgbWVyZ2UgZm9yIHZhbHMKICAgIGNvbnN0IGNvbmYgPSB7CiAgICAgIHZhbHVlczogY2xlYXIgPyBhbHRzLnZhbHVlcyA6IHsgLi4uZHVyLnZhbHVlcywgLi4uKGFsdHMudmFsdWVzIHx8IHt9KSB9LAogICAgICBsb2M6IGR1ci5sb2MuY2xvbmUoYWx0cy5sb2MpLAogICAgICBjb252ZXJzaW9uQWNjdXJhY3k6IGFsdHMuY29udmVyc2lvbkFjY3VyYWN5IHx8IGR1ci5jb252ZXJzaW9uQWNjdXJhY3ksCiAgICAgIG1hdHJpeDogYWx0cy5tYXRyaXggfHwgZHVyLm1hdHJpeCwKICAgIH07CiAgICByZXR1cm4gbmV3IER1cmF0aW9uKGNvbmYpOwogIH0KCiAgZnVuY3Rpb24gZHVyYXRpb25Ub01pbGxpcyhtYXRyaXgsIHZhbHMpIHsKICAgIGxldCBzdW0gPSB2YWxzLm1pbGxpc2Vjb25kcyA/PyAwOwogICAgZm9yIChjb25zdCB1bml0IG9mIHJldmVyc2VVbml0cy5zbGljZSgxKSkgewogICAgICBpZiAodmFsc1t1bml0XSkgewogICAgICAgIHN1bSArPSB2YWxzW3VuaXRdICogbWF0cml4W3VuaXRdWyJtaWxsaXNlY29uZHMiXTsKICAgICAgfQogICAgfQogICAgcmV0dXJuIHN1bTsKICB9CgogIC8vIE5COiBtdXRhdGVzIHBhcmFtZXRlcnMKICBmdW5jdGlvbiBub3JtYWxpemVWYWx1ZXMobWF0cml4LCB2YWxzKSB7CiAgICAvLyB0aGUgbG9naWMgYmVsb3cgYXNzdW1lcyB0aGUgb3ZlcmFsbCB2YWx1ZSBvZiB0aGUgZHVyYXRpb24gaXMgcG9zaXRpdmUKICAgIC8vIGlmIHRoaXMgaXMgbm90IHRoZSBjYXNlLCBmYWN0b3IgaXMgdXNlZCB0byBtYWtlIGl0IHNvCiAgICBjb25zdCBmYWN0b3IgPSBkdXJhdGlvblRvTWlsbGlzKG1hdHJpeCwgdmFscykgPCAwID8gLTEgOiAxOwoKICAgIG9yZGVyZWRVbml0cyQxLnJlZHVjZVJpZ2h0KChwcmV2aW91cywgY3VycmVudCkgPT4gewogICAgICBpZiAoIWlzVW5kZWZpbmVkKHZhbHNbY3VycmVudF0pKSB7CiAgICAgICAgaWYgKHByZXZpb3VzKSB7CiAgICAgICAgICBjb25zdCBwcmV2aW91c1ZhbCA9IHZhbHNbcHJldmlvdXNdICogZmFjdG9yOwogICAgICAgICAgY29uc3QgY29udiA9IG1hdHJpeFtjdXJyZW50XVtwcmV2aW91c107CgogICAgICAgICAgLy8gaWYgKHByZXZpb3VzVmFsIDwgMCk6CiAgICAgICAgICAvLyBsb3dlciBvcmRlciB1bml0IGlzIG5lZ2F0aXZlIChlLmcuIHsgeWVhcnM6IDIsIGRheXM6IC0yIH0pCiAgICAgICAgICAvLyBub3JtYWxpemUgdGhpcyBieSByZWR1Y2luZyB0aGUgaGlnaGVyIG9yZGVyIHVuaXQgYnkgdGhlIGFwcHJvcHJpYXRlIGFtb3VudAogICAgICAgICAgLy8gYW5kIGluY3JlYXNpbmcgdGhlIGxvd2VyIG9yZGVyIHVuaXQKICAgICAgICAgIC8vIHRoaXMgY2FuIG5ldmVyIG1ha2UgdGhlIGhpZ2hlciBvcmRlciB1bml0IG5lZ2F0aXZlLCBiZWNhdXNlIHRoaXMgZnVuY3Rpb24gb25seSBvcGVyYXRlcwogICAgICAgICAgLy8gb24gcG9zaXRpdmUgZHVyYXRpb25zLCBzbyB0aGUgYW1vdW50IG9mIHRpbWUgcmVwcmVzZW50ZWQgYnkgdGhlIGxvd2VyIG9yZGVyIHVuaXQgY2Fubm90CiAgICAgICAgICAvLyBiZSBsYXJnZXIgdGhhbiB0aGUgaGlnaGVyIG9yZGVyIHVuaXQKICAgICAgICAgIC8vIGVsc2U6CiAgICAgICAgICAvLyBsb3dlciBvcmRlciB1bml0IGlzIHBvc2l0aXZlIChlLmcuIHsgeWVhcnM6IDIsIGRheXM6IDQ1MCB9IG9yIHsgeWVhcnM6IC0yLCBkYXlzOiA0NTAgfSkKICAgICAgICAgIC8vIGluIHRoaXMgY2FzZSB3ZSBhdHRlbXB0IHRvIGNvbnZlcnQgYXMgbXVjaCBhcyBwb3NzaWJsZSBmcm9tIHRoZSBsb3dlciBvcmRlciB1bml0IGludG8KICAgICAgICAgIC8vIHRoZSBoaWdoZXIgb3JkZXIgb25lCiAgICAgICAgICAvLwogICAgICAgICAgLy8gTWF0aC5mbG9vciB0YWtlcyBjYXJlIG9mIGJvdGggb2YgdGhlc2UgY2FzZXMsIHJvdW5kaW5nIGF3YXkgZnJvbSAwCiAgICAgICAgICAvLyBpZiBwcmV2aW91c1ZhbCA8IDAgaXQgbWFrZXMgdGhlIGFic29sdXRlIHZhbHVlIGxhcmdlcgogICAgICAgICAgLy8gaWYgcHJldmlvdXNWYWwgPj0gaXQgbWFrZXMgdGhlIGFic29sdXRlIHZhbHVlIHNtYWxsZXIKICAgICAgICAgIGNvbnN0IHJvbGxVcCA9IE1hdGguZmxvb3IocHJldmlvdXNWYWwgLyBjb252KTsKICAgICAgICAgIHZhbHNbY3VycmVudF0gKz0gcm9sbFVwICogZmFjdG9yOwogICAgICAgICAgdmFsc1twcmV2aW91c10gLT0gcm9sbFVwICogY29udiAqIGZhY3RvcjsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIGN1cnJlbnQ7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIHByZXZpb3VzOwogICAgICB9CiAgICB9LCBudWxsKTsKCiAgICAvLyB0cnkgdG8gY29udmVydCBhbnkgZGVjaW1hbHMgaW50byBzbWFsbGVyIHVuaXRzIGlmIHBvc3NpYmxlCiAgICAvLyBmb3IgZXhhbXBsZSBmb3IgeyB5ZWFyczogMi41LCBkYXlzOiAwLCBzZWNvbmRzOiAwIH0gd2Ugd2FudCB0byBnZXQgeyB5ZWFyczogMiwgZGF5czogMTgyLCBob3VyczogMTIgfQogICAgb3JkZXJlZFVuaXRzJDEucmVkdWNlKChwcmV2aW91cywgY3VycmVudCkgPT4gewogICAgICBpZiAoIWlzVW5kZWZpbmVkKHZhbHNbY3VycmVudF0pKSB7CiAgICAgICAgaWYgKHByZXZpb3VzKSB7CiAgICAgICAgICBjb25zdCBmcmFjdGlvbiA9IHZhbHNbcHJldmlvdXNdICUgMTsKICAgICAgICAgIHZhbHNbcHJldmlvdXNdIC09IGZyYWN0aW9uOwogICAgICAgICAgdmFsc1tjdXJyZW50XSArPSBmcmFjdGlvbiAqIG1hdHJpeFtwcmV2aW91c11bY3VycmVudF07CiAgICAgICAgfQogICAgICAgIHJldHVybiBjdXJyZW50OwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBwcmV2aW91czsKICAgICAgfQogICAgfSwgbnVsbCk7CiAgfQoKICAvLyBSZW1vdmUgYWxsIHByb3BlcnRpZXMgd2l0aCBhIHZhbHVlIG9mIDAgZnJvbSBhbiBvYmplY3QKICBmdW5jdGlvbiByZW1vdmVaZXJvZXModmFscykgewogICAgY29uc3QgbmV3VmFscyA9IHt9OwogICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXModmFscykpIHsKICAgICAgaWYgKHZhbHVlICE9PSAwKSB7CiAgICAgICAgbmV3VmFsc1trZXldID0gdmFsdWU7CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBuZXdWYWxzOwogIH0KCiAgLyoqCiAgICogQSBEdXJhdGlvbiBvYmplY3QgcmVwcmVzZW50cyBhIHBlcmlvZCBvZiB0aW1lLCBsaWtlICIyIG1vbnRocyIgb3IgIjEgZGF5LCAxIGhvdXIiLiBDb25jZXB0dWFsbHksIGl0J3MganVzdCBhIG1hcCBvZiB1bml0cyB0byB0aGVpciBxdWFudGl0aWVzLCBhY2NvbXBhbmllZCBieSBzb21lIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBhbmQgbWV0aG9kcyBmb3IgY3JlYXRpbmcsIHBhcnNpbmcsIGludGVycm9nYXRpbmcsIHRyYW5zZm9ybWluZywgYW5kIGZvcm1hdHRpbmcgdGhlbS4gVGhleSBjYW4gYmUgdXNlZCBvbiB0aGVpciBvd24gb3IgaW4gY29uanVuY3Rpb24gd2l0aCBvdGhlciBMdXhvbiB0eXBlczsgZm9yIGV4YW1wbGUsIHlvdSBjYW4gdXNlIHtAbGluayBEYXRlVGltZSNwbHVzfSB0byBhZGQgYSBEdXJhdGlvbiBvYmplY3QgdG8gYSBEYXRlVGltZSwgcHJvZHVjaW5nIGFub3RoZXIgRGF0ZVRpbWUuCiAgICoKICAgKiBIZXJlIGlzIGEgYnJpZWYgb3ZlcnZpZXcgb2YgY29tbW9ubHkgdXNlZCBtZXRob2RzIGFuZCBnZXR0ZXJzIGluIER1cmF0aW9uOgogICAqCiAgICogKiAqKkNyZWF0aW9uKiogVG8gY3JlYXRlIGEgRHVyYXRpb24sIHVzZSB7QGxpbmsgRHVyYXRpb24uZnJvbU1pbGxpc30sIHtAbGluayBEdXJhdGlvbi5mcm9tT2JqZWN0fSwgb3Ige0BsaW5rIER1cmF0aW9uLmZyb21JU099LgogICAqICogKipVbml0IHZhbHVlcyoqIFNlZSB0aGUge0BsaW5rIER1cmF0aW9uI3llYXJzfSwge0BsaW5rIER1cmF0aW9uI21vbnRoc30sIHtAbGluayBEdXJhdGlvbiN3ZWVrc30sIHtAbGluayBEdXJhdGlvbiNkYXlzfSwge0BsaW5rIER1cmF0aW9uI2hvdXJzfSwge0BsaW5rIER1cmF0aW9uI21pbnV0ZXN9LCB7QGxpbmsgRHVyYXRpb24jc2Vjb25kc30sIHtAbGluayBEdXJhdGlvbiNtaWxsaXNlY29uZHN9IGFjY2Vzc29ycy4KICAgKiAqICoqQ29uZmlndXJhdGlvbioqIFNlZSAge0BsaW5rIER1cmF0aW9uI2xvY2FsZX0gYW5kIHtAbGluayBEdXJhdGlvbiNudW1iZXJpbmdTeXN0ZW19IGFjY2Vzc29ycy4KICAgKiAqICoqVHJhbnNmb3JtYXRpb24qKiBUbyBjcmVhdGUgbmV3IER1cmF0aW9ucyBvdXQgb2Ygb2xkIG9uZXMgdXNlIHtAbGluayBEdXJhdGlvbiNwbHVzfSwge0BsaW5rIER1cmF0aW9uI21pbnVzfSwge0BsaW5rIER1cmF0aW9uI25vcm1hbGl6ZX0sIHtAbGluayBEdXJhdGlvbiNzZXR9LCB7QGxpbmsgRHVyYXRpb24jcmVjb25maWd1cmV9LCB7QGxpbmsgRHVyYXRpb24jc2hpZnRUb30sIGFuZCB7QGxpbmsgRHVyYXRpb24jbmVnYXRlfS4KICAgKiAqICoqT3V0cHV0KiogVG8gY29udmVydCB0aGUgRHVyYXRpb24gaW50byBvdGhlciByZXByZXNlbnRhdGlvbnMsIHNlZSB7QGxpbmsgRHVyYXRpb24jYXN9LCB7QGxpbmsgRHVyYXRpb24jdG9JU099LCB7QGxpbmsgRHVyYXRpb24jdG9Gb3JtYXR9LCBhbmQge0BsaW5rIER1cmF0aW9uI3RvSlNPTn0KICAgKgogICAqIFRoZXJlJ3MgYXJlIG1vcmUgbWV0aG9kcyBkb2N1bWVudGVkIGJlbG93LiBJbiBhZGRpdGlvbiwgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gc3VidGxlciB0b3BpY3MgbGlrZSBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgdmFsaWRpdHksIHNlZSB0aGUgZXh0ZXJuYWwgZG9jdW1lbnRhdGlvbi4KICAgKi8KICBjbGFzcyBEdXJhdGlvbiB7CiAgICAvKioKICAgICAqIEBwcml2YXRlCiAgICAgKi8KICAgIGNvbnN0cnVjdG9yKGNvbmZpZykgewogICAgICBjb25zdCBhY2N1cmF0ZSA9IGNvbmZpZy5jb252ZXJzaW9uQWNjdXJhY3kgPT09ICJsb25ndGVybSIgfHwgZmFsc2U7CiAgICAgIGxldCBtYXRyaXggPSBhY2N1cmF0ZSA/IGFjY3VyYXRlTWF0cml4IDogY2FzdWFsTWF0cml4OwoKICAgICAgaWYgKGNvbmZpZy5tYXRyaXgpIHsKICAgICAgICBtYXRyaXggPSBjb25maWcubWF0cml4OwogICAgICB9CgogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLnZhbHVlcyA9IGNvbmZpZy52YWx1ZXM7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMubG9jID0gY29uZmlnLmxvYyB8fCBMb2NhbGUuY3JlYXRlKCk7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMuY29udmVyc2lvbkFjY3VyYWN5ID0gYWNjdXJhdGUgPyAibG9uZ3Rlcm0iIDogImNhc3VhbCI7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMuaW52YWxpZCA9IGNvbmZpZy5pbnZhbGlkIHx8IG51bGw7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMubWF0cml4ID0gbWF0cml4OwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLmlzTHV4b25EdXJhdGlvbiA9IHRydWU7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgRHVyYXRpb24gZnJvbSBhIG51bWJlciBvZiBtaWxsaXNlY29uZHMuCiAgICAgKiBAcGFyYW0ge251bWJlcn0gY291bnQgb2YgbWlsbGlzZWNvbmRzCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMgZm9yIHBhcnNpbmcKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5sb2NhbGU9J2VuLVVTJ10gLSB0aGUgbG9jYWxlIHRvIHVzZQogICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdHMubnVtYmVyaW5nU3lzdGVtIC0gdGhlIG51bWJlcmluZyBzeXN0ZW0gdG8gdXNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuY29udmVyc2lvbkFjY3VyYWN5PSdjYXN1YWwnXSAtIHRoZSBjb252ZXJzaW9uIHN5c3RlbSB0byB1c2UKICAgICAqIEByZXR1cm4ge0R1cmF0aW9ufQogICAgICovCiAgICBzdGF0aWMgZnJvbU1pbGxpcyhjb3VudCwgb3B0cykgewogICAgICByZXR1cm4gRHVyYXRpb24uZnJvbU9iamVjdCh7IG1pbGxpc2Vjb25kczogY291bnQgfSwgb3B0cyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEdXJhdGlvbiBmcm9tIGEgSmF2YVNjcmlwdCBvYmplY3Qgd2l0aCBrZXlzIGxpa2UgJ3llYXJzJyBhbmQgJ2hvdXJzJy4KICAgICAqIElmIHRoaXMgb2JqZWN0IGlzIGVtcHR5IHRoZW4gYSB6ZXJvIG1pbGxpc2Vjb25kcyBkdXJhdGlvbiBpcyByZXR1cm5lZC4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvYmogLSB0aGUgb2JqZWN0IHRvIGNyZWF0ZSB0aGUgRGF0ZVRpbWUgZnJvbQogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai55ZWFycwogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5xdWFydGVycwogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5tb250aHMKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvYmoud2Vla3MKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvYmouZGF5cwogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5ob3VycwogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5taW51dGVzCiAgICAgKiBAcGFyYW0ge251bWJlcn0gb2JqLnNlY29uZHMKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvYmoubWlsbGlzZWNvbmRzCiAgICAgKiBAcGFyYW0ge09iamVjdH0gW29wdHM9W11dIC0gb3B0aW9ucyBmb3IgY3JlYXRpbmcgdGhpcyBEdXJhdGlvbgogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmxvY2FsZT0nZW4tVVMnXSAtIHRoZSBsb2NhbGUgdG8gdXNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0cy5udW1iZXJpbmdTeXN0ZW0gLSB0aGUgbnVtYmVyaW5nIHN5c3RlbSB0byB1c2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5jb252ZXJzaW9uQWNjdXJhY3k9J2Nhc3VhbCddIC0gdGhlIHByZXNldCBjb252ZXJzaW9uIHN5c3RlbSB0byB1c2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5tYXRyaXg9T2JqZWN0XSAtIHRoZSBjdXN0b20gY29udmVyc2lvbiBzeXN0ZW0gdG8gdXNlCiAgICAgKiBAcmV0dXJuIHtEdXJhdGlvbn0KICAgICAqLwogICAgc3RhdGljIGZyb21PYmplY3Qob2JqLCBvcHRzID0ge30pIHsKICAgICAgaWYgKG9iaiA9PSBudWxsIHx8IHR5cGVvZiBvYmogIT09ICJvYmplY3QiKSB7CiAgICAgICAgdGhyb3cgbmV3IEludmFsaWRBcmd1bWVudEVycm9yKAogICAgICAgICAgYER1cmF0aW9uLmZyb21PYmplY3Q6IGFyZ3VtZW50IGV4cGVjdGVkIHRvIGJlIGFuIG9iamVjdCwgZ290ICR7CiAgICAgICAgICBvYmogPT09IG51bGwgPyAibnVsbCIgOiB0eXBlb2Ygb2JqCiAgICAgICAgfWAKICAgICAgICApOwogICAgICB9CgogICAgICByZXR1cm4gbmV3IER1cmF0aW9uKHsKICAgICAgICB2YWx1ZXM6IG5vcm1hbGl6ZU9iamVjdChvYmosIER1cmF0aW9uLm5vcm1hbGl6ZVVuaXQpLAogICAgICAgIGxvYzogTG9jYWxlLmZyb21PYmplY3Qob3B0cyksCiAgICAgICAgY29udmVyc2lvbkFjY3VyYWN5OiBvcHRzLmNvbnZlcnNpb25BY2N1cmFjeSwKICAgICAgICBtYXRyaXg6IG9wdHMubWF0cml4LAogICAgICB9KTsKICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZSBhIER1cmF0aW9uIGZyb20gRHVyYXRpb25MaWtlLgogICAgICoKICAgICAqIEBwYXJhbSB7T2JqZWN0IHwgbnVtYmVyIHwgRHVyYXRpb259IGR1cmF0aW9uTGlrZQogICAgICogT25lIG9mOgogICAgICogLSBvYmplY3Qgd2l0aCBrZXlzIGxpa2UgJ3llYXJzJyBhbmQgJ2hvdXJzJy4KICAgICAqIC0gbnVtYmVyIHJlcHJlc2VudGluZyBtaWxsaXNlY29uZHMKICAgICAqIC0gRHVyYXRpb24gaW5zdGFuY2UKICAgICAqIEByZXR1cm4ge0R1cmF0aW9ufQogICAgICovCiAgICBzdGF0aWMgZnJvbUR1cmF0aW9uTGlrZShkdXJhdGlvbkxpa2UpIHsKICAgICAgaWYgKGlzTnVtYmVyKGR1cmF0aW9uTGlrZSkpIHsKICAgICAgICByZXR1cm4gRHVyYXRpb24uZnJvbU1pbGxpcyhkdXJhdGlvbkxpa2UpOwogICAgICB9IGVsc2UgaWYgKER1cmF0aW9uLmlzRHVyYXRpb24oZHVyYXRpb25MaWtlKSkgewogICAgICAgIHJldHVybiBkdXJhdGlvbkxpa2U7CiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGR1cmF0aW9uTGlrZSA9PT0gIm9iamVjdCIpIHsKICAgICAgICByZXR1cm4gRHVyYXRpb24uZnJvbU9iamVjdChkdXJhdGlvbkxpa2UpOwogICAgICB9IGVsc2UgewogICAgICAgIHRocm93IG5ldyBJbnZhbGlkQXJndW1lbnRFcnJvcigKICAgICAgICAgIGBVbmtub3duIGR1cmF0aW9uIGFyZ3VtZW50ICR7ZHVyYXRpb25MaWtlfSBvZiB0eXBlICR7dHlwZW9mIGR1cmF0aW9uTGlrZX1gCiAgICAgICAgKTsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGEgRHVyYXRpb24gZnJvbSBhbiBJU08gODYwMSBkdXJhdGlvbiBzdHJpbmcuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdGV4dCAtIHRleHQgdG8gcGFyc2UKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRzIC0gb3B0aW9ucyBmb3IgcGFyc2luZwogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmxvY2FsZT0nZW4tVVMnXSAtIHRoZSBsb2NhbGUgdG8gdXNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0cy5udW1iZXJpbmdTeXN0ZW0gLSB0aGUgbnVtYmVyaW5nIHN5c3RlbSB0byB1c2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5jb252ZXJzaW9uQWNjdXJhY3k9J2Nhc3VhbCddIC0gdGhlIHByZXNldCBjb252ZXJzaW9uIHN5c3RlbSB0byB1c2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5tYXRyaXg9T2JqZWN0XSAtIHRoZSBwcmVzZXQgY29udmVyc2lvbiBzeXN0ZW0gdG8gdXNlCiAgICAgKiBAc2VlIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT184NjAxI0R1cmF0aW9ucwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbUlTTygnUDNZNk0xVzREVDEySDMwTTVTJykudG9PYmplY3QoKSAvLz0+IHsgeWVhcnM6IDMsIG1vbnRoczogNiwgd2Vla3M6IDEsIGRheXM6IDQsIGhvdXJzOiAxMiwgbWludXRlczogMzAsIHNlY29uZHM6IDUgfQogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbUlTTygnUFQyM0gnKS50b09iamVjdCgpIC8vPT4geyBob3VyczogMjMgfQogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbUlTTygnUDVZM00nKS50b09iamVjdCgpIC8vPT4geyB5ZWFyczogNSwgbW9udGhzOiAzIH0KICAgICAqIEByZXR1cm4ge0R1cmF0aW9ufQogICAgICovCiAgICBzdGF0aWMgZnJvbUlTTyh0ZXh0LCBvcHRzKSB7CiAgICAgIGNvbnN0IFtwYXJzZWRdID0gcGFyc2VJU09EdXJhdGlvbih0ZXh0KTsKICAgICAgaWYgKHBhcnNlZCkgewogICAgICAgIHJldHVybiBEdXJhdGlvbi5mcm9tT2JqZWN0KHBhcnNlZCwgb3B0cyk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIER1cmF0aW9uLmludmFsaWQoInVucGFyc2FibGUiLCBgdGhlIGlucHV0ICIke3RleHR9IiBjYW4ndCBiZSBwYXJzZWQgYXMgSVNPIDg2MDFgKTsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGEgRHVyYXRpb24gZnJvbSBhbiBJU08gODYwMSB0aW1lIHN0cmluZy4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gdGV4dCB0byBwYXJzZQogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIGZvciBwYXJzaW5nCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jYWxlPSdlbi1VUyddIC0gdGhlIGxvY2FsZSB0byB1c2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLm51bWJlcmluZ1N5c3RlbSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHVzZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmNvbnZlcnNpb25BY2N1cmFjeT0nY2FzdWFsJ10gLSB0aGUgcHJlc2V0IGNvbnZlcnNpb24gc3lzdGVtIHRvIHVzZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm1hdHJpeD1PYmplY3RdIC0gdGhlIGNvbnZlcnNpb24gc3lzdGVtIHRvIHVzZQogICAgICogQHNlZSBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fODYwMSNUaW1lcwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbUlTT1RpbWUoJzExOjIyOjMzLjQ0NCcpLnRvT2JqZWN0KCkgLy89PiB7IGhvdXJzOiAxMSwgbWludXRlczogMjIsIHNlY29uZHM6IDMzLCBtaWxsaXNlY29uZHM6IDQ0NCB9CiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tSVNPVGltZSgnMTE6MDAnKS50b09iamVjdCgpIC8vPT4geyBob3VyczogMTEsIG1pbnV0ZXM6IDAsIHNlY29uZHM6IDAgfQogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbUlTT1RpbWUoJ1QxMTowMCcpLnRvT2JqZWN0KCkgLy89PiB7IGhvdXJzOiAxMSwgbWludXRlczogMCwgc2Vjb25kczogMCB9CiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tSVNPVGltZSgnMTEwMCcpLnRvT2JqZWN0KCkgLy89PiB7IGhvdXJzOiAxMSwgbWludXRlczogMCwgc2Vjb25kczogMCB9CiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tSVNPVGltZSgnVDExMDAnKS50b09iamVjdCgpIC8vPT4geyBob3VyczogMTEsIG1pbnV0ZXM6IDAsIHNlY29uZHM6IDAgfQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIHN0YXRpYyBmcm9tSVNPVGltZSh0ZXh0LCBvcHRzKSB7CiAgICAgIGNvbnN0IFtwYXJzZWRdID0gcGFyc2VJU09UaW1lT25seSh0ZXh0KTsKICAgICAgaWYgKHBhcnNlZCkgewogICAgICAgIHJldHVybiBEdXJhdGlvbi5mcm9tT2JqZWN0KHBhcnNlZCwgb3B0cyk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIER1cmF0aW9uLmludmFsaWQoInVucGFyc2FibGUiLCBgdGhlIGlucHV0ICIke3RleHR9IiBjYW4ndCBiZSBwYXJzZWQgYXMgSVNPIDg2MDFgKTsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGFuIGludmFsaWQgRHVyYXRpb24uCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gcmVhc29uIC0gc2ltcGxlIHN0cmluZyBvZiB3aHkgdGhpcyBkYXRldGltZSBpcyBpbnZhbGlkLiBTaG91bGQgbm90IGNvbnRhaW4gcGFyYW1ldGVycyBvciBhbnl0aGluZyBlbHNlIGRhdGEtZGVwZW5kZW50CiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW2V4cGxhbmF0aW9uPW51bGxdIC0gbG9uZ2VyIGV4cGxhbmF0aW9uLCBtYXkgaW5jbHVkZSBwYXJhbWV0ZXJzIGFuZCBvdGhlciB1c2VmdWwgZGVidWdnaW5nIGluZm9ybWF0aW9uCiAgICAgKiBAcmV0dXJuIHtEdXJhdGlvbn0KICAgICAqLwogICAgc3RhdGljIGludmFsaWQocmVhc29uLCBleHBsYW5hdGlvbiA9IG51bGwpIHsKICAgICAgaWYgKCFyZWFzb24pIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZEFyZ3VtZW50RXJyb3IoIm5lZWQgdG8gc3BlY2lmeSBhIHJlYXNvbiB0aGUgRHVyYXRpb24gaXMgaW52YWxpZCIpOwogICAgICB9CgogICAgICBjb25zdCBpbnZhbGlkID0gcmVhc29uIGluc3RhbmNlb2YgSW52YWxpZCA/IHJlYXNvbiA6IG5ldyBJbnZhbGlkKHJlYXNvbiwgZXhwbGFuYXRpb24pOwoKICAgICAgaWYgKFNldHRpbmdzLnRocm93T25JbnZhbGlkKSB7CiAgICAgICAgdGhyb3cgbmV3IEludmFsaWREdXJhdGlvbkVycm9yKGludmFsaWQpOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBuZXcgRHVyYXRpb24oeyBpbnZhbGlkIH0pOwogICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBAcHJpdmF0ZQogICAgICovCiAgICBzdGF0aWMgbm9ybWFsaXplVW5pdCh1bml0KSB7CiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSB7CiAgICAgICAgeWVhcjogInllYXJzIiwKICAgICAgICB5ZWFyczogInllYXJzIiwKICAgICAgICBxdWFydGVyOiAicXVhcnRlcnMiLAogICAgICAgIHF1YXJ0ZXJzOiAicXVhcnRlcnMiLAogICAgICAgIG1vbnRoOiAibW9udGhzIiwKICAgICAgICBtb250aHM6ICJtb250aHMiLAogICAgICAgIHdlZWs6ICJ3ZWVrcyIsCiAgICAgICAgd2Vla3M6ICJ3ZWVrcyIsCiAgICAgICAgZGF5OiAiZGF5cyIsCiAgICAgICAgZGF5czogImRheXMiLAogICAgICAgIGhvdXI6ICJob3VycyIsCiAgICAgICAgaG91cnM6ICJob3VycyIsCiAgICAgICAgbWludXRlOiAibWludXRlcyIsCiAgICAgICAgbWludXRlczogIm1pbnV0ZXMiLAogICAgICAgIHNlY29uZDogInNlY29uZHMiLAogICAgICAgIHNlY29uZHM6ICJzZWNvbmRzIiwKICAgICAgICBtaWxsaXNlY29uZDogIm1pbGxpc2Vjb25kcyIsCiAgICAgICAgbWlsbGlzZWNvbmRzOiAibWlsbGlzZWNvbmRzIiwKICAgICAgfVt1bml0ID8gdW5pdC50b0xvd2VyQ2FzZSgpIDogdW5pdF07CgogICAgICBpZiAoIW5vcm1hbGl6ZWQpIHRocm93IG5ldyBJbnZhbGlkVW5pdEVycm9yKHVuaXQpOwoKICAgICAgcmV0dXJuIG5vcm1hbGl6ZWQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBDaGVjayBpZiBhbiBvYmplY3QgaXMgYSBEdXJhdGlvbi4gV29ya3MgYWNyb3NzIGNvbnRleHQgYm91bmRhcmllcwogICAgICogQHBhcmFtIHtvYmplY3R9IG8KICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIHN0YXRpYyBpc0R1cmF0aW9uKG8pIHsKICAgICAgcmV0dXJuIChvICYmIG8uaXNMdXhvbkR1cmF0aW9uKSB8fCBmYWxzZTsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCAgdGhlIGxvY2FsZSBvZiBhIER1cmF0aW9uLCBzdWNoICdlbi1HQicKICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCBsb2NhbGUoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLmxvYy5sb2NhbGUgOiBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBudW1iZXJpbmcgc3lzdGVtIG9mIGEgRHVyYXRpb24sIHN1Y2ggJ2JlbmcnLiBUaGUgbnVtYmVyaW5nIHN5c3RlbSBpcyB1c2VkIHdoZW4gZm9ybWF0dGluZyB0aGUgRHVyYXRpb24KICAgICAqCiAgICAgKiBAdHlwZSB7c3RyaW5nfQogICAgICovCiAgICBnZXQgbnVtYmVyaW5nU3lzdGVtKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy5sb2MubnVtYmVyaW5nU3lzdGVtIDogbnVsbDsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBEdXJhdGlvbiBmb3JtYXR0ZWQgYWNjb3JkaW5nIHRvIHRoZSBzcGVjaWZpZWQgZm9ybWF0IHN0cmluZy4gWW91IG1heSB1c2UgdGhlc2UgdG9rZW5zOgogICAgICogKiBgU2AgZm9yIG1pbGxpc2Vjb25kcwogICAgICogKiBgc2AgZm9yIHNlY29uZHMKICAgICAqICogYG1gIGZvciBtaW51dGVzCiAgICAgKiAqIGBoYCBmb3IgaG91cnMKICAgICAqICogYGRgIGZvciBkYXlzCiAgICAgKiAqIGB3YCBmb3Igd2Vla3MKICAgICAqICogYE1gIGZvciBtb250aHMKICAgICAqICogYHlgIGZvciB5ZWFycwogICAgICogTm90ZXM6CiAgICAgKiAqIEFkZCBwYWRkaW5nIGJ5IHJlcGVhdGluZyB0aGUgdG9rZW4sIGUuZy4gInl5IiBwYWRzIHRoZSB5ZWFycyB0byB0d28gZGlnaXRzLCAiaGhoaCIgcGFkcyB0aGUgaG91cnMgb3V0IHRvIGZvdXIgZGlnaXRzCiAgICAgKiAqIFRva2VucyBjYW4gYmUgZXNjYXBlZCBieSB3cmFwcGluZyB3aXRoIHNpbmdsZSBxdW90ZXMuCiAgICAgKiAqIFRoZSBkdXJhdGlvbiB3aWxsIGJlIGNvbnZlcnRlZCB0byB0aGUgc2V0IG9mIHVuaXRzIGluIHRoZSBmb3JtYXQgc3RyaW5nIHVzaW5nIHtAbGluayBEdXJhdGlvbiNzaGlmdFRvfSBhbmQgdGhlIER1cmF0aW9ucydzIGNvbnZlcnNpb24gYWNjdXJhY3kgc2V0dGluZy4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBmbXQgLSB0aGUgZm9ybWF0IHN0cmluZwogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmZsb29yPXRydWVdIC0gZmxvb3IgbnVtZXJpY2FsIHZhbHVlcwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IHllYXJzOiAxLCBkYXlzOiA2LCBzZWNvbmRzOiAyIH0pLnRvRm9ybWF0KCJ5IGQgcyIpIC8vPT4gIjEgNiAyIgogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IHllYXJzOiAxLCBkYXlzOiA2LCBzZWNvbmRzOiAyIH0pLnRvRm9ybWF0KCJ5eSBkZCBzc3MiKSAvLz0+ICIwMSAwNiAwMDIiCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgeWVhcnM6IDEsIGRheXM6IDYsIHNlY29uZHM6IDIgfSkudG9Gb3JtYXQoIk0gUyIpIC8vPT4gIjEyIDUxODQwMjAwMCIKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9Gb3JtYXQoZm10LCBvcHRzID0ge30pIHsKICAgICAgLy8gcmV2ZXJzZS1jb21wYXQgc2luY2UgMS4yOyB3ZSBhbHdheXMgcm91bmQgZG93biBub3csIG5ldmVyIHVwLCBhbmQgd2UgZG8gaXQgYnkgZGVmYXVsdAogICAgICBjb25zdCBmbXRPcHRzID0gewogICAgICAgIC4uLm9wdHMsCiAgICAgICAgZmxvb3I6IG9wdHMucm91bmQgIT09IGZhbHNlICYmIG9wdHMuZmxvb3IgIT09IGZhbHNlLAogICAgICB9OwogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkCiAgICAgICAgPyBGb3JtYXR0ZXIuY3JlYXRlKHRoaXMubG9jLCBmbXRPcHRzKS5mb3JtYXREdXJhdGlvbkZyb21TdHJpbmcodGhpcywgZm10KQogICAgICAgIDogSU5WQUxJRCQyOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBhIER1cmF0aW9uIHdpdGggYWxsIHVuaXRzIGluY2x1ZGVkLgogICAgICogVG8gbW9kaWZ5IGl0cyBiZWhhdmlvciB1c2UgdGhlIGBsaXN0U3R5bGVgIGFuZCBhbnkgSW50bC5OdW1iZXJGb3JtYXQgb3B0aW9uLCB0aG91Z2ggYHVuaXREaXNwbGF5YCBpcyBlc3BlY2lhbGx5IHJlbGV2YW50LgogICAgICogQHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9JbnRsL051bWJlckZvcm1hdAogICAgICogQHBhcmFtIG9wdHMgLSBPbiBvcHRpb24gb2JqZWN0IHRvIG92ZXJyaWRlIHRoZSBmb3JtYXR0aW5nLiBBY2NlcHRzIHRoZSBzYW1lIGtleXMgYXMgdGhlIG9wdGlvbnMgcGFyYW1ldGVyIG9mIHRoZSBuYXRpdmUgYEludC5OdW1iZXJGb3JtYXRgIGNvbnN0cnVjdG9yLCBhcyB3ZWxsIGFzIGBsaXN0U3R5bGVgLgogICAgICogQGV4YW1wbGUKICAgICAqIGBgYGpzCiAgICAgKiB2YXIgZHVyID0gRHVyYXRpb24uZnJvbU9iamVjdCh7IGRheXM6IDEsIGhvdXJzOiA1LCBtaW51dGVzOiA2IH0pCiAgICAgKiBkdXIudG9IdW1hbigpIC8vPT4gJzEgZGF5LCA1IGhvdXJzLCA2IG1pbnV0ZXMnCiAgICAgKiBkdXIudG9IdW1hbih7IGxpc3RTdHlsZTogImxvbmciIH0pIC8vPT4gJzEgZGF5LCA1IGhvdXJzLCBhbmQgNiBtaW51dGVzJwogICAgICogZHVyLnRvSHVtYW4oeyB1bml0RGlzcGxheTogInNob3J0IiB9KSAvLz0+ICcxIGRheSwgNSBociwgNiBtaW4nCiAgICAgKiBgYGAKICAgICAqLwogICAgdG9IdW1hbihvcHRzID0ge30pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBJTlZBTElEJDI7CgogICAgICBjb25zdCBsID0gb3JkZXJlZFVuaXRzJDEKICAgICAgICAubWFwKCh1bml0KSA9PiB7CiAgICAgICAgICBjb25zdCB2YWwgPSB0aGlzLnZhbHVlc1t1bml0XTsKICAgICAgICAgIGlmIChpc1VuZGVmaW5lZCh2YWwpKSB7CiAgICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuIHRoaXMubG9jCiAgICAgICAgICAgIC5udW1iZXJGb3JtYXR0ZXIoeyBzdHlsZTogInVuaXQiLCB1bml0RGlzcGxheTogImxvbmciLCAuLi5vcHRzLCB1bml0OiB1bml0LnNsaWNlKDAsIC0xKSB9KQogICAgICAgICAgICAuZm9ybWF0KHZhbCk7CiAgICAgICAgfSkKICAgICAgICAuZmlsdGVyKChuKSA9PiBuKTsKCiAgICAgIHJldHVybiB0aGlzLmxvYwogICAgICAgIC5saXN0Rm9ybWF0dGVyKHsgdHlwZTogImNvbmp1bmN0aW9uIiwgc3R5bGU6IG9wdHMubGlzdFN0eWxlIHx8ICJuYXJyb3ciLCAuLi5vcHRzIH0pCiAgICAgICAgLmZvcm1hdChsKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYSBKYXZhU2NyaXB0IG9iamVjdCB3aXRoIHRoaXMgRHVyYXRpb24ncyB2YWx1ZXMuCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgeWVhcnM6IDEsIGRheXM6IDYsIHNlY29uZHM6IDIgfSkudG9PYmplY3QoKSAvLz0+IHsgeWVhcnM6IDEsIGRheXM6IDYsIHNlY29uZHM6IDIgfQogICAgICogQHJldHVybiB7T2JqZWN0fQogICAgICovCiAgICB0b09iamVjdCgpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB7fTsKICAgICAgcmV0dXJuIHsgLi4udGhpcy52YWx1ZXMgfTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYW4gSVNPIDg2MDEtY29tcGxpYW50IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIER1cmF0aW9uLgogICAgICogQHNlZSBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fODYwMSNEdXJhdGlvbnMKICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyB5ZWFyczogMywgc2Vjb25kczogNDUgfSkudG9JU08oKSAvLz0+ICdQM1lUNDVTJwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IG1vbnRoczogNCwgc2Vjb25kczogNDUgfSkudG9JU08oKSAvLz0+ICdQNE1UNDVTJwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IG1vbnRoczogNSB9KS50b0lTTygpIC8vPT4gJ1A1TScKICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBtaW51dGVzOiA1IH0pLnRvSVNPKCkgLy89PiAnUFQ1TScKICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBtaWxsaXNlY29uZHM6IDYgfSkudG9JU08oKSAvLz0+ICdQVDAuMDA2UycKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9JU08oKSB7CiAgICAgIC8vIHdlIGNvdWxkIHVzZSB0aGUgZm9ybWF0dGVyLCBidXQgdGhpcyBpcyBhbiBlYXNpZXIgd2F5IHRvIGdldCB0aGUgbWluaW11bSBzdHJpbmcKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBudWxsOwoKICAgICAgbGV0IHMgPSAiUCI7CiAgICAgIGlmICh0aGlzLnllYXJzICE9PSAwKSBzICs9IHRoaXMueWVhcnMgKyAiWSI7CiAgICAgIGlmICh0aGlzLm1vbnRocyAhPT0gMCB8fCB0aGlzLnF1YXJ0ZXJzICE9PSAwKSBzICs9IHRoaXMubW9udGhzICsgdGhpcy5xdWFydGVycyAqIDMgKyAiTSI7CiAgICAgIGlmICh0aGlzLndlZWtzICE9PSAwKSBzICs9IHRoaXMud2Vla3MgKyAiVyI7CiAgICAgIGlmICh0aGlzLmRheXMgIT09IDApIHMgKz0gdGhpcy5kYXlzICsgIkQiOwogICAgICBpZiAodGhpcy5ob3VycyAhPT0gMCB8fCB0aGlzLm1pbnV0ZXMgIT09IDAgfHwgdGhpcy5zZWNvbmRzICE9PSAwIHx8IHRoaXMubWlsbGlzZWNvbmRzICE9PSAwKQogICAgICAgIHMgKz0gIlQiOwogICAgICBpZiAodGhpcy5ob3VycyAhPT0gMCkgcyArPSB0aGlzLmhvdXJzICsgIkgiOwogICAgICBpZiAodGhpcy5taW51dGVzICE9PSAwKSBzICs9IHRoaXMubWludXRlcyArICJNIjsKICAgICAgaWYgKHRoaXMuc2Vjb25kcyAhPT0gMCB8fCB0aGlzLm1pbGxpc2Vjb25kcyAhPT0gMCkKICAgICAgICAvLyB0aGlzIHdpbGwgaGFuZGxlICJmbG9hdGluZyBwb2ludCBtYWRuZXNzIiBieSByZW1vdmluZyBleHRyYSBkZWNpbWFsIHBsYWNlcwogICAgICAgIC8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU4ODAwNC9pcy1mbG9hdGluZy1wb2ludC1tYXRoLWJyb2tlbgogICAgICAgIHMgKz0gcm91bmRUbyh0aGlzLnNlY29uZHMgKyB0aGlzLm1pbGxpc2Vjb25kcyAvIDEwMDAsIDMpICsgIlMiOwogICAgICBpZiAocyA9PT0gIlAiKSBzICs9ICJUMFMiOwogICAgICByZXR1cm4gczsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYW4gSVNPIDg2MDEtY29tcGxpYW50IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIER1cmF0aW9uLCBmb3JtYXR0ZWQgYXMgYSB0aW1lIG9mIGRheS4KICAgICAqIE5vdGUgdGhhdCB0aGlzIHdpbGwgcmV0dXJuIG51bGwgaWYgdGhlIGR1cmF0aW9uIGlzIGludmFsaWQsIG5lZ2F0aXZlLCBvciBlcXVhbCB0byBvciBncmVhdGVyIHRoYW4gMjQgaG91cnMuCiAgICAgKiBAc2VlIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT184NjAxI1RpbWVzCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuc3VwcHJlc3NNaWxsaXNlY29uZHM9ZmFsc2VdIC0gZXhjbHVkZSBtaWxsaXNlY29uZHMgZnJvbSB0aGUgZm9ybWF0IGlmIHRoZXkncmUgMAogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5zdXBwcmVzc1NlY29uZHM9ZmFsc2VdIC0gZXhjbHVkZSBzZWNvbmRzIGZyb20gdGhlIGZvcm1hdCBpZiB0aGV5J3JlIDAKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuaW5jbHVkZVByZWZpeD1mYWxzZV0gLSBpbmNsdWRlIHRoZSBgVGAgcHJlZml4CiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuZm9ybWF0PSdleHRlbmRlZCddIC0gY2hvb3NlIGJldHdlZW4gdGhlIGJhc2ljIGFuZCBleHRlbmRlZCBmb3JtYXQKICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMTEgfSkudG9JU09UaW1lKCkgLy89PiAnMTE6MDA6MDAuMDAwJwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IGhvdXJzOiAxMSB9KS50b0lTT1RpbWUoeyBzdXBwcmVzc01pbGxpc2Vjb25kczogdHJ1ZSB9KSAvLz0+ICcxMTowMDowMCcKICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMTEgfSkudG9JU09UaW1lKHsgc3VwcHJlc3NTZWNvbmRzOiB0cnVlIH0pIC8vPT4gJzExOjAwJwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IGhvdXJzOiAxMSB9KS50b0lTT1RpbWUoeyBpbmNsdWRlUHJlZml4OiB0cnVlIH0pIC8vPT4gJ1QxMTowMDowMC4wMDAnCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgaG91cnM6IDExIH0pLnRvSVNPVGltZSh7IGZvcm1hdDogJ2Jhc2ljJyB9KSAvLz0+ICcxMTAwMDAuMDAwJwogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0lTT1RpbWUob3B0cyA9IHt9KSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gbnVsbDsKCiAgICAgIGNvbnN0IG1pbGxpcyA9IHRoaXMudG9NaWxsaXMoKTsKICAgICAgaWYgKG1pbGxpcyA8IDAgfHwgbWlsbGlzID49IDg2NDAwMDAwKSByZXR1cm4gbnVsbDsKCiAgICAgIG9wdHMgPSB7CiAgICAgICAgc3VwcHJlc3NNaWxsaXNlY29uZHM6IGZhbHNlLAogICAgICAgIHN1cHByZXNzU2Vjb25kczogZmFsc2UsCiAgICAgICAgaW5jbHVkZVByZWZpeDogZmFsc2UsCiAgICAgICAgZm9ybWF0OiAiZXh0ZW5kZWQiLAogICAgICAgIC4uLm9wdHMsCiAgICAgICAgaW5jbHVkZU9mZnNldDogZmFsc2UsCiAgICAgIH07CgogICAgICBjb25zdCBkYXRlVGltZSA9IERhdGVUaW1lLmZyb21NaWxsaXMobWlsbGlzLCB7IHpvbmU6ICJVVEMiIH0pOwogICAgICByZXR1cm4gZGF0ZVRpbWUudG9JU09UaW1lKG9wdHMpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBJU08gODYwMSByZXByZXNlbnRhdGlvbiBvZiB0aGlzIER1cmF0aW9uIGFwcHJvcHJpYXRlIGZvciB1c2UgaW4gSlNPTi4KICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9KU09OKCkgewogICAgICByZXR1cm4gdGhpcy50b0lTTygpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBJU08gODYwMSByZXByZXNlbnRhdGlvbiBvZiB0aGlzIER1cmF0aW9uIGFwcHJvcHJpYXRlIGZvciB1c2UgaW4gZGVidWdnaW5nLgogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b1N0cmluZygpIHsKICAgICAgcmV0dXJuIHRoaXMudG9JU08oKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYW4gbWlsbGlzZWNvbmRzIHZhbHVlIG9mIHRoaXMgRHVyYXRpb24uCiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9CiAgICAgKi8KICAgIHRvTWlsbGlzKCkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIE5hTjsKCiAgICAgIHJldHVybiBkdXJhdGlvblRvTWlsbGlzKHRoaXMubWF0cml4LCB0aGlzLnZhbHVlcyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIG1pbGxpc2Vjb25kcyB2YWx1ZSBvZiB0aGlzIER1cmF0aW9uLiBBbGlhcyBvZiB7QGxpbmsgdG9NaWxsaXN9CiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9CiAgICAgKi8KICAgIHZhbHVlT2YoKSB7CiAgICAgIHJldHVybiB0aGlzLnRvTWlsbGlzKCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBNYWtlIHRoaXMgRHVyYXRpb24gbG9uZ2VyIGJ5IHRoZSBzcGVjaWZpZWQgYW1vdW50LiBSZXR1cm4gYSBuZXdseS1jb25zdHJ1Y3RlZCBEdXJhdGlvbi4KICAgICAqIEBwYXJhbSB7RHVyYXRpb258T2JqZWN0fG51bWJlcn0gZHVyYXRpb24gLSBUaGUgYW1vdW50IHRvIGFkZC4gRWl0aGVyIGEgTHV4b24gRHVyYXRpb24sIGEgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcywgdGhlIG9iamVjdCBhcmd1bWVudCB0byBEdXJhdGlvbi5mcm9tT2JqZWN0KCkKICAgICAqIEByZXR1cm4ge0R1cmF0aW9ufQogICAgICovCiAgICBwbHVzKGR1cmF0aW9uKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gdGhpczsKCiAgICAgIGNvbnN0IGR1ciA9IER1cmF0aW9uLmZyb21EdXJhdGlvbkxpa2UoZHVyYXRpb24pLAogICAgICAgIHJlc3VsdCA9IHt9OwoKICAgICAgZm9yIChjb25zdCBrIG9mIG9yZGVyZWRVbml0cyQxKSB7CiAgICAgICAgaWYgKGhhc093blByb3BlcnR5KGR1ci52YWx1ZXMsIGspIHx8IGhhc093blByb3BlcnR5KHRoaXMudmFsdWVzLCBrKSkgewogICAgICAgICAgcmVzdWx0W2tdID0gZHVyLmdldChrKSArIHRoaXMuZ2V0KGspOwogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIGNsb25lJDEodGhpcywgeyB2YWx1ZXM6IHJlc3VsdCB9LCB0cnVlKTsKICAgIH0KCiAgICAvKioKICAgICAqIE1ha2UgdGhpcyBEdXJhdGlvbiBzaG9ydGVyIGJ5IHRoZSBzcGVjaWZpZWQgYW1vdW50LiBSZXR1cm4gYSBuZXdseS1jb25zdHJ1Y3RlZCBEdXJhdGlvbi4KICAgICAqIEBwYXJhbSB7RHVyYXRpb258T2JqZWN0fG51bWJlcn0gZHVyYXRpb24gLSBUaGUgYW1vdW50IHRvIHN1YnRyYWN0LiBFaXRoZXIgYSBMdXhvbiBEdXJhdGlvbiwgYSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLCB0aGUgb2JqZWN0IGFyZ3VtZW50IHRvIER1cmF0aW9uLmZyb21PYmplY3QoKQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIG1pbnVzKGR1cmF0aW9uKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gdGhpczsKCiAgICAgIGNvbnN0IGR1ciA9IER1cmF0aW9uLmZyb21EdXJhdGlvbkxpa2UoZHVyYXRpb24pOwogICAgICByZXR1cm4gdGhpcy5wbHVzKGR1ci5uZWdhdGUoKSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBTY2FsZSB0aGlzIER1cmF0aW9uIGJ5IHRoZSBzcGVjaWZpZWQgYW1vdW50LiBSZXR1cm4gYSBuZXdseS1jb25zdHJ1Y3RlZCBEdXJhdGlvbi4KICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGFwcGx5IHRvIGVhY2ggdW5pdC4gQXJpdHkgaXMgMSBvciAyOiB0aGUgdmFsdWUgb2YgdGhlIHVuaXQgYW5kLCBvcHRpb25hbGx5LCB0aGUgdW5pdCBuYW1lLiBNdXN0IHJldHVybiBhIG51bWJlci4KICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMSwgbWludXRlczogMzAgfSkubWFwVW5pdHMoeCA9PiB4ICogMikgLy89PiB7IGhvdXJzOiAyLCBtaW51dGVzOiA2MCB9CiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgaG91cnM6IDEsIG1pbnV0ZXM6IDMwIH0pLm1hcFVuaXRzKCh4LCB1KSA9PiB1ID09PSAiaG91cnMiID8geCAqIDIgOiB4KSAvLz0+IHsgaG91cnM6IDIsIG1pbnV0ZXM6IDMwIH0KICAgICAqIEByZXR1cm4ge0R1cmF0aW9ufQogICAgICovCiAgICBtYXBVbml0cyhmbikgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CiAgICAgIGNvbnN0IHJlc3VsdCA9IHt9OwogICAgICBmb3IgKGNvbnN0IGsgb2YgT2JqZWN0LmtleXModGhpcy52YWx1ZXMpKSB7CiAgICAgICAgcmVzdWx0W2tdID0gYXNOdW1iZXIoZm4odGhpcy52YWx1ZXNba10sIGspKTsKICAgICAgfQogICAgICByZXR1cm4gY2xvbmUkMSh0aGlzLCB7IHZhbHVlczogcmVzdWx0IH0sIHRydWUpOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSB2YWx1ZSBvZiB1bml0LgogICAgICogQHBhcmFtIHtzdHJpbmd9IHVuaXQgLSBhIHVuaXQgc3VjaCBhcyAnbWludXRlJyBvciAnZGF5JwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7eWVhcnM6IDIsIGRheXM6IDN9KS5nZXQoJ3llYXJzJykgLy89PiAyCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHt5ZWFyczogMiwgZGF5czogM30pLmdldCgnbW9udGhzJykgLy89PiAwCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHt5ZWFyczogMiwgZGF5czogM30pLmdldCgnZGF5cycpIC8vPT4gMwogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICBnZXQodW5pdCkgewogICAgICByZXR1cm4gdGhpc1tEdXJhdGlvbi5ub3JtYWxpemVVbml0KHVuaXQpXTsKICAgIH0KCiAgICAvKioKICAgICAqICJTZXQiIHRoZSB2YWx1ZXMgb2Ygc3BlY2lmaWVkIHVuaXRzLiBSZXR1cm4gYSBuZXdseS1jb25zdHJ1Y3RlZCBEdXJhdGlvbi4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YWx1ZXMgLSBhIG1hcHBpbmcgb2YgdW5pdHMgdG8gbnVtYmVycwogICAgICogQGV4YW1wbGUgZHVyLnNldCh7IHllYXJzOiAyMDE3IH0pCiAgICAgKiBAZXhhbXBsZSBkdXIuc2V0KHsgaG91cnM6IDgsIG1pbnV0ZXM6IDMwIH0pCiAgICAgKiBAcmV0dXJuIHtEdXJhdGlvbn0KICAgICAqLwogICAgc2V0KHZhbHVlcykgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CgogICAgICBjb25zdCBtaXhlZCA9IHsgLi4udGhpcy52YWx1ZXMsIC4uLm5vcm1hbGl6ZU9iamVjdCh2YWx1ZXMsIER1cmF0aW9uLm5vcm1hbGl6ZVVuaXQpIH07CiAgICAgIHJldHVybiBjbG9uZSQxKHRoaXMsIHsgdmFsdWVzOiBtaXhlZCB9KTsKICAgIH0KCiAgICAvKioKICAgICAqICJTZXQiIHRoZSBsb2NhbGUgYW5kL29yIG51bWJlcmluZ1N5c3RlbS4gIFJldHVybnMgYSBuZXdseS1jb25zdHJ1Y3RlZCBEdXJhdGlvbi4KICAgICAqIEBleGFtcGxlIGR1ci5yZWNvbmZpZ3VyZSh7IGxvY2FsZTogJ2VuLUdCJyB9KQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIHJlY29uZmlndXJlKHsgbG9jYWxlLCBudW1iZXJpbmdTeXN0ZW0sIGNvbnZlcnNpb25BY2N1cmFjeSwgbWF0cml4IH0gPSB7fSkgewogICAgICBjb25zdCBsb2MgPSB0aGlzLmxvYy5jbG9uZSh7IGxvY2FsZSwgbnVtYmVyaW5nU3lzdGVtIH0pOwogICAgICBjb25zdCBvcHRzID0geyBsb2MsIG1hdHJpeCwgY29udmVyc2lvbkFjY3VyYWN5IH07CiAgICAgIHJldHVybiBjbG9uZSQxKHRoaXMsIG9wdHMpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHRoZSBsZW5ndGggb2YgdGhlIGR1cmF0aW9uIGluIHRoZSBzcGVjaWZpZWQgdW5pdC4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1bml0IC0gYSB1bml0IHN1Y2ggYXMgJ21pbnV0ZXMnIG9yICdkYXlzJwogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7eWVhcnM6IDF9KS5hcygnZGF5cycpIC8vPT4gMzY1CiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHt5ZWFyczogMX0pLmFzKCdtb250aHMnKSAvLz0+IDEyCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHtob3VyczogNjB9KS5hcygnZGF5cycpIC8vPT4gMi41CiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9CiAgICAgKi8KICAgIGFzKHVuaXQpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMuc2hpZnRUbyh1bml0KS5nZXQodW5pdCkgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBSZWR1Y2UgdGhpcyBEdXJhdGlvbiB0byBpdHMgY2Fub25pY2FsIHJlcHJlc2VudGF0aW9uIGluIGl0cyBjdXJyZW50IHVuaXRzLgogICAgICogQXNzdW1pbmcgdGhlIG92ZXJhbGwgdmFsdWUgb2YgdGhlIER1cmF0aW9uIGlzIHBvc2l0aXZlLCB0aGlzIG1lYW5zOgogICAgICogLSBleGNlc3NpdmUgdmFsdWVzIGZvciBsb3dlci1vcmRlciB1bml0cyBhcmUgY29udmVydGVkIHRvIGhpZ2hlci1vcmRlciB1bml0cyAoaWYgcG9zc2libGUsIHNlZSBmaXJzdCBhbmQgc2Vjb25kIGV4YW1wbGUpCiAgICAgKiAtIG5lZ2F0aXZlIGxvd2VyLW9yZGVyIHVuaXRzIGFyZSBjb252ZXJ0ZWQgdG8gaGlnaGVyIG9yZGVyIHVuaXRzICh0aGVyZSBtdXN0IGJlIHN1Y2ggYSBoaWdoZXIgb3JkZXIgdW5pdCwgb3RoZXJ3aXNlCiAgICAgKiAgIHRoZSBvdmVyYWxsIHZhbHVlIHdvdWxkIGJlIG5lZ2F0aXZlLCBzZWUgc2Vjb25kIGV4YW1wbGUpCiAgICAgKiAtIGZyYWN0aW9uYWwgdmFsdWVzIGZvciBoaWdoZXItb3JkZXIgdW5pdHMgYXJlIGNvbnZlcnRlZCB0byBsb3dlci1vcmRlciB1bml0cyAoaWYgcG9zc2libGUsIHNlZSBmb3VydGggZXhhbXBsZSkKICAgICAqCiAgICAgKiBJZiB0aGUgb3ZlcmFsbCB2YWx1ZSBpcyBuZWdhdGl2ZSwgdGhlIHJlc3VsdCBvZiB0aGlzIG1ldGhvZCBpcyBlcXVpdmFsZW50IHRvIGB0aGlzLm5lZ2F0ZSgpLm5vcm1hbGl6ZSgpLm5lZ2F0ZSgpYC4KICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyB5ZWFyczogMiwgZGF5czogNTAwMCB9KS5ub3JtYWxpemUoKS50b09iamVjdCgpIC8vPT4geyB5ZWFyczogMTUsIGRheXM6IDI1NSB9CiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgZGF5czogNTAwMCB9KS5ub3JtYWxpemUoKS50b09iamVjdCgpIC8vPT4geyBkYXlzOiA1MDAwIH0KICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMTIsIG1pbnV0ZXM6IC00NSB9KS5ub3JtYWxpemUoKS50b09iamVjdCgpIC8vPT4geyBob3VyczogMTEsIG1pbnV0ZXM6IDE1IH0KICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyB5ZWFyczogMi41LCBkYXlzOiAwLCBob3VyczogMCB9KS5ub3JtYWxpemUoKS50b09iamVjdCgpIC8vPT4geyB5ZWFyczogMiwgZGF5czogMTgyLCBob3VyczogMTIgfQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIG5vcm1hbGl6ZSgpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB0aGlzOwogICAgICBjb25zdCB2YWxzID0gdGhpcy50b09iamVjdCgpOwogICAgICBub3JtYWxpemVWYWx1ZXModGhpcy5tYXRyaXgsIHZhbHMpOwogICAgICByZXR1cm4gY2xvbmUkMSh0aGlzLCB7IHZhbHVlczogdmFscyB9LCB0cnVlKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJlc2NhbGUgdW5pdHMgdG8gaXRzIGxhcmdlc3QgcmVwcmVzZW50YXRpb24KICAgICAqIEBleGFtcGxlIER1cmF0aW9uLmZyb21PYmplY3QoeyBtaWxsaXNlY29uZHM6IDkwMDAwIH0pLnJlc2NhbGUoKS50b09iamVjdCgpIC8vPT4geyBtaW51dGVzOiAxLCBzZWNvbmRzOiAzMCB9CiAgICAgKiBAcmV0dXJuIHtEdXJhdGlvbn0KICAgICAqLwogICAgcmVzY2FsZSgpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB0aGlzOwogICAgICBjb25zdCB2YWxzID0gcmVtb3ZlWmVyb2VzKHRoaXMubm9ybWFsaXplKCkuc2hpZnRUb0FsbCgpLnRvT2JqZWN0KCkpOwogICAgICByZXR1cm4gY2xvbmUkMSh0aGlzLCB7IHZhbHVlczogdmFscyB9LCB0cnVlKTsKICAgIH0KCiAgICAvKioKICAgICAqIENvbnZlcnQgdGhpcyBEdXJhdGlvbiBpbnRvIGl0cyByZXByZXNlbnRhdGlvbiBpbiBhIGRpZmZlcmVudCBzZXQgb2YgdW5pdHMuCiAgICAgKiBAZXhhbXBsZSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgaG91cnM6IDEsIHNlY29uZHM6IDMwIH0pLnNoaWZ0VG8oJ21pbnV0ZXMnLCAnbWlsbGlzZWNvbmRzJykudG9PYmplY3QoKSAvLz0+IHsgbWludXRlczogNjAsIG1pbGxpc2Vjb25kczogMzAwMDAgfQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIHNoaWZ0VG8oLi4udW5pdHMpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB0aGlzOwoKICAgICAgaWYgKHVuaXRzLmxlbmd0aCA9PT0gMCkgewogICAgICAgIHJldHVybiB0aGlzOwogICAgICB9CgogICAgICB1bml0cyA9IHVuaXRzLm1hcCgodSkgPT4gRHVyYXRpb24ubm9ybWFsaXplVW5pdCh1KSk7CgogICAgICBjb25zdCBidWlsdCA9IHt9LAogICAgICAgIGFjY3VtdWxhdGVkID0ge30sCiAgICAgICAgdmFscyA9IHRoaXMudG9PYmplY3QoKTsKICAgICAgbGV0IGxhc3RVbml0OwoKICAgICAgZm9yIChjb25zdCBrIG9mIG9yZGVyZWRVbml0cyQxKSB7CiAgICAgICAgaWYgKHVuaXRzLmluZGV4T2YoaykgPj0gMCkgewogICAgICAgICAgbGFzdFVuaXQgPSBrOwoKICAgICAgICAgIGxldCBvd24gPSAwOwoKICAgICAgICAgIC8vIGFueXRoaW5nIHdlIGhhdmVuJ3QgYm9pbGVkIGRvd24geWV0IHNob3VsZCBnZXQgYm9pbGVkIHRvIHRoaXMgdW5pdAogICAgICAgICAgZm9yIChjb25zdCBhayBpbiBhY2N1bXVsYXRlZCkgewogICAgICAgICAgICBvd24gKz0gdGhpcy5tYXRyaXhbYWtdW2tdICogYWNjdW11bGF0ZWRbYWtdOwogICAgICAgICAgICBhY2N1bXVsYXRlZFtha10gPSAwOwogICAgICAgICAgfQoKICAgICAgICAgIC8vIHBsdXMgYW55dGhpbmcgdGhhdCdzIGFscmVhZHkgaW4gdGhpcyB1bml0CiAgICAgICAgICBpZiAoaXNOdW1iZXIodmFsc1trXSkpIHsKICAgICAgICAgICAgb3duICs9IHZhbHNba107CiAgICAgICAgICB9CgogICAgICAgICAgLy8gb25seSBrZWVwIHRoZSBpbnRlZ2VyIHBhcnQgZm9yIG5vdyBpbiB0aGUgaG9wZXMgb2YgcHV0dGluZyBhbnkgZGVjaW1hbCBwYXJ0CiAgICAgICAgICAvLyBpbnRvIGEgc21hbGxlciB1bml0IGxhdGVyCiAgICAgICAgICBjb25zdCBpID0gTWF0aC50cnVuYyhvd24pOwogICAgICAgICAgYnVpbHRba10gPSBpOwogICAgICAgICAgYWNjdW11bGF0ZWRba10gPSAob3duICogMTAwMCAtIGkgKiAxMDAwKSAvIDEwMDA7CgogICAgICAgICAgLy8gb3RoZXJ3aXNlLCBrZWVwIGl0IGluIHRoZSB3aW5ncyB0byBib2lsIGl0IGxhdGVyCiAgICAgICAgfSBlbHNlIGlmIChpc051bWJlcih2YWxzW2tdKSkgewogICAgICAgICAgYWNjdW11bGF0ZWRba10gPSB2YWxzW2tdOwogICAgICAgIH0KICAgICAgfQoKICAgICAgLy8gYW55dGhpbmcgbGVmdG92ZXIgYmVjb21lcyB0aGUgZGVjaW1hbCBmb3IgdGhlIGxhc3QgdW5pdAogICAgICAvLyBsYXN0VW5pdCBtdXN0IGJlIGRlZmluZWQgc2luY2UgdW5pdHMgaXMgbm90IGVtcHR5CiAgICAgIGZvciAoY29uc3Qga2V5IGluIGFjY3VtdWxhdGVkKSB7CiAgICAgICAgaWYgKGFjY3VtdWxhdGVkW2tleV0gIT09IDApIHsKICAgICAgICAgIGJ1aWx0W2xhc3RVbml0XSArPQogICAgICAgICAgICBrZXkgPT09IGxhc3RVbml0ID8gYWNjdW11bGF0ZWRba2V5XSA6IGFjY3VtdWxhdGVkW2tleV0gLyB0aGlzLm1hdHJpeFtsYXN0VW5pdF1ba2V5XTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIG5vcm1hbGl6ZVZhbHVlcyh0aGlzLm1hdHJpeCwgYnVpbHQpOwogICAgICByZXR1cm4gY2xvbmUkMSh0aGlzLCB7IHZhbHVlczogYnVpbHQgfSwgdHJ1ZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBTaGlmdCB0aGlzIER1cmF0aW9uIHRvIGFsbCBhdmFpbGFibGUgdW5pdHMuCiAgICAgKiBTYW1lIGFzIHNoaWZ0VG8oInllYXJzIiwgIm1vbnRocyIsICJ3ZWVrcyIsICJkYXlzIiwgImhvdXJzIiwgIm1pbnV0ZXMiLCAic2Vjb25kcyIsICJtaWxsaXNlY29uZHMiKQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIHNoaWZ0VG9BbGwoKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gdGhpczsKICAgICAgcmV0dXJuIHRoaXMuc2hpZnRUbygKICAgICAgICAieWVhcnMiLAogICAgICAgICJtb250aHMiLAogICAgICAgICJ3ZWVrcyIsCiAgICAgICAgImRheXMiLAogICAgICAgICJob3VycyIsCiAgICAgICAgIm1pbnV0ZXMiLAogICAgICAgICJzZWNvbmRzIiwKICAgICAgICAibWlsbGlzZWNvbmRzIgogICAgICApOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHRoZSBuZWdhdGl2ZSBvZiB0aGlzIER1cmF0aW9uLgogICAgICogQGV4YW1wbGUgRHVyYXRpb24uZnJvbU9iamVjdCh7IGhvdXJzOiAxLCBzZWNvbmRzOiAzMCB9KS5uZWdhdGUoKS50b09iamVjdCgpIC8vPT4geyBob3VyczogLTEsIHNlY29uZHM6IC0zMCB9CiAgICAgKiBAcmV0dXJuIHtEdXJhdGlvbn0KICAgICAqLwogICAgbmVnYXRlKCkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CiAgICAgIGNvbnN0IG5lZ2F0ZWQgPSB7fTsKICAgICAgZm9yIChjb25zdCBrIG9mIE9iamVjdC5rZXlzKHRoaXMudmFsdWVzKSkgewogICAgICAgIG5lZ2F0ZWRba10gPSB0aGlzLnZhbHVlc1trXSA9PT0gMCA/IDAgOiAtdGhpcy52YWx1ZXNba107CiAgICAgIH0KICAgICAgcmV0dXJuIGNsb25lJDEodGhpcywgeyB2YWx1ZXM6IG5lZ2F0ZWQgfSwgdHJ1ZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIHllYXJzLgogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IHllYXJzKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy52YWx1ZXMueWVhcnMgfHwgMCA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgcXVhcnRlcnMuCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgcXVhcnRlcnMoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLnZhbHVlcy5xdWFydGVycyB8fCAwIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBtb250aHMuCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgbW9udGhzKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy52YWx1ZXMubW9udGhzIHx8IDAgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIHdlZWtzCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgd2Vla3MoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLnZhbHVlcy53ZWVrcyB8fCAwIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBkYXlzLgogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IGRheXMoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLnZhbHVlcy5kYXlzIHx8IDAgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIGhvdXJzLgogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IGhvdXJzKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy52YWx1ZXMuaG91cnMgfHwgMCA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgbWludXRlcy4KICAgICAqIEB0eXBlIHtudW1iZXJ9CiAgICAgKi8KICAgIGdldCBtaW51dGVzKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy52YWx1ZXMubWludXRlcyB8fCAwIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBzZWNvbmRzLgogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICBnZXQgc2Vjb25kcygpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMudmFsdWVzLnNlY29uZHMgfHwgMCA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgbWlsbGlzZWNvbmRzLgogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICBnZXQgbWlsbGlzZWNvbmRzKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy52YWx1ZXMubWlsbGlzZWNvbmRzIHx8IDAgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhlIER1cmF0aW9uIGlzIGludmFsaWQuIEludmFsaWQgZHVyYXRpb25zIGFyZSByZXR1cm5lZCBieSBkaWZmIG9wZXJhdGlvbnMKICAgICAqIG9uIGludmFsaWQgRGF0ZVRpbWVzIG9yIEludGVydmFscy4KICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIGdldCBpc1ZhbGlkKCkgewogICAgICByZXR1cm4gdGhpcy5pbnZhbGlkID09PSBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBlcnJvciBjb2RlIGlmIHRoaXMgRHVyYXRpb24gYmVjYW1lIGludmFsaWQsIG9yIG51bGwgaWYgdGhlIER1cmF0aW9uIGlzIHZhbGlkCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCBpbnZhbGlkUmVhc29uKCkgewogICAgICByZXR1cm4gdGhpcy5pbnZhbGlkID8gdGhpcy5pbnZhbGlkLnJlYXNvbiA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIGV4cGxhbmF0aW9uIG9mIHdoeSB0aGlzIER1cmF0aW9uIGJlY2FtZSBpbnZhbGlkLCBvciBudWxsIGlmIHRoZSBEdXJhdGlvbiBpcyB2YWxpZAogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IGludmFsaWRFeHBsYW5hdGlvbigpIHsKICAgICAgcmV0dXJuIHRoaXMuaW52YWxpZCA/IHRoaXMuaW52YWxpZC5leHBsYW5hdGlvbiA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBFcXVhbGl0eSBjaGVjawogICAgICogVHdvIER1cmF0aW9ucyBhcmUgZXF1YWwgaWZmIHRoZXkgaGF2ZSB0aGUgc2FtZSB1bml0cyBhbmQgdGhlIHNhbWUgdmFsdWVzIGZvciBlYWNoIHVuaXQuCiAgICAgKiBAcGFyYW0ge0R1cmF0aW9ufSBvdGhlcgogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgZXF1YWxzKG90aGVyKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkIHx8ICFvdGhlci5pc1ZhbGlkKSB7CiAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICB9CgogICAgICBpZiAoIXRoaXMubG9jLmVxdWFscyhvdGhlci5sb2MpKSB7CiAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICB9CgogICAgICBmdW5jdGlvbiBlcSh2MSwgdjIpIHsKICAgICAgICAvLyBDb25zaWRlciAwIGFuZCB1bmRlZmluZWQgYXMgZXF1YWwKICAgICAgICBpZiAodjEgPT09IHVuZGVmaW5lZCB8fCB2MSA9PT0gMCkgcmV0dXJuIHYyID09PSB1bmRlZmluZWQgfHwgdjIgPT09IDA7CiAgICAgICAgcmV0dXJuIHYxID09PSB2MjsKICAgICAgfQoKICAgICAgZm9yIChjb25zdCB1IG9mIG9yZGVyZWRVbml0cyQxKSB7CiAgICAgICAgaWYgKCFlcSh0aGlzLnZhbHVlc1t1XSwgb3RoZXIudmFsdWVzW3VdKSkgewogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KICB9CgogIGNvbnN0IElOVkFMSUQkMSA9ICJJbnZhbGlkIEludGVydmFsIjsKCiAgLy8gY2hlY2tzIGlmIHRoZSBzdGFydCBpcyBlcXVhbCB0byBvciBiZWZvcmUgdGhlIGVuZAogIGZ1bmN0aW9uIHZhbGlkYXRlU3RhcnRFbmQoc3RhcnQsIGVuZCkgewogICAgaWYgKCFzdGFydCB8fCAhc3RhcnQuaXNWYWxpZCkgewogICAgICByZXR1cm4gSW50ZXJ2YWwuaW52YWxpZCgibWlzc2luZyBvciBpbnZhbGlkIHN0YXJ0Iik7CiAgICB9IGVsc2UgaWYgKCFlbmQgfHwgIWVuZC5pc1ZhbGlkKSB7CiAgICAgIHJldHVybiBJbnRlcnZhbC5pbnZhbGlkKCJtaXNzaW5nIG9yIGludmFsaWQgZW5kIik7CiAgICB9IGVsc2UgaWYgKGVuZCA8IHN0YXJ0KSB7CiAgICAgIHJldHVybiBJbnRlcnZhbC5pbnZhbGlkKAogICAgICAgICJlbmQgYmVmb3JlIHN0YXJ0IiwKICAgICAgICBgVGhlIGVuZCBvZiBhbiBpbnRlcnZhbCBtdXN0IGJlIGFmdGVyIGl0cyBzdGFydCwgYnV0IHlvdSBoYWQgc3RhcnQ9JHtzdGFydC50b0lTTygpfSBhbmQgZW5kPSR7ZW5kLnRvSVNPKCl9YAogICAgICApOwogICAgfSBlbHNlIHsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgfQoKICAvKioKICAgKiBBbiBJbnRlcnZhbCBvYmplY3QgcmVwcmVzZW50cyBhIGhhbGYtb3BlbiBpbnRlcnZhbCBvZiB0aW1lLCB3aGVyZSBlYWNoIGVuZHBvaW50IGlzIGEge0BsaW5rIERhdGVUaW1lfS4gQ29uY2VwdHVhbGx5LCBpdCdzIGEgY29udGFpbmVyIGZvciB0aG9zZSB0d28gZW5kcG9pbnRzLCBhY2NvbXBhbmllZCBieSBtZXRob2RzIGZvciBjcmVhdGluZywgcGFyc2luZywgaW50ZXJyb2dhdGluZywgY29tcGFyaW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCBmb3JtYXR0aW5nIHRoZW0uCiAgICoKICAgKiBIZXJlIGlzIGEgYnJpZWYgb3ZlcnZpZXcgb2YgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBtZXRob2RzIGFuZCBnZXR0ZXJzIGluIEludGVydmFsOgogICAqCiAgICogKiAqKkNyZWF0aW9uKiogVG8gY3JlYXRlIGFuIEludGVydmFsLCB1c2Uge0BsaW5rIEludGVydmFsLmZyb21EYXRlVGltZXN9LCB7QGxpbmsgSW50ZXJ2YWwuYWZ0ZXJ9LCB7QGxpbmsgSW50ZXJ2YWwuYmVmb3JlfSwgb3Ige0BsaW5rIEludGVydmFsLmZyb21JU099LgogICAqICogKipBY2Nlc3NvcnMqKiBVc2Uge0BsaW5rIEludGVydmFsI3N0YXJ0fSBhbmQge0BsaW5rIEludGVydmFsI2VuZH0gdG8gZ2V0IHRoZSBzdGFydCBhbmQgZW5kLgogICAqICogKipJbnRlcnJvZ2F0aW9uKiogVG8gYW5hbHl6ZSB0aGUgSW50ZXJ2YWwsIHVzZSB7QGxpbmsgSW50ZXJ2YWwjY291bnR9LCB7QGxpbmsgSW50ZXJ2YWwjbGVuZ3RofSwge0BsaW5rIEludGVydmFsI2hhc1NhbWV9LCB7QGxpbmsgSW50ZXJ2YWwjY29udGFpbnN9LCB7QGxpbmsgSW50ZXJ2YWwjaXNBZnRlcn0sIG9yIHtAbGluayBJbnRlcnZhbCNpc0JlZm9yZX0uCiAgICogKiAqKlRyYW5zZm9ybWF0aW9uKiogVG8gY3JlYXRlIG90aGVyIEludGVydmFscyBvdXQgb2YgdGhpcyBvbmUsIHVzZSB7QGxpbmsgSW50ZXJ2YWwjc2V0fSwge0BsaW5rIEludGVydmFsI3NwbGl0QXR9LCB7QGxpbmsgSW50ZXJ2YWwjc3BsaXRCeX0sIHtAbGluayBJbnRlcnZhbCNkaXZpZGVFcXVhbGx5fSwge0BsaW5rIEludGVydmFsLm1lcmdlfSwge0BsaW5rIEludGVydmFsLnhvcn0sIHtAbGluayBJbnRlcnZhbCN1bmlvbn0sIHtAbGluayBJbnRlcnZhbCNpbnRlcnNlY3Rpb259LCBvciB7QGxpbmsgSW50ZXJ2YWwjZGlmZmVyZW5jZX0uCiAgICogKiAqKkNvbXBhcmlzb24qKiBUbyBjb21wYXJlIHRoaXMgSW50ZXJ2YWwgdG8gYW5vdGhlciBvbmUsIHVzZSB7QGxpbmsgSW50ZXJ2YWwjZXF1YWxzfSwge0BsaW5rIEludGVydmFsI292ZXJsYXBzfSwge0BsaW5rIEludGVydmFsI2FidXRzU3RhcnR9LCB7QGxpbmsgSW50ZXJ2YWwjYWJ1dHNFbmR9LCB7QGxpbmsgSW50ZXJ2YWwjZW5ndWxmc30KICAgKiAqICoqT3V0cHV0KiogVG8gY29udmVydCB0aGUgSW50ZXJ2YWwgaW50byBvdGhlciByZXByZXNlbnRhdGlvbnMsIHNlZSB7QGxpbmsgSW50ZXJ2YWwjdG9TdHJpbmd9LCB7QGxpbmsgSW50ZXJ2YWwjdG9Mb2NhbGVTdHJpbmd9LCB7QGxpbmsgSW50ZXJ2YWwjdG9JU099LCB7QGxpbmsgSW50ZXJ2YWwjdG9JU09EYXRlfSwge0BsaW5rIEludGVydmFsI3RvSVNPVGltZX0sIHtAbGluayBJbnRlcnZhbCN0b0Zvcm1hdH0sIGFuZCB7QGxpbmsgSW50ZXJ2YWwjdG9EdXJhdGlvbn0uCiAgICovCiAgY2xhc3MgSW50ZXJ2YWwgewogICAgLyoqCiAgICAgKiBAcHJpdmF0ZQogICAgICovCiAgICBjb25zdHJ1Y3Rvcihjb25maWcpIHsKICAgICAgLyoqCiAgICAgICAqIEBhY2Nlc3MgcHJpdmF0ZQogICAgICAgKi8KICAgICAgdGhpcy5zID0gY29uZmlnLnN0YXJ0OwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLmUgPSBjb25maWcuZW5kOwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLmludmFsaWQgPSBjb25maWcuaW52YWxpZCB8fCBudWxsOwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLmlzTHV4b25JbnRlcnZhbCA9IHRydWU7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYW4gaW52YWxpZCBJbnRlcnZhbC4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSByZWFzb24gLSBzaW1wbGUgc3RyaW5nIG9mIHdoeSB0aGlzIEludGVydmFsIGlzIGludmFsaWQuIFNob3VsZCBub3QgY29udGFpbiBwYXJhbWV0ZXJzIG9yIGFueXRoaW5nIGVsc2UgZGF0YS1kZXBlbmRlbnQKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbZXhwbGFuYXRpb249bnVsbF0gLSBsb25nZXIgZXhwbGFuYXRpb24sIG1heSBpbmNsdWRlIHBhcmFtZXRlcnMgYW5kIG90aGVyIHVzZWZ1bCBkZWJ1Z2dpbmcgaW5mb3JtYXRpb24KICAgICAqIEByZXR1cm4ge0ludGVydmFsfQogICAgICovCiAgICBzdGF0aWMgaW52YWxpZChyZWFzb24sIGV4cGxhbmF0aW9uID0gbnVsbCkgewogICAgICBpZiAoIXJlYXNvbikgewogICAgICAgIHRocm93IG5ldyBJbnZhbGlkQXJndW1lbnRFcnJvcigibmVlZCB0byBzcGVjaWZ5IGEgcmVhc29uIHRoZSBJbnRlcnZhbCBpcyBpbnZhbGlkIik7CiAgICAgIH0KCiAgICAgIGNvbnN0IGludmFsaWQgPSByZWFzb24gaW5zdGFuY2VvZiBJbnZhbGlkID8gcmVhc29uIDogbmV3IEludmFsaWQocmVhc29uLCBleHBsYW5hdGlvbik7CgogICAgICBpZiAoU2V0dGluZ3MudGhyb3dPbkludmFsaWQpIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZEludGVydmFsRXJyb3IoaW52YWxpZCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIG5ldyBJbnRlcnZhbCh7IGludmFsaWQgfSk7CiAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZSBhbiBJbnRlcnZhbCBmcm9tIGEgc3RhcnQgRGF0ZVRpbWUgYW5kIGFuIGVuZCBEYXRlVGltZS4gSW5jbHVzaXZlIG9mIHRoZSBzdGFydCBidXQgbm90IHRoZSBlbmQuCiAgICAgKiBAcGFyYW0ge0RhdGVUaW1lfERhdGV8T2JqZWN0fSBzdGFydAogICAgICogQHBhcmFtIHtEYXRlVGltZXxEYXRlfE9iamVjdH0gZW5kCiAgICAgKiBAcmV0dXJuIHtJbnRlcnZhbH0KICAgICAqLwogICAgc3RhdGljIGZyb21EYXRlVGltZXMoc3RhcnQsIGVuZCkgewogICAgICBjb25zdCBidWlsdFN0YXJ0ID0gZnJpZW5kbHlEYXRlVGltZShzdGFydCksCiAgICAgICAgYnVpbHRFbmQgPSBmcmllbmRseURhdGVUaW1lKGVuZCk7CgogICAgICBjb25zdCB2YWxpZGF0ZUVycm9yID0gdmFsaWRhdGVTdGFydEVuZChidWlsdFN0YXJ0LCBidWlsdEVuZCk7CgogICAgICBpZiAodmFsaWRhdGVFcnJvciA9PSBudWxsKSB7CiAgICAgICAgcmV0dXJuIG5ldyBJbnRlcnZhbCh7CiAgICAgICAgICBzdGFydDogYnVpbHRTdGFydCwKICAgICAgICAgIGVuZDogYnVpbHRFbmQsCiAgICAgICAgfSk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIHZhbGlkYXRlRXJyb3I7CiAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZSBhbiBJbnRlcnZhbCBmcm9tIGEgc3RhcnQgRGF0ZVRpbWUgYW5kIGEgRHVyYXRpb24gdG8gZXh0ZW5kIHRvLgogICAgICogQHBhcmFtIHtEYXRlVGltZXxEYXRlfE9iamVjdH0gc3RhcnQKICAgICAqIEBwYXJhbSB7RHVyYXRpb258T2JqZWN0fG51bWJlcn0gZHVyYXRpb24gLSB0aGUgbGVuZ3RoIG9mIHRoZSBJbnRlcnZhbC4KICAgICAqIEByZXR1cm4ge0ludGVydmFsfQogICAgICovCiAgICBzdGF0aWMgYWZ0ZXIoc3RhcnQsIGR1cmF0aW9uKSB7CiAgICAgIGNvbnN0IGR1ciA9IER1cmF0aW9uLmZyb21EdXJhdGlvbkxpa2UoZHVyYXRpb24pLAogICAgICAgIGR0ID0gZnJpZW5kbHlEYXRlVGltZShzdGFydCk7CiAgICAgIHJldHVybiBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKGR0LCBkdC5wbHVzKGR1cikpOwogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGFuIEludGVydmFsIGZyb20gYW4gZW5kIERhdGVUaW1lIGFuZCBhIER1cmF0aW9uIHRvIGV4dGVuZCBiYWNrd2FyZHMgdG8uCiAgICAgKiBAcGFyYW0ge0RhdGVUaW1lfERhdGV8T2JqZWN0fSBlbmQKICAgICAqIEBwYXJhbSB7RHVyYXRpb258T2JqZWN0fG51bWJlcn0gZHVyYXRpb24gLSB0aGUgbGVuZ3RoIG9mIHRoZSBJbnRlcnZhbC4KICAgICAqIEByZXR1cm4ge0ludGVydmFsfQogICAgICovCiAgICBzdGF0aWMgYmVmb3JlKGVuZCwgZHVyYXRpb24pIHsKICAgICAgY29uc3QgZHVyID0gRHVyYXRpb24uZnJvbUR1cmF0aW9uTGlrZShkdXJhdGlvbiksCiAgICAgICAgZHQgPSBmcmllbmRseURhdGVUaW1lKGVuZCk7CiAgICAgIHJldHVybiBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKGR0Lm1pbnVzKGR1ciksIGR0KTsKICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZSBhbiBJbnRlcnZhbCBmcm9tIGFuIElTTyA4NjAxIHN0cmluZy4KICAgICAqIEFjY2VwdHMgYDxzdGFydD4vPGVuZD5gLCBgPHN0YXJ0Pi88ZHVyYXRpb24+YCwgYW5kIGA8ZHVyYXRpb24+LzxlbmQ+YCBmb3JtYXRzLgogICAgICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSB0aGUgSVNPIHN0cmluZyB0byBwYXJzZQogICAgICogQHBhcmFtIHtPYmplY3R9IFtvcHRzXSAtIG9wdGlvbnMgdG8gcGFzcyB7QGxpbmsgRGF0ZVRpbWUjZnJvbUlTT30gYW5kIG9wdGlvbmFsbHkge0BsaW5rIER1cmF0aW9uI2Zyb21JU099CiAgICAgKiBAc2VlIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT184NjAxI1RpbWVfaW50ZXJ2YWxzCiAgICAgKiBAcmV0dXJuIHtJbnRlcnZhbH0KICAgICAqLwogICAgc3RhdGljIGZyb21JU08odGV4dCwgb3B0cykgewogICAgICBjb25zdCBbcywgZV0gPSAodGV4dCB8fCAiIikuc3BsaXQoIi8iLCAyKTsKICAgICAgaWYgKHMgJiYgZSkgewogICAgICAgIGxldCBzdGFydCwgc3RhcnRJc1ZhbGlkOwogICAgICAgIHRyeSB7CiAgICAgICAgICBzdGFydCA9IERhdGVUaW1lLmZyb21JU08ocywgb3B0cyk7CiAgICAgICAgICBzdGFydElzVmFsaWQgPSBzdGFydC5pc1ZhbGlkOwogICAgICAgIH0gY2F0Y2ggKGUpIHsKICAgICAgICAgIHN0YXJ0SXNWYWxpZCA9IGZhbHNlOwogICAgICAgIH0KCiAgICAgICAgbGV0IGVuZCwgZW5kSXNWYWxpZDsKICAgICAgICB0cnkgewogICAgICAgICAgZW5kID0gRGF0ZVRpbWUuZnJvbUlTTyhlLCBvcHRzKTsKICAgICAgICAgIGVuZElzVmFsaWQgPSBlbmQuaXNWYWxpZDsKICAgICAgICB9IGNhdGNoIChlKSB7CiAgICAgICAgICBlbmRJc1ZhbGlkID0gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICBpZiAoc3RhcnRJc1ZhbGlkICYmIGVuZElzVmFsaWQpIHsKICAgICAgICAgIHJldHVybiBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKHN0YXJ0LCBlbmQpOwogICAgICAgIH0KCiAgICAgICAgaWYgKHN0YXJ0SXNWYWxpZCkgewogICAgICAgICAgY29uc3QgZHVyID0gRHVyYXRpb24uZnJvbUlTTyhlLCBvcHRzKTsKICAgICAgICAgIGlmIChkdXIuaXNWYWxpZCkgewogICAgICAgICAgICByZXR1cm4gSW50ZXJ2YWwuYWZ0ZXIoc3RhcnQsIGR1cik7CiAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIGlmIChlbmRJc1ZhbGlkKSB7CiAgICAgICAgICBjb25zdCBkdXIgPSBEdXJhdGlvbi5mcm9tSVNPKHMsIG9wdHMpOwogICAgICAgICAgaWYgKGR1ci5pc1ZhbGlkKSB7CiAgICAgICAgICAgIHJldHVybiBJbnRlcnZhbC5iZWZvcmUoZW5kLCBkdXIpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gSW50ZXJ2YWwuaW52YWxpZCgidW5wYXJzYWJsZSIsIGB0aGUgaW5wdXQgIiR7dGV4dH0iIGNhbid0IGJlIHBhcnNlZCBhcyBJU08gODYwMWApOwogICAgfQoKICAgIC8qKgogICAgICogQ2hlY2sgaWYgYW4gb2JqZWN0IGlzIGFuIEludGVydmFsLiBXb3JrcyBhY3Jvc3MgY29udGV4dCBib3VuZGFyaWVzCiAgICAgKiBAcGFyYW0ge29iamVjdH0gbwogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgc3RhdGljIGlzSW50ZXJ2YWwobykgewogICAgICByZXR1cm4gKG8gJiYgby5pc0x1eG9uSW50ZXJ2YWwpIHx8IGZhbHNlOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyB0aGUgc3RhcnQgb2YgdGhlIEludGVydmFsCiAgICAgKiBAdHlwZSB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIGdldCBzdGFydCgpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMucyA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHRoZSBlbmQgb2YgdGhlIEludGVydmFsCiAgICAgKiBAdHlwZSB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIGdldCBlbmQoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLmUgOiBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyB3aGV0aGVyIHRoaXMgSW50ZXJ2YWwncyBlbmQgaXMgYXQgbGVhc3QgaXRzIHN0YXJ0LCBtZWFuaW5nIHRoYXQgdGhlIEludGVydmFsIGlzbid0ICdiYWNrd2FyZHMnLgogICAgICogQHR5cGUge2Jvb2xlYW59CiAgICAgKi8KICAgIGdldCBpc1ZhbGlkKCkgewogICAgICByZXR1cm4gdGhpcy5pbnZhbGlkUmVhc29uID09PSBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBlcnJvciBjb2RlIGlmIHRoaXMgSW50ZXJ2YWwgaXMgaW52YWxpZCwgb3IgbnVsbCBpZiB0aGUgSW50ZXJ2YWwgaXMgdmFsaWQKICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCBpbnZhbGlkUmVhc29uKCkgewogICAgICByZXR1cm4gdGhpcy5pbnZhbGlkID8gdGhpcy5pbnZhbGlkLnJlYXNvbiA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIGV4cGxhbmF0aW9uIG9mIHdoeSB0aGlzIEludGVydmFsIGJlY2FtZSBpbnZhbGlkLCBvciBudWxsIGlmIHRoZSBJbnRlcnZhbCBpcyB2YWxpZAogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IGludmFsaWRFeHBsYW5hdGlvbigpIHsKICAgICAgcmV0dXJuIHRoaXMuaW52YWxpZCA/IHRoaXMuaW52YWxpZC5leHBsYW5hdGlvbiA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHRoZSBsZW5ndGggb2YgdGhlIEludGVydmFsIGluIHRoZSBzcGVjaWZpZWQgdW5pdC4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSB1bml0IC0gdGhlIHVuaXQgKHN1Y2ggYXMgJ2hvdXJzJyBvciAnZGF5cycpIHRvIHJldHVybiB0aGUgbGVuZ3RoIGluLgogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICBsZW5ndGgodW5pdCA9ICJtaWxsaXNlY29uZHMiKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLnRvRHVyYXRpb24oLi4uW3VuaXRdKS5nZXQodW5pdCkgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHRoZSBjb3VudCBvZiBtaW51dGVzLCBob3VycywgZGF5cywgbW9udGhzLCBvciB5ZWFycyBpbmNsdWRlZCBpbiB0aGUgSW50ZXJ2YWwsIGV2ZW4gaW4gcGFydC4KICAgICAqIFVubGlrZSB7QGxpbmsgSW50ZXJ2YWwjbGVuZ3RofSB0aGlzIGNvdW50cyBzZWN0aW9ucyBvZiB0aGUgY2FsZW5kYXIsIG5vdCBwZXJpb2RzIG9mIHRpbWUsIGUuZy4gc3BlY2lmeWluZyAnZGF5JwogICAgICogYXNrcyAnd2hhdCBkYXRlcyBhcmUgaW5jbHVkZWQgaW4gdGhpcyBpbnRlcnZhbD8nLCBub3QgJ2hvdyBtYW55IGRheXMgbG9uZyBpcyB0aGlzIGludGVydmFsPycKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbdW5pdD0nbWlsbGlzZWNvbmRzJ10gLSB0aGUgdW5pdCBvZiB0aW1lIHRvIGNvdW50LgogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICBjb3VudCh1bml0ID0gIm1pbGxpc2Vjb25kcyIpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBOYU47CiAgICAgIGNvbnN0IHN0YXJ0ID0gdGhpcy5zdGFydC5zdGFydE9mKHVuaXQpLAogICAgICAgIGVuZCA9IHRoaXMuZW5kLnN0YXJ0T2YodW5pdCk7CiAgICAgIHJldHVybiBNYXRoLmZsb29yKGVuZC5kaWZmKHN0YXJ0LCB1bml0KS5nZXQodW5pdCkpICsgKGVuZC52YWx1ZU9mKCkgIT09IHRoaXMuZW5kLnZhbHVlT2YoKSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHdoZXRoZXIgdGhpcyBJbnRlcnZhbCdzIHN0YXJ0IGFuZCBlbmQgYXJlIGJvdGggaW4gdGhlIHNhbWUgdW5pdCBvZiB0aW1lCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdW5pdCAtIHRoZSB1bml0IG9mIHRpbWUgdG8gY2hlY2sgc2FtZW5lc3Mgb24KICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIGhhc1NhbWUodW5pdCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy5pc0VtcHR5KCkgfHwgdGhpcy5lLm1pbnVzKDEpLmhhc1NhbWUodGhpcy5zLCB1bml0KSA6IGZhbHNlOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHdoZXRoZXIgdGhpcyBJbnRlcnZhbCBoYXMgdGhlIHNhbWUgc3RhcnQgYW5kIGVuZCBEYXRlVGltZXMuCiAgICAgKiBAcmV0dXJuIHtib29sZWFufQogICAgICovCiAgICBpc0VtcHR5KCkgewogICAgICByZXR1cm4gdGhpcy5zLnZhbHVlT2YoKSA9PT0gdGhpcy5lLnZhbHVlT2YoKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB3aGV0aGVyIHRoaXMgSW50ZXJ2YWwncyBzdGFydCBpcyBhZnRlciB0aGUgc3BlY2lmaWVkIERhdGVUaW1lLgogICAgICogQHBhcmFtIHtEYXRlVGltZX0gZGF0ZVRpbWUKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIGlzQWZ0ZXIoZGF0ZVRpbWUpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBmYWxzZTsKICAgICAgcmV0dXJuIHRoaXMucyA+IGRhdGVUaW1lOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHdoZXRoZXIgdGhpcyBJbnRlcnZhbCdzIGVuZCBpcyBiZWZvcmUgdGhlIHNwZWNpZmllZCBEYXRlVGltZS4KICAgICAqIEBwYXJhbSB7RGF0ZVRpbWV9IGRhdGVUaW1lCiAgICAgKiBAcmV0dXJuIHtib29sZWFufQogICAgICovCiAgICBpc0JlZm9yZShkYXRlVGltZSkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIGZhbHNlOwogICAgICByZXR1cm4gdGhpcy5lIDw9IGRhdGVUaW1lOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHdoZXRoZXIgdGhpcyBJbnRlcnZhbCBjb250YWlucyB0aGUgc3BlY2lmaWVkIERhdGVUaW1lLgogICAgICogQHBhcmFtIHtEYXRlVGltZX0gZGF0ZVRpbWUKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIGNvbnRhaW5zKGRhdGVUaW1lKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gZmFsc2U7CiAgICAgIHJldHVybiB0aGlzLnMgPD0gZGF0ZVRpbWUgJiYgdGhpcy5lID4gZGF0ZVRpbWU7CiAgICB9CgogICAgLyoqCiAgICAgKiAiU2V0cyIgdGhlIHN0YXJ0IGFuZC9vciBlbmQgZGF0ZXMuIFJldHVybnMgYSBuZXdseS1jb25zdHJ1Y3RlZCBJbnRlcnZhbC4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YWx1ZXMgLSB0aGUgdmFsdWVzIHRvIHNldAogICAgICogQHBhcmFtIHtEYXRlVGltZX0gdmFsdWVzLnN0YXJ0IC0gdGhlIHN0YXJ0aW5nIERhdGVUaW1lCiAgICAgKiBAcGFyYW0ge0RhdGVUaW1lfSB2YWx1ZXMuZW5kIC0gdGhlIGVuZGluZyBEYXRlVGltZQogICAgICogQHJldHVybiB7SW50ZXJ2YWx9CiAgICAgKi8KICAgIHNldCh7IHN0YXJ0LCBlbmQgfSA9IHt9KSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gdGhpczsKICAgICAgcmV0dXJuIEludGVydmFsLmZyb21EYXRlVGltZXMoc3RhcnQgfHwgdGhpcy5zLCBlbmQgfHwgdGhpcy5lKTsKICAgIH0KCiAgICAvKioKICAgICAqIFNwbGl0IHRoaXMgSW50ZXJ2YWwgYXQgZWFjaCBvZiB0aGUgc3BlY2lmaWVkIERhdGVUaW1lcwogICAgICogQHBhcmFtIHsuLi5EYXRlVGltZX0gZGF0ZVRpbWVzIC0gdGhlIHVuaXQgb2YgdGltZSB0byBjb3VudC4KICAgICAqIEByZXR1cm4ge0FycmF5fQogICAgICovCiAgICBzcGxpdEF0KC4uLmRhdGVUaW1lcykgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIFtdOwogICAgICBjb25zdCBzb3J0ZWQgPSBkYXRlVGltZXMKICAgICAgICAgIC5tYXAoZnJpZW5kbHlEYXRlVGltZSkKICAgICAgICAgIC5maWx0ZXIoKGQpID0+IHRoaXMuY29udGFpbnMoZCkpCiAgICAgICAgICAuc29ydCgpLAogICAgICAgIHJlc3VsdHMgPSBbXTsKICAgICAgbGV0IHsgcyB9ID0gdGhpcywKICAgICAgICBpID0gMDsKCiAgICAgIHdoaWxlIChzIDwgdGhpcy5lKSB7CiAgICAgICAgY29uc3QgYWRkZWQgPSBzb3J0ZWRbaV0gfHwgdGhpcy5lLAogICAgICAgICAgbmV4dCA9ICthZGRlZCA+ICt0aGlzLmUgPyB0aGlzLmUgOiBhZGRlZDsKICAgICAgICByZXN1bHRzLnB1c2goSW50ZXJ2YWwuZnJvbURhdGVUaW1lcyhzLCBuZXh0KSk7CiAgICAgICAgcyA9IG5leHQ7CiAgICAgICAgaSArPSAxOwogICAgICB9CgogICAgICByZXR1cm4gcmVzdWx0czsKICAgIH0KCiAgICAvKioKICAgICAqIFNwbGl0IHRoaXMgSW50ZXJ2YWwgaW50byBzbWFsbGVyIEludGVydmFscywgZWFjaCBvZiB0aGUgc3BlY2lmaWVkIGxlbmd0aC4KICAgICAqIExlZnQgb3ZlciB0aW1lIGlzIGdyb3VwZWQgaW50byBhIHNtYWxsZXIgaW50ZXJ2YWwKICAgICAqIEBwYXJhbSB7RHVyYXRpb258T2JqZWN0fG51bWJlcn0gZHVyYXRpb24gLSBUaGUgbGVuZ3RoIG9mIGVhY2ggcmVzdWx0aW5nIGludGVydmFsLgogICAgICogQHJldHVybiB7QXJyYXl9CiAgICAgKi8KICAgIHNwbGl0QnkoZHVyYXRpb24pIHsKICAgICAgY29uc3QgZHVyID0gRHVyYXRpb24uZnJvbUR1cmF0aW9uTGlrZShkdXJhdGlvbik7CgogICAgICBpZiAoIXRoaXMuaXNWYWxpZCB8fCAhZHVyLmlzVmFsaWQgfHwgZHVyLmFzKCJtaWxsaXNlY29uZHMiKSA9PT0gMCkgewogICAgICAgIHJldHVybiBbXTsKICAgICAgfQoKICAgICAgbGV0IHsgcyB9ID0gdGhpcywKICAgICAgICBpZHggPSAxLAogICAgICAgIG5leHQ7CgogICAgICBjb25zdCByZXN1bHRzID0gW107CiAgICAgIHdoaWxlIChzIDwgdGhpcy5lKSB7CiAgICAgICAgY29uc3QgYWRkZWQgPSB0aGlzLnN0YXJ0LnBsdXMoZHVyLm1hcFVuaXRzKCh4KSA9PiB4ICogaWR4KSk7CiAgICAgICAgbmV4dCA9ICthZGRlZCA+ICt0aGlzLmUgPyB0aGlzLmUgOiBhZGRlZDsKICAgICAgICByZXN1bHRzLnB1c2goSW50ZXJ2YWwuZnJvbURhdGVUaW1lcyhzLCBuZXh0KSk7CiAgICAgICAgcyA9IG5leHQ7CiAgICAgICAgaWR4ICs9IDE7CiAgICAgIH0KCiAgICAgIHJldHVybiByZXN1bHRzOwogICAgfQoKICAgIC8qKgogICAgICogU3BsaXQgdGhpcyBJbnRlcnZhbCBpbnRvIHRoZSBzcGVjaWZpZWQgbnVtYmVyIG9mIHNtYWxsZXIgaW50ZXJ2YWxzLgogICAgICogQHBhcmFtIHtudW1iZXJ9IG51bWJlck9mUGFydHMgLSBUaGUgbnVtYmVyIG9mIEludGVydmFscyB0byBkaXZpZGUgdGhlIEludGVydmFsIGludG8uCiAgICAgKiBAcmV0dXJuIHtBcnJheX0KICAgICAqLwogICAgZGl2aWRlRXF1YWxseShudW1iZXJPZlBhcnRzKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gW107CiAgICAgIHJldHVybiB0aGlzLnNwbGl0QnkodGhpcy5sZW5ndGgoKSAvIG51bWJlck9mUGFydHMpLnNsaWNlKDAsIG51bWJlck9mUGFydHMpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHdoZXRoZXIgdGhpcyBJbnRlcnZhbCBvdmVybGFwcyB3aXRoIHRoZSBzcGVjaWZpZWQgSW50ZXJ2YWwKICAgICAqIEBwYXJhbSB7SW50ZXJ2YWx9IG90aGVyCiAgICAgKiBAcmV0dXJuIHtib29sZWFufQogICAgICovCiAgICBvdmVybGFwcyhvdGhlcikgewogICAgICByZXR1cm4gdGhpcy5lID4gb3RoZXIucyAmJiB0aGlzLnMgPCBvdGhlci5lOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIHdoZXRoZXIgdGhpcyBJbnRlcnZhbCdzIGVuZCBpcyBhZGphY2VudCB0byB0aGUgc3BlY2lmaWVkIEludGVydmFsJ3Mgc3RhcnQuCiAgICAgKiBAcGFyYW0ge0ludGVydmFsfSBvdGhlcgogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgYWJ1dHNTdGFydChvdGhlcikgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIGZhbHNlOwogICAgICByZXR1cm4gK3RoaXMuZSA9PT0gK290aGVyLnM7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gd2hldGhlciB0aGlzIEludGVydmFsJ3Mgc3RhcnQgaXMgYWRqYWNlbnQgdG8gdGhlIHNwZWNpZmllZCBJbnRlcnZhbCdzIGVuZC4KICAgICAqIEBwYXJhbSB7SW50ZXJ2YWx9IG90aGVyCiAgICAgKiBAcmV0dXJuIHtib29sZWFufQogICAgICovCiAgICBhYnV0c0VuZChvdGhlcikgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIGZhbHNlOwogICAgICByZXR1cm4gK290aGVyLmUgPT09ICt0aGlzLnM7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gd2hldGhlciB0aGlzIEludGVydmFsIGVuZ3VsZnMgdGhlIHN0YXJ0IGFuZCBlbmQgb2YgdGhlIHNwZWNpZmllZCBJbnRlcnZhbC4KICAgICAqIEBwYXJhbSB7SW50ZXJ2YWx9IG90aGVyCiAgICAgKiBAcmV0dXJuIHtib29sZWFufQogICAgICovCiAgICBlbmd1bGZzKG90aGVyKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gZmFsc2U7CiAgICAgIHJldHVybiB0aGlzLnMgPD0gb3RoZXIucyAmJiB0aGlzLmUgPj0gb3RoZXIuZTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB3aGV0aGVyIHRoaXMgSW50ZXJ2YWwgaGFzIHRoZSBzYW1lIHN0YXJ0IGFuZCBlbmQgYXMgdGhlIHNwZWNpZmllZCBJbnRlcnZhbC4KICAgICAqIEBwYXJhbSB7SW50ZXJ2YWx9IG90aGVyCiAgICAgKiBAcmV0dXJuIHtib29sZWFufQogICAgICovCiAgICBlcXVhbHMob3RoZXIpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQgfHwgIW90aGVyLmlzVmFsaWQpIHsKICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgIH0KCiAgICAgIHJldHVybiB0aGlzLnMuZXF1YWxzKG90aGVyLnMpICYmIHRoaXMuZS5lcXVhbHMob3RoZXIuZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gSW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBpbnRlcnNlY3Rpb24gb2YgdGhpcyBJbnRlcnZhbCBhbmQgdGhlIHNwZWNpZmllZCBJbnRlcnZhbC4KICAgICAqIFNwZWNpZmljYWxseSwgdGhlIHJlc3VsdGluZyBJbnRlcnZhbCBoYXMgdGhlIG1heGltdW0gc3RhcnQgdGltZSBhbmQgdGhlIG1pbmltdW0gZW5kIHRpbWUgb2YgdGhlIHR3byBJbnRlcnZhbHMuCiAgICAgKiBSZXR1cm5zIG51bGwgaWYgdGhlIGludGVyc2VjdGlvbiBpcyBlbXB0eSwgbWVhbmluZywgdGhlIGludGVydmFscyBkb24ndCBpbnRlcnNlY3QuCiAgICAgKiBAcGFyYW0ge0ludGVydmFsfSBvdGhlcgogICAgICogQHJldHVybiB7SW50ZXJ2YWx9CiAgICAgKi8KICAgIGludGVyc2VjdGlvbihvdGhlcikgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CiAgICAgIGNvbnN0IHMgPSB0aGlzLnMgPiBvdGhlci5zID8gdGhpcy5zIDogb3RoZXIucywKICAgICAgICBlID0gdGhpcy5lIDwgb3RoZXIuZSA/IHRoaXMuZSA6IG90aGVyLmU7CgogICAgICBpZiAocyA+PSBlKSB7CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIEludGVydmFsLmZyb21EYXRlVGltZXMocywgZSk7CiAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiBhbiBJbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIHVuaW9uIG9mIHRoaXMgSW50ZXJ2YWwgYW5kIHRoZSBzcGVjaWZpZWQgSW50ZXJ2YWwuCiAgICAgKiBTcGVjaWZpY2FsbHksIHRoZSByZXN1bHRpbmcgSW50ZXJ2YWwgaGFzIHRoZSBtaW5pbXVtIHN0YXJ0IHRpbWUgYW5kIHRoZSBtYXhpbXVtIGVuZCB0aW1lIG9mIHRoZSB0d28gSW50ZXJ2YWxzLgogICAgICogQHBhcmFtIHtJbnRlcnZhbH0gb3RoZXIKICAgICAqIEByZXR1cm4ge0ludGVydmFsfQogICAgICovCiAgICB1bmlvbihvdGhlcikgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CiAgICAgIGNvbnN0IHMgPSB0aGlzLnMgPCBvdGhlci5zID8gdGhpcy5zIDogb3RoZXIucywKICAgICAgICBlID0gdGhpcy5lID4gb3RoZXIuZSA/IHRoaXMuZSA6IG90aGVyLmU7CiAgICAgIHJldHVybiBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKHMsIGUpOwogICAgfQoKICAgIC8qKgogICAgICogTWVyZ2UgYW4gYXJyYXkgb2YgSW50ZXJ2YWxzIGludG8gYSBlcXVpdmFsZW50IG1pbmltYWwgc2V0IG9mIEludGVydmFscy4KICAgICAqIENvbWJpbmVzIG92ZXJsYXBwaW5nIGFuZCBhZGphY2VudCBJbnRlcnZhbHMuCiAgICAgKiBAcGFyYW0ge0FycmF5fSBpbnRlcnZhbHMKICAgICAqIEByZXR1cm4ge0FycmF5fQogICAgICovCiAgICBzdGF0aWMgbWVyZ2UoaW50ZXJ2YWxzKSB7CiAgICAgIGNvbnN0IFtmb3VuZCwgZmluYWxdID0gaW50ZXJ2YWxzCiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGEucyAtIGIucykKICAgICAgICAucmVkdWNlKAogICAgICAgICAgKFtzb2ZhciwgY3VycmVudF0sIGl0ZW0pID0+IHsKICAgICAgICAgICAgaWYgKCFjdXJyZW50KSB7CiAgICAgICAgICAgICAgcmV0dXJuIFtzb2ZhciwgaXRlbV07CiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VycmVudC5vdmVybGFwcyhpdGVtKSB8fCBjdXJyZW50LmFidXRzU3RhcnQoaXRlbSkpIHsKICAgICAgICAgICAgICByZXR1cm4gW3NvZmFyLCBjdXJyZW50LnVuaW9uKGl0ZW0pXTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICByZXR1cm4gW3NvZmFyLmNvbmNhdChbY3VycmVudF0pLCBpdGVtXTsKICAgICAgICAgICAgfQogICAgICAgICAgfSwKICAgICAgICAgIFtbXSwgbnVsbF0KICAgICAgICApOwogICAgICBpZiAoZmluYWwpIHsKICAgICAgICBmb3VuZC5wdXNoKGZpbmFsKTsKICAgICAgfQogICAgICByZXR1cm4gZm91bmQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gYXJyYXkgb2YgSW50ZXJ2YWxzIHJlcHJlc2VudGluZyB0aGUgc3BhbnMgb2YgdGltZSB0aGF0IG9ubHkgYXBwZWFyIGluIG9uZSBvZiB0aGUgc3BlY2lmaWVkIEludGVydmFscy4KICAgICAqIEBwYXJhbSB7QXJyYXl9IGludGVydmFscwogICAgICogQHJldHVybiB7QXJyYXl9CiAgICAgKi8KICAgIHN0YXRpYyB4b3IoaW50ZXJ2YWxzKSB7CiAgICAgIGxldCBzdGFydCA9IG51bGwsCiAgICAgICAgY3VycmVudENvdW50ID0gMDsKICAgICAgY29uc3QgcmVzdWx0cyA9IFtdLAogICAgICAgIGVuZHMgPSBpbnRlcnZhbHMubWFwKChpKSA9PiBbCiAgICAgICAgICB7IHRpbWU6IGkucywgdHlwZTogInMiIH0sCiAgICAgICAgICB7IHRpbWU6IGkuZSwgdHlwZTogImUiIH0sCiAgICAgICAgXSksCiAgICAgICAgZmxhdHRlbmVkID0gQXJyYXkucHJvdG90eXBlLmNvbmNhdCguLi5lbmRzKSwKICAgICAgICBhcnIgPSBmbGF0dGVuZWQuc29ydCgoYSwgYikgPT4gYS50aW1lIC0gYi50aW1lKTsKCiAgICAgIGZvciAoY29uc3QgaSBvZiBhcnIpIHsKICAgICAgICBjdXJyZW50Q291bnQgKz0gaS50eXBlID09PSAicyIgPyAxIDogLTE7CgogICAgICAgIGlmIChjdXJyZW50Q291bnQgPT09IDEpIHsKICAgICAgICAgIHN0YXJ0ID0gaS50aW1lOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBpZiAoc3RhcnQgJiYgK3N0YXJ0ICE9PSAraS50aW1lKSB7CiAgICAgICAgICAgIHJlc3VsdHMucHVzaChJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKHN0YXJ0LCBpLnRpbWUpKTsKICAgICAgICAgIH0KCiAgICAgICAgICBzdGFydCA9IG51bGw7CiAgICAgICAgfQogICAgICB9CgogICAgICByZXR1cm4gSW50ZXJ2YWwubWVyZ2UocmVzdWx0cyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gSW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBzcGFuIG9mIHRpbWUgaW4gdGhpcyBJbnRlcnZhbCB0aGF0IGRvZXNuJ3Qgb3ZlcmxhcCB3aXRoIGFueSBvZiB0aGUgc3BlY2lmaWVkIEludGVydmFscy4KICAgICAqIEBwYXJhbSB7Li4uSW50ZXJ2YWx9IGludGVydmFscwogICAgICogQHJldHVybiB7QXJyYXl9CiAgICAgKi8KICAgIGRpZmZlcmVuY2UoLi4uaW50ZXJ2YWxzKSB7CiAgICAgIHJldHVybiBJbnRlcnZhbC54b3IoW3RoaXNdLmNvbmNhdChpbnRlcnZhbHMpKQogICAgICAgIC5tYXAoKGkpID0+IHRoaXMuaW50ZXJzZWN0aW9uKGkpKQogICAgICAgIC5maWx0ZXIoKGkpID0+IGkgJiYgIWkuaXNFbXB0eSgpKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBJbnRlcnZhbCBhcHByb3ByaWF0ZSBmb3IgZGVidWdnaW5nLgogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b1N0cmluZygpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBJTlZBTElEJDE7CiAgICAgIHJldHVybiBgWyR7dGhpcy5zLnRvSVNPKCl9IOKAkyAke3RoaXMuZS50b0lTTygpfSlgOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIGxvY2FsaXplZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoaXMgSW50ZXJ2YWwuIEFjY2VwdHMgdGhlIHNhbWUgb3B0aW9ucyBhcyB0aGUKICAgICAqIEludGwuRGF0ZVRpbWVGb3JtYXQgY29uc3RydWN0b3IgYW5kIGFueSBwcmVzZXRzIGRlZmluZWQgYnkgTHV4b24sIHN1Y2ggYXMKICAgICAqIHtAbGluayBEYXRlVGltZS5EQVRFX0ZVTEx9IG9yIHtAbGluayBEYXRlVGltZS5USU1FX1NJTVBMRX0uIFRoZSBleGFjdCBiZWhhdmlvciBvZiB0aGlzIG1ldGhvZAogICAgICogaXMgYnJvd3Nlci1zcGVjaWZpYywgYnV0IGluIGdlbmVyYWwgaXQgd2lsbCByZXR1cm4gYW4gYXBwcm9wcmlhdGUgcmVwcmVzZW50YXRpb24gb2YgdGhlCiAgICAgKiBJbnRlcnZhbCBpbiB0aGUgYXNzaWduZWQgbG9jYWxlLiBEZWZhdWx0cyB0byB0aGUgc3lzdGVtJ3MgbG9jYWxlIGlmIG5vIGxvY2FsZSBoYXMgYmVlbgogICAgICogc3BlY2lmaWVkLgogICAgICogQHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9HbG9iYWxfT2JqZWN0cy9EYXRlVGltZUZvcm1hdAogICAgICogQHBhcmFtIHtPYmplY3R9IFtmb3JtYXRPcHRzPURhdGVUaW1lLkRBVEVfU0hPUlRdIC0gRWl0aGVyIGEgRGF0ZVRpbWUgcHJlc2V0IG9yCiAgICAgKiBJbnRsLkRhdGVUaW1lRm9ybWF0IGNvbnN0cnVjdG9yIG9wdGlvbnMuCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIE9wdGlvbnMgdG8gb3ZlcnJpZGUgdGhlIGNvbmZpZ3VyYXRpb24gb2YgdGhlIHN0YXJ0IERhdGVUaW1lLgogICAgICogQGV4YW1wbGUgSW50ZXJ2YWwuZnJvbUlTTygnMjAyMi0xMS0wN1QwOTowMFovMjAyMi0xMS0wOFQwOTowMFonKS50b0xvY2FsZVN0cmluZygpOyAvLz0+IDExLzcvMjAyMiDigJMgMTEvOC8yMDIyCiAgICAgKiBAZXhhbXBsZSBJbnRlcnZhbC5mcm9tSVNPKCcyMDIyLTExLTA3VDA5OjAwWi8yMDIyLTExLTA4VDA5OjAwWicpLnRvTG9jYWxlU3RyaW5nKERhdGVUaW1lLkRBVEVfRlVMTCk7IC8vPT4gTm92ZW1iZXIgNyDigJMgOCwgMjAyMgogICAgICogQGV4YW1wbGUgSW50ZXJ2YWwuZnJvbUlTTygnMjAyMi0xMS0wN1QwOTowMFovMjAyMi0xMS0wOFQwOTowMFonKS50b0xvY2FsZVN0cmluZyhEYXRlVGltZS5EQVRFX0ZVTEwsIHsgbG9jYWxlOiAnZnItRlInIH0pOyAvLz0+IDfigJM4IG5vdmVtYnJlIDIwMjIKICAgICAqIEBleGFtcGxlIEludGVydmFsLmZyb21JU08oJzIwMjItMTEtMDdUMTc6MDBaLzIwMjItMTEtMDdUMTk6MDBaJykudG9Mb2NhbGVTdHJpbmcoRGF0ZVRpbWUuVElNRV9TSU1QTEUpOyAvLz0+IDY6MDAg4oCTIDg6MDAgUE0KICAgICAqIEBleGFtcGxlIEludGVydmFsLmZyb21JU08oJzIwMjItMTEtMDdUMTc6MDBaLzIwMjItMTEtMDdUMTk6MDBaJykudG9Mb2NhbGVTdHJpbmcoeyB3ZWVrZGF5OiAnc2hvcnQnLCBtb250aDogJ3Nob3J0JywgZGF5OiAnMi1kaWdpdCcsIGhvdXI6ICcyLWRpZ2l0JywgbWludXRlOiAnMi1kaWdpdCcgfSk7IC8vPT4gTW9uLCBOb3YgMDcsIDY6MDAg4oCTIDg6MDAgcAogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0xvY2FsZVN0cmluZyhmb3JtYXRPcHRzID0gREFURV9TSE9SVCwgb3B0cyA9IHt9KSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQKICAgICAgICA/IEZvcm1hdHRlci5jcmVhdGUodGhpcy5zLmxvYy5jbG9uZShvcHRzKSwgZm9ybWF0T3B0cykuZm9ybWF0SW50ZXJ2YWwodGhpcykKICAgICAgICA6IElOVkFMSUQkMTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYW4gSVNPIDg2MDEtY29tcGxpYW50IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIEludGVydmFsLgogICAgICogQHNlZSBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fODYwMSNUaW1lX2ludGVydmFscwogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBUaGUgc2FtZSBvcHRpb25zIGFzIHtAbGluayBEYXRlVGltZSN0b0lTT30KICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9JU08ob3B0cykgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIElOVkFMSUQkMTsKICAgICAgcmV0dXJuIGAke3RoaXMucy50b0lTTyhvcHRzKX0vJHt0aGlzLmUudG9JU08ob3B0cyl9YDsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYW4gSVNPIDg2MDEtY29tcGxpYW50IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBkYXRlIG9mIHRoaXMgSW50ZXJ2YWwuCiAgICAgKiBUaGUgdGltZSBjb21wb25lbnRzIGFyZSBpZ25vcmVkLgogICAgICogQHNlZSBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fODYwMSNUaW1lX2ludGVydmFscwogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0lTT0RhdGUoKSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gSU5WQUxJRCQxOwogICAgICByZXR1cm4gYCR7dGhpcy5zLnRvSVNPRGF0ZSgpfS8ke3RoaXMuZS50b0lTT0RhdGUoKX1gOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBJU08gODYwMS1jb21wbGlhbnQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRpbWUgb2YgdGhpcyBJbnRlcnZhbC4KICAgICAqIFRoZSBkYXRlIGNvbXBvbmVudHMgYXJlIGlnbm9yZWQuCiAgICAgKiBAc2VlIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT184NjAxI1RpbWVfaW50ZXJ2YWxzCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIFRoZSBzYW1lIG9wdGlvbnMgYXMge0BsaW5rIERhdGVUaW1lI3RvSVNPfQogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0lTT1RpbWUob3B0cykgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIElOVkFMSUQkMTsKICAgICAgcmV0dXJuIGAke3RoaXMucy50b0lTT1RpbWUob3B0cyl9LyR7dGhpcy5lLnRvSVNPVGltZShvcHRzKX1gOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIEludGVydmFsIGZvcm1hdHRlZCBhY2NvcmRpbmcgdG8gdGhlIHNwZWNpZmllZCBmb3JtYXQKICAgICAqIHN0cmluZy4gKipZb3UgbWF5IG5vdCB3YW50IHRoaXMuKiogU2VlIHtAbGluayBJbnRlcnZhbCN0b0xvY2FsZVN0cmluZ30gZm9yIGEgbW9yZSBmbGV4aWJsZQogICAgICogZm9ybWF0dGluZyB0b29sLgogICAgICogQHBhcmFtIHtzdHJpbmd9IGRhdGVGb3JtYXQgLSBUaGUgZm9ybWF0IHN0cmluZy4gVGhpcyBzdHJpbmcgZm9ybWF0cyB0aGUgc3RhcnQgYW5kIGVuZCB0aW1lLgogICAgICogU2VlIHtAbGluayBEYXRlVGltZSN0b0Zvcm1hdH0gZm9yIGRldGFpbHMuCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIE9wdGlvbnMuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuc2VwYXJhdG9yID0gICcg4oCTICddIC0gQSBzZXBhcmF0b3IgdG8gcGxhY2UgYmV0d2VlbiB0aGUgc3RhcnQgYW5kIGVuZAogICAgICogcmVwcmVzZW50YXRpb25zLgogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0Zvcm1hdChkYXRlRm9ybWF0LCB7IHNlcGFyYXRvciA9ICIg4oCTICIgfSA9IHt9KSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gSU5WQUxJRCQxOwogICAgICByZXR1cm4gYCR7dGhpcy5zLnRvRm9ybWF0KGRhdGVGb3JtYXQpfSR7c2VwYXJhdG9yfSR7dGhpcy5lLnRvRm9ybWF0KGRhdGVGb3JtYXQpfWA7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYSBEdXJhdGlvbiByZXByZXNlbnRpbmcgdGhlIHRpbWUgc3Bhbm5lZCBieSB0aGlzIGludGVydmFsLgogICAgICogQHBhcmFtIHtzdHJpbmd8c3RyaW5nW119IFt1bml0PVsnbWlsbGlzZWNvbmRzJ11dIC0gdGhlIHVuaXQgb3IgdW5pdHMgKHN1Y2ggYXMgJ2hvdXJzJyBvciAnZGF5cycpIHRvIGluY2x1ZGUgaW4gdGhlIGR1cmF0aW9uLgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIHRoYXQgYWZmZWN0IHRoZSBjcmVhdGlvbiBvZiB0aGUgRHVyYXRpb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5jb252ZXJzaW9uQWNjdXJhY3k9J2Nhc3VhbCddIC0gdGhlIGNvbnZlcnNpb24gc3lzdGVtIHRvIHVzZQogICAgICogQGV4YW1wbGUgSW50ZXJ2YWwuZnJvbURhdGVUaW1lcyhkdDEsIGR0MikudG9EdXJhdGlvbigpLnRvT2JqZWN0KCkgLy89PiB7IG1pbGxpc2Vjb25kczogODg0ODkyNTcgfQogICAgICogQGV4YW1wbGUgSW50ZXJ2YWwuZnJvbURhdGVUaW1lcyhkdDEsIGR0MikudG9EdXJhdGlvbignZGF5cycpLnRvT2JqZWN0KCkgLy89PiB7IGRheXM6IDEuMDI0MTgxMjE1Mjc3Nzc3OCB9CiAgICAgKiBAZXhhbXBsZSBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKGR0MSwgZHQyKS50b0R1cmF0aW9uKFsnaG91cnMnLCAnbWludXRlcyddKS50b09iamVjdCgpIC8vPT4geyBob3VyczogMjQsIG1pbnV0ZXM6IDM0LjgyMDk1IH0KICAgICAqIEBleGFtcGxlIEludGVydmFsLmZyb21EYXRlVGltZXMoZHQxLCBkdDIpLnRvRHVyYXRpb24oWydob3VycycsICdtaW51dGVzJywgJ3NlY29uZHMnXSkudG9PYmplY3QoKSAvLz0+IHsgaG91cnM6IDI0LCBtaW51dGVzOiAzNCwgc2Vjb25kczogNDkuMjU3IH0KICAgICAqIEBleGFtcGxlIEludGVydmFsLmZyb21EYXRlVGltZXMoZHQxLCBkdDIpLnRvRHVyYXRpb24oJ3NlY29uZHMnKS50b09iamVjdCgpIC8vPT4geyBzZWNvbmRzOiA4ODQ4OS4yNTcgfQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIHRvRHVyYXRpb24odW5pdCwgb3B0cykgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgewogICAgICAgIHJldHVybiBEdXJhdGlvbi5pbnZhbGlkKHRoaXMuaW52YWxpZFJlYXNvbik7CiAgICAgIH0KICAgICAgcmV0dXJuIHRoaXMuZS5kaWZmKHRoaXMucywgdW5pdCwgb3B0cyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSdW4gbWFwRm4gb24gdGhlIGludGVydmFsIHN0YXJ0IGFuZCBlbmQsIHJldHVybmluZyBhIG5ldyBJbnRlcnZhbCBmcm9tIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWVzCiAgICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBtYXBGbgogICAgICogQHJldHVybiB7SW50ZXJ2YWx9CiAgICAgKiBAZXhhbXBsZSBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKGR0MSwgZHQyKS5tYXBFbmRwb2ludHMoZW5kcG9pbnQgPT4gZW5kcG9pbnQudG9VVEMoKSkKICAgICAqIEBleGFtcGxlIEludGVydmFsLmZyb21EYXRlVGltZXMoZHQxLCBkdDIpLm1hcEVuZHBvaW50cyhlbmRwb2ludCA9PiBlbmRwb2ludC5wbHVzKHsgaG91cnM6IDIgfSkpCiAgICAgKi8KICAgIG1hcEVuZHBvaW50cyhtYXBGbikgewogICAgICByZXR1cm4gSW50ZXJ2YWwuZnJvbURhdGVUaW1lcyhtYXBGbih0aGlzLnMpLCBtYXBGbih0aGlzLmUpKTsKICAgIH0KICB9CgogIC8qKgogICAqIFRoZSBJbmZvIGNsYXNzIGNvbnRhaW5zIHN0YXRpYyBtZXRob2RzIGZvciByZXRyaWV2aW5nIGdlbmVyYWwgdGltZSBhbmQgZGF0ZSByZWxhdGVkIGRhdGEuIEZvciBleGFtcGxlLCBpdCBoYXMgbWV0aG9kcyBmb3IgZmluZGluZyBvdXQgaWYgYSB0aW1lIHpvbmUgaGFzIGEgRFNULCBmb3IgbGlzdGluZyB0aGUgbW9udGhzIGluIGFueSBzdXBwb3J0ZWQgbG9jYWxlLCBhbmQgZm9yIGRpc2NvdmVyaW5nIHdoaWNoIG9mIEx1eG9uIGZlYXR1cmVzIGFyZSBhdmFpbGFibGUgaW4gdGhlIGN1cnJlbnQgZW52aXJvbm1lbnQuCiAgICovCiAgY2xhc3MgSW5mbyB7CiAgICAvKioKICAgICAqIFJldHVybiB3aGV0aGVyIHRoZSBzcGVjaWZpZWQgem9uZSBjb250YWlucyBhIERTVC4KICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFt6b25lPSdsb2NhbCddIC0gWm9uZSB0byBjaGVjay4gRGVmYXVsdHMgdG8gdGhlIGVudmlyb25tZW50J3MgbG9jYWwgem9uZS4KICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIHN0YXRpYyBoYXNEU1Qoem9uZSA9IFNldHRpbmdzLmRlZmF1bHRab25lKSB7CiAgICAgIGNvbnN0IHByb3RvID0gRGF0ZVRpbWUubm93KCkuc2V0Wm9uZSh6b25lKS5zZXQoeyBtb250aDogMTIgfSk7CgogICAgICByZXR1cm4gIXpvbmUuaXNVbml2ZXJzYWwgJiYgcHJvdG8ub2Zmc2V0ICE9PSBwcm90by5zZXQoeyBtb250aDogNiB9KS5vZmZzZXQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gd2hldGhlciB0aGUgc3BlY2lmaWVkIHpvbmUgaXMgYSB2YWxpZCBJQU5BIHNwZWNpZmllci4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSB6b25lIC0gWm9uZSB0byBjaGVjawogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgc3RhdGljIGlzVmFsaWRJQU5BWm9uZSh6b25lKSB7CiAgICAgIHJldHVybiBJQU5BWm9uZS5pc1ZhbGlkWm9uZSh6b25lKTsKICAgIH0KCiAgICAvKioKICAgICAqIENvbnZlcnRzIHRoZSBpbnB1dCBpbnRvIGEge0BsaW5rIFpvbmV9IGluc3RhbmNlLgogICAgICoKICAgICAqICogSWYgYGlucHV0YCBpcyBhbHJlYWR5IGEgWm9uZSBpbnN0YW5jZSwgaXQgaXMgcmV0dXJuZWQgdW5jaGFuZ2VkLgogICAgICogKiBJZiBgaW5wdXRgIGlzIGEgc3RyaW5nIGNvbnRhaW5pbmcgYSB2YWxpZCB0aW1lIHpvbmUgbmFtZSwgYSBab25lIGluc3RhbmNlCiAgICAgKiAgIHdpdGggdGhhdCBuYW1lIGlzIHJldHVybmVkLgogICAgICogKiBJZiBgaW5wdXRgIGlzIGEgc3RyaW5nIHRoYXQgZG9lc24ndCByZWZlciB0byBhIGtub3duIHRpbWUgem9uZSwgYSBab25lCiAgICAgKiAgIGluc3RhbmNlIHdpdGgge0BsaW5rIFpvbmUjaXNWYWxpZH0gPT0gZmFsc2UgaXMgcmV0dXJuZWQuCiAgICAgKiAqIElmIGBpbnB1dCBpcyBhIG51bWJlciwgYSBab25lIGluc3RhbmNlIHdpdGggdGhlIHNwZWNpZmllZCBmaXhlZCBvZmZzZXQKICAgICAqICAgaW4gbWludXRlcyBpcyByZXR1cm5lZC4KICAgICAqICogSWYgYGlucHV0YCBpcyBgbnVsbGAgb3IgYHVuZGVmaW5lZGAsIHRoZSBkZWZhdWx0IHpvbmUgaXMgcmV0dXJuZWQuCiAgICAgKiBAcGFyYW0ge3N0cmluZ3xab25lfG51bWJlcn0gW2lucHV0XSAtIHRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQKICAgICAqIEByZXR1cm4ge1pvbmV9CiAgICAgKi8KICAgIHN0YXRpYyBub3JtYWxpemVab25lKGlucHV0KSB7CiAgICAgIHJldHVybiBub3JtYWxpemVab25lKGlucHV0LCBTZXR0aW5ncy5kZWZhdWx0Wm9uZSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gYXJyYXkgb2Ygc3RhbmRhbG9uZSBtb250aCBuYW1lcy4KICAgICAqIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvRGF0ZVRpbWVGb3JtYXQKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbbGVuZ3RoPSdsb25nJ10gLSB0aGUgbGVuZ3RoIG9mIHRoZSBtb250aCByZXByZXNlbnRhdGlvbiwgc3VjaCBhcyAibnVtZXJpYyIsICIyLWRpZ2l0IiwgIm5hcnJvdyIsICJzaG9ydCIsICJsb25nIgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jYWxlXSAtIHRoZSBsb2NhbGUgY29kZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm51bWJlcmluZ1N5c3RlbT1udWxsXSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jT2JqPW51bGxdIC0gYW4gZXhpc3RpbmcgbG9jYWxlIG9iamVjdCB0byB1c2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5vdXRwdXRDYWxlbmRhcj0nZ3JlZ29yeSddIC0gdGhlIGNhbGVuZGFyCiAgICAgKiBAZXhhbXBsZSBJbmZvLm1vbnRocygpWzBdIC8vPT4gJ0phbnVhcnknCiAgICAgKiBAZXhhbXBsZSBJbmZvLm1vbnRocygnc2hvcnQnKVswXSAvLz0+ICdKYW4nCiAgICAgKiBAZXhhbXBsZSBJbmZvLm1vbnRocygnbnVtZXJpYycpWzBdIC8vPT4gJzEnCiAgICAgKiBAZXhhbXBsZSBJbmZvLm1vbnRocygnc2hvcnQnLCB7IGxvY2FsZTogJ2ZyLUNBJyB9IClbMF0gLy89PiAnamFudi4nCiAgICAgKiBAZXhhbXBsZSBJbmZvLm1vbnRocygnbnVtZXJpYycsIHsgbG9jYWxlOiAnYXInIH0pWzBdIC8vPT4gJ9mhJwogICAgICogQGV4YW1wbGUgSW5mby5tb250aHMoJ2xvbmcnLCB7IG91dHB1dENhbGVuZGFyOiAnaXNsYW1pYycgfSlbMF0gLy89PiAnUmFiacq7IEknCiAgICAgKiBAcmV0dXJuIHtBcnJheX0KICAgICAqLwogICAgc3RhdGljIG1vbnRocygKICAgICAgbGVuZ3RoID0gImxvbmciLAogICAgICB7IGxvY2FsZSA9IG51bGwsIG51bWJlcmluZ1N5c3RlbSA9IG51bGwsIGxvY09iaiA9IG51bGwsIG91dHB1dENhbGVuZGFyID0gImdyZWdvcnkiIH0gPSB7fQogICAgKSB7CiAgICAgIHJldHVybiAobG9jT2JqIHx8IExvY2FsZS5jcmVhdGUobG9jYWxlLCBudW1iZXJpbmdTeXN0ZW0sIG91dHB1dENhbGVuZGFyKSkubW9udGhzKGxlbmd0aCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gYXJyYXkgb2YgZm9ybWF0IG1vbnRoIG5hbWVzLgogICAgICogRm9ybWF0IG1vbnRocyBkaWZmZXIgZnJvbSBzdGFuZGFsb25lIG1vbnRocyBpbiB0aGF0IHRoZXkncmUgbWVhbnQgdG8gYXBwZWFyIG5leHQgdG8gdGhlIGRheSBvZiB0aGUgbW9udGguIEluIHNvbWUgbGFuZ3VhZ2VzLCB0aGF0CiAgICAgKiBjaGFuZ2VzIHRoZSBzdHJpbmcuCiAgICAgKiBTZWUge0BsaW5rIEluZm8jbW9udGhzfQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtsZW5ndGg9J2xvbmcnXSAtIHRoZSBsZW5ndGggb2YgdGhlIG1vbnRoIHJlcHJlc2VudGF0aW9uLCBzdWNoIGFzICJudW1lcmljIiwgIjItZGlnaXQiLCAibmFycm93IiwgInNob3J0IiwgImxvbmciCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5sb2NhbGVdIC0gdGhlIGxvY2FsZSBjb2RlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubnVtYmVyaW5nU3lzdGVtPW51bGxdIC0gdGhlIG51bWJlcmluZyBzeXN0ZW0KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5sb2NPYmo9bnVsbF0gLSBhbiBleGlzdGluZyBsb2NhbGUgb2JqZWN0IHRvIHVzZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm91dHB1dENhbGVuZGFyPSdncmVnb3J5J10gLSB0aGUgY2FsZW5kYXIKICAgICAqIEByZXR1cm4ge0FycmF5fQogICAgICovCiAgICBzdGF0aWMgbW9udGhzRm9ybWF0KAogICAgICBsZW5ndGggPSAibG9uZyIsCiAgICAgIHsgbG9jYWxlID0gbnVsbCwgbnVtYmVyaW5nU3lzdGVtID0gbnVsbCwgbG9jT2JqID0gbnVsbCwgb3V0cHV0Q2FsZW5kYXIgPSAiZ3JlZ29yeSIgfSA9IHt9CiAgICApIHsKICAgICAgcmV0dXJuIChsb2NPYmogfHwgTG9jYWxlLmNyZWF0ZShsb2NhbGUsIG51bWJlcmluZ1N5c3RlbSwgb3V0cHV0Q2FsZW5kYXIpKS5tb250aHMobGVuZ3RoLCB0cnVlKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiBhbiBhcnJheSBvZiBzdGFuZGFsb25lIHdlZWsgbmFtZXMuCiAgICAgKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0RhdGVUaW1lRm9ybWF0CiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW2xlbmd0aD0nbG9uZyddIC0gdGhlIGxlbmd0aCBvZiB0aGUgd2Vla2RheSByZXByZXNlbnRhdGlvbiwgc3VjaCBhcyAibmFycm93IiwgInNob3J0IiwgImxvbmciLgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jYWxlXSAtIHRoZSBsb2NhbGUgY29kZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm51bWJlcmluZ1N5c3RlbT1udWxsXSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jT2JqPW51bGxdIC0gYW4gZXhpc3RpbmcgbG9jYWxlIG9iamVjdCB0byB1c2UKICAgICAqIEBleGFtcGxlIEluZm8ud2Vla2RheXMoKVswXSAvLz0+ICdNb25kYXknCiAgICAgKiBAZXhhbXBsZSBJbmZvLndlZWtkYXlzKCdzaG9ydCcpWzBdIC8vPT4gJ01vbicKICAgICAqIEBleGFtcGxlIEluZm8ud2Vla2RheXMoJ3Nob3J0JywgeyBsb2NhbGU6ICdmci1DQScgfSlbMF0gLy89PiAnbHVuLicKICAgICAqIEBleGFtcGxlIEluZm8ud2Vla2RheXMoJ3Nob3J0JywgeyBsb2NhbGU6ICdhcicgfSlbMF0gLy89PiAn2KfZhNin2KvZhtmK2YYnCiAgICAgKiBAcmV0dXJuIHtBcnJheX0KICAgICAqLwogICAgc3RhdGljIHdlZWtkYXlzKGxlbmd0aCA9ICJsb25nIiwgeyBsb2NhbGUgPSBudWxsLCBudW1iZXJpbmdTeXN0ZW0gPSBudWxsLCBsb2NPYmogPSBudWxsIH0gPSB7fSkgewogICAgICByZXR1cm4gKGxvY09iaiB8fCBMb2NhbGUuY3JlYXRlKGxvY2FsZSwgbnVtYmVyaW5nU3lzdGVtLCBudWxsKSkud2Vla2RheXMobGVuZ3RoKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiBhbiBhcnJheSBvZiBmb3JtYXQgd2VlayBuYW1lcy4KICAgICAqIEZvcm1hdCB3ZWVrZGF5cyBkaWZmZXIgZnJvbSBzdGFuZGFsb25lIHdlZWtkYXlzIGluIHRoYXQgdGhleSdyZSBtZWFudCB0byBhcHBlYXIgbmV4dCB0byBtb3JlIGRhdGUgaW5mb3JtYXRpb24uIEluIHNvbWUgbGFuZ3VhZ2VzLCB0aGF0CiAgICAgKiBjaGFuZ2VzIHRoZSBzdHJpbmcuCiAgICAgKiBTZWUge0BsaW5rIEluZm8jd2Vla2RheXN9CiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW2xlbmd0aD0nbG9uZyddIC0gdGhlIGxlbmd0aCBvZiB0aGUgbW9udGggcmVwcmVzZW50YXRpb24sIHN1Y2ggYXMgIm5hcnJvdyIsICJzaG9ydCIsICJsb25nIi4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRzIC0gb3B0aW9ucwogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmxvY2FsZT1udWxsXSAtIHRoZSBsb2NhbGUgY29kZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLm51bWJlcmluZ1N5c3RlbT1udWxsXSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jT2JqPW51bGxdIC0gYW4gZXhpc3RpbmcgbG9jYWxlIG9iamVjdCB0byB1c2UKICAgICAqIEByZXR1cm4ge0FycmF5fQogICAgICovCiAgICBzdGF0aWMgd2Vla2RheXNGb3JtYXQoCiAgICAgIGxlbmd0aCA9ICJsb25nIiwKICAgICAgeyBsb2NhbGUgPSBudWxsLCBudW1iZXJpbmdTeXN0ZW0gPSBudWxsLCBsb2NPYmogPSBudWxsIH0gPSB7fQogICAgKSB7CiAgICAgIHJldHVybiAobG9jT2JqIHx8IExvY2FsZS5jcmVhdGUobG9jYWxlLCBudW1iZXJpbmdTeXN0ZW0sIG51bGwpKS53ZWVrZGF5cyhsZW5ndGgsIHRydWUpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJuIGFuIGFycmF5IG9mIG1lcmlkaWVtcy4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRzIC0gb3B0aW9ucwogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmxvY2FsZV0gLSB0aGUgbG9jYWxlIGNvZGUKICAgICAqIEBleGFtcGxlIEluZm8ubWVyaWRpZW1zKCkgLy89PiBbICdBTScsICdQTScgXQogICAgICogQGV4YW1wbGUgSW5mby5tZXJpZGllbXMoeyBsb2NhbGU6ICdteScgfSkgLy89PiBbICfhgJThgLbhgJThgIDhgLonLCAn4YCK4YCU4YCxJyBdCiAgICAgKiBAcmV0dXJuIHtBcnJheX0KICAgICAqLwogICAgc3RhdGljIG1lcmlkaWVtcyh7IGxvY2FsZSA9IG51bGwgfSA9IHt9KSB7CiAgICAgIHJldHVybiBMb2NhbGUuY3JlYXRlKGxvY2FsZSkubWVyaWRpZW1zKCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gYXJyYXkgb2YgZXJhcywgc3VjaCBhcyBbJ0JDJywgJ0FEJ10uIFRoZSBsb2NhbGUgY2FuIGJlIHNwZWNpZmllZCwgYnV0IHRoZSBjYWxlbmRhciBzeXN0ZW0gaXMgYWx3YXlzIEdyZWdvcmlhbi4KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbbGVuZ3RoPSdzaG9ydCddIC0gdGhlIGxlbmd0aCBvZiB0aGUgZXJhIHJlcHJlc2VudGF0aW9uLCBzdWNoIGFzICJzaG9ydCIgb3IgImxvbmciLgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jYWxlXSAtIHRoZSBsb2NhbGUgY29kZQogICAgICogQGV4YW1wbGUgSW5mby5lcmFzKCkgLy89PiBbICdCQycsICdBRCcgXQogICAgICogQGV4YW1wbGUgSW5mby5lcmFzKCdsb25nJykgLy89PiBbICdCZWZvcmUgQ2hyaXN0JywgJ0Fubm8gRG9taW5pJyBdCiAgICAgKiBAZXhhbXBsZSBJbmZvLmVyYXMoJ2xvbmcnLCB7IGxvY2FsZTogJ2ZyJyB9KSAvLz0+IFsgJ2F2YW50IErDqXN1cy1DaHJpc3QnLCAnYXByw6hzIErDqXN1cy1DaHJpc3QnIF0KICAgICAqIEByZXR1cm4ge0FycmF5fQogICAgICovCiAgICBzdGF0aWMgZXJhcyhsZW5ndGggPSAic2hvcnQiLCB7IGxvY2FsZSA9IG51bGwgfSA9IHt9KSB7CiAgICAgIHJldHVybiBMb2NhbGUuY3JlYXRlKGxvY2FsZSwgbnVsbCwgImdyZWdvcnkiKS5lcmFzKGxlbmd0aCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdGhlIHNldCBvZiBhdmFpbGFibGUgZmVhdHVyZXMgaW4gdGhpcyBlbnZpcm9ubWVudC4KICAgICAqIFNvbWUgZmVhdHVyZXMgb2YgTHV4b24gYXJlIG5vdCBhdmFpbGFibGUgaW4gYWxsIGVudmlyb25tZW50cy4gRm9yIGV4YW1wbGUsIG9uIG9sZGVyIGJyb3dzZXJzLCByZWxhdGl2ZSB0aW1lIGZvcm1hdHRpbmcgc3VwcG9ydCBpcyBub3QgYXZhaWxhYmxlLiBVc2UgdGhpcyBmdW5jdGlvbiB0byBmaWd1cmUgb3V0IGlmIHRoYXQncyB0aGUgY2FzZS4KICAgICAqIEtleXM6CiAgICAgKiAqIGByZWxhdGl2ZWA6IHdoZXRoZXIgdGhpcyBlbnZpcm9ubWVudCBzdXBwb3J0cyByZWxhdGl2ZSB0aW1lIGZvcm1hdHRpbmcKICAgICAqIEBleGFtcGxlIEluZm8uZmVhdHVyZXMoKSAvLz0+IHsgcmVsYXRpdmU6IGZhbHNlIH0KICAgICAqIEByZXR1cm4ge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGZlYXR1cmVzKCkgewogICAgICByZXR1cm4geyByZWxhdGl2ZTogaGFzUmVsYXRpdmUoKSB9OwogICAgfQogIH0KCiAgZnVuY3Rpb24gZGF5RGlmZihlYXJsaWVyLCBsYXRlcikgewogICAgY29uc3QgdXRjRGF5U3RhcnQgPSAoZHQpID0+IGR0LnRvVVRDKDAsIHsga2VlcExvY2FsVGltZTogdHJ1ZSB9KS5zdGFydE9mKCJkYXkiKS52YWx1ZU9mKCksCiAgICAgIG1zID0gdXRjRGF5U3RhcnQobGF0ZXIpIC0gdXRjRGF5U3RhcnQoZWFybGllcik7CiAgICByZXR1cm4gTWF0aC5mbG9vcihEdXJhdGlvbi5mcm9tTWlsbGlzKG1zKS5hcygiZGF5cyIpKTsKICB9CgogIGZ1bmN0aW9uIGhpZ2hPcmRlckRpZmZzKGN1cnNvciwgbGF0ZXIsIHVuaXRzKSB7CiAgICBjb25zdCBkaWZmZXJzID0gWwogICAgICBbInllYXJzIiwgKGEsIGIpID0+IGIueWVhciAtIGEueWVhcl0sCiAgICAgIFsicXVhcnRlcnMiLCAoYSwgYikgPT4gYi5xdWFydGVyIC0gYS5xdWFydGVyICsgKGIueWVhciAtIGEueWVhcikgKiA0XSwKICAgICAgWyJtb250aHMiLCAoYSwgYikgPT4gYi5tb250aCAtIGEubW9udGggKyAoYi55ZWFyIC0gYS55ZWFyKSAqIDEyXSwKICAgICAgWwogICAgICAgICJ3ZWVrcyIsCiAgICAgICAgKGEsIGIpID0+IHsKICAgICAgICAgIGNvbnN0IGRheXMgPSBkYXlEaWZmKGEsIGIpOwogICAgICAgICAgcmV0dXJuIChkYXlzIC0gKGRheXMgJSA3KSkgLyA3OwogICAgICAgIH0sCiAgICAgIF0sCiAgICAgIFsiZGF5cyIsIGRheURpZmZdLAogICAgXTsKCiAgICBjb25zdCByZXN1bHRzID0ge307CiAgICBjb25zdCBlYXJsaWVyID0gY3Vyc29yOwogICAgbGV0IGxvd2VzdE9yZGVyLCBoaWdoV2F0ZXI7CgogICAgLyogVGhpcyBsb29wIHRyaWVzIHRvIGRpZmYgdXNpbmcgbGFyZ2VyIHVuaXRzIGZpcnN0LgogICAgICAgSWYgd2Ugb3ZlcnNob290LCB3ZSBiYWNrdHJhY2sgYW5kIHRyeSB0aGUgbmV4dCBzbWFsbGVyIHVuaXQuCiAgICAgICAiY3Vyc29yIiBzdGFydHMgb3V0IGF0IHRoZSBlYXJsaWVyIHRpbWVzdGFtcCBhbmQgbW92ZXMgY2xvc2VyIGFuZCBjbG9zZXIgdG8gImxhdGVyIgogICAgICAgYXMgd2UgdXNlIHNtYWxsZXIgYW5kIHNtYWxsZXIgdW5pdHMuCiAgICAgICBoaWdoV2F0ZXIga2VlcHMgdHJhY2sgb2Ygd2hlcmUgd2Ugd291bGQgYmUgaWYgd2UgYWRkZWQgb25lIG1vcmUgb2YgdGhlIHNtYWxsZXN0IHVuaXQsCiAgICAgICB0aGlzIGlzIHVzZWQgbGF0ZXIgdG8gcG90ZW50aWFsbHkgY29udmVydCBhbnkgZGlmZmVyZW5jZSBzbWFsbGVyIHRoYW4gdGhlIHNtYWxsZXN0IGhpZ2hlciBvcmRlciB1bml0CiAgICAgICBpbnRvIGEgZnJhY3Rpb24gb2YgdGhhdCBzbWFsbGVzdCBoaWdoZXIgb3JkZXIgdW5pdAogICAgKi8KICAgIGZvciAoY29uc3QgW3VuaXQsIGRpZmZlcl0gb2YgZGlmZmVycykgewogICAgICBpZiAodW5pdHMuaW5kZXhPZih1bml0KSA+PSAwKSB7CiAgICAgICAgbG93ZXN0T3JkZXIgPSB1bml0OwoKICAgICAgICByZXN1bHRzW3VuaXRdID0gZGlmZmVyKGN1cnNvciwgbGF0ZXIpOwogICAgICAgIGhpZ2hXYXRlciA9IGVhcmxpZXIucGx1cyhyZXN1bHRzKTsKCiAgICAgICAgaWYgKGhpZ2hXYXRlciA+IGxhdGVyKSB7CiAgICAgICAgICAvLyB3ZSBvdmVyc2hvdCB0aGUgZW5kIHBvaW50LCBiYWNrdHJhY2sgY3Vyc29yIGJ5IDEKICAgICAgICAgIHJlc3VsdHNbdW5pdF0tLTsKICAgICAgICAgIGN1cnNvciA9IGVhcmxpZXIucGx1cyhyZXN1bHRzKTsKCiAgICAgICAgICAvLyBpZiB3ZSBhcmUgc3RpbGwgb3ZlcnNob290aW5nIG5vdywgd2UgbmVlZCB0byBiYWNrdHJhY2sgYWdhaW4KICAgICAgICAgIC8vIHRoaXMgaGFwcGVucyBpbiBjZXJ0YWluIHNpdHVhdGlvbnMgd2hlbiBkaWZmaW5nIHRpbWVzIGluIGRpZmZlcmVudCB6b25lcywKICAgICAgICAgIC8vIGJlY2F1c2UgdGhpcyBjYWxjdWxhdGlvbiBpZ25vcmVzIHRpbWUgem9uZXMKICAgICAgICAgIGlmIChjdXJzb3IgPiBsYXRlcikgewogICAgICAgICAgICAvLyBrZWVwIHRoZSAib3ZlcnNob3QgYnkgMSIgYXJvdW5kIGFzIGhpZ2hXYXRlcgogICAgICAgICAgICBoaWdoV2F0ZXIgPSBjdXJzb3I7CiAgICAgICAgICAgIC8vIGJhY2t0cmFjayBjdXJzb3IgYnkgMQogICAgICAgICAgICByZXN1bHRzW3VuaXRdLS07CiAgICAgICAgICAgIGN1cnNvciA9IGVhcmxpZXIucGx1cyhyZXN1bHRzKTsKICAgICAgICAgIH0KICAgICAgICB9IGVsc2UgewogICAgICAgICAgY3Vyc29yID0gaGlnaFdhdGVyOwogICAgICAgIH0KICAgICAgfQogICAgfQoKICAgIHJldHVybiBbY3Vyc29yLCByZXN1bHRzLCBoaWdoV2F0ZXIsIGxvd2VzdE9yZGVyXTsKICB9CgogIGZ1bmN0aW9uIGRpZmYgKGVhcmxpZXIsIGxhdGVyLCB1bml0cywgb3B0cykgewogICAgbGV0IFtjdXJzb3IsIHJlc3VsdHMsIGhpZ2hXYXRlciwgbG93ZXN0T3JkZXJdID0gaGlnaE9yZGVyRGlmZnMoZWFybGllciwgbGF0ZXIsIHVuaXRzKTsKCiAgICBjb25zdCByZW1haW5pbmdNaWxsaXMgPSBsYXRlciAtIGN1cnNvcjsKCiAgICBjb25zdCBsb3dlck9yZGVyVW5pdHMgPSB1bml0cy5maWx0ZXIoCiAgICAgICh1KSA9PiBbImhvdXJzIiwgIm1pbnV0ZXMiLCAic2Vjb25kcyIsICJtaWxsaXNlY29uZHMiXS5pbmRleE9mKHUpID49IDAKICAgICk7CgogICAgaWYgKGxvd2VyT3JkZXJVbml0cy5sZW5ndGggPT09IDApIHsKICAgICAgaWYgKGhpZ2hXYXRlciA8IGxhdGVyKSB7CiAgICAgICAgaGlnaFdhdGVyID0gY3Vyc29yLnBsdXMoeyBbbG93ZXN0T3JkZXJdOiAxIH0pOwogICAgICB9CgogICAgICBpZiAoaGlnaFdhdGVyICE9PSBjdXJzb3IpIHsKICAgICAgICByZXN1bHRzW2xvd2VzdE9yZGVyXSA9IChyZXN1bHRzW2xvd2VzdE9yZGVyXSB8fCAwKSArIHJlbWFpbmluZ01pbGxpcyAvIChoaWdoV2F0ZXIgLSBjdXJzb3IpOwogICAgICB9CiAgICB9CgogICAgY29uc3QgZHVyYXRpb24gPSBEdXJhdGlvbi5mcm9tT2JqZWN0KHJlc3VsdHMsIG9wdHMpOwoKICAgIGlmIChsb3dlck9yZGVyVW5pdHMubGVuZ3RoID4gMCkgewogICAgICByZXR1cm4gRHVyYXRpb24uZnJvbU1pbGxpcyhyZW1haW5pbmdNaWxsaXMsIG9wdHMpCiAgICAgICAgLnNoaWZ0VG8oLi4ubG93ZXJPcmRlclVuaXRzKQogICAgICAgIC5wbHVzKGR1cmF0aW9uKTsKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybiBkdXJhdGlvbjsKICAgIH0KICB9CgogIGNvbnN0IG51bWJlcmluZ1N5c3RlbXMgPSB7CiAgICBhcmFiOiAiW1x1MDY2MC1cdTA2NjldIiwKICAgIGFyYWJleHQ6ICJbXHUwNkYwLVx1MDZGOV0iLAogICAgYmFsaTogIltcdTFCNTAtXHUxQjU5XSIsCiAgICBiZW5nOiAiW1x1MDlFNi1cdTA5RUZdIiwKICAgIGRldmE6ICJbXHUwOTY2LVx1MDk2Rl0iLAogICAgZnVsbHdpZGU6ICJbXHVGRjEwLVx1RkYxOV0iLAogICAgZ3VqcjogIltcdTBBRTYtXHUwQUVGXSIsCiAgICBoYW5pZGVjOiAiW+OAh3zkuIB85LqMfOS4iXzlm5t85LqUfOWFrXzkuIN85YWrfOS5nV0iLAogICAga2htcjogIltcdTE3RTAtXHUxN0U5XSIsCiAgICBrbmRhOiAiW1x1MENFNi1cdTBDRUZdIiwKICAgIGxhb286ICJbXHUwRUQwLVx1MEVEOV0iLAogICAgbGltYjogIltcdTE5NDYtXHUxOTRGXSIsCiAgICBtbHltOiAiW1x1MEQ2Ni1cdTBENkZdIiwKICAgIG1vbmc6ICJbXHUxODEwLVx1MTgxOV0iLAogICAgbXltcjogIltcdTEwNDAtXHUxMDQ5XSIsCiAgICBvcnlhOiAiW1x1MEI2Ni1cdTBCNkZdIiwKICAgIHRhbWxkZWM6ICJbXHUwQkU2LVx1MEJFRl0iLAogICAgdGVsdTogIltcdTBDNjYtXHUwQzZGXSIsCiAgICB0aGFpOiAiW1x1MEU1MC1cdTBFNTldIiwKICAgIHRpYnQ6ICJbXHUwRjIwLVx1MEYyOV0iLAogICAgbGF0bjogIlxcZCIsCiAgfTsKCiAgY29uc3QgbnVtYmVyaW5nU3lzdGVtc1VURjE2ID0gewogICAgYXJhYjogWzE2MzIsIDE2NDFdLAogICAgYXJhYmV4dDogWzE3NzYsIDE3ODVdLAogICAgYmFsaTogWzY5OTIsIDcwMDFdLAogICAgYmVuZzogWzI1MzQsIDI1NDNdLAogICAgZGV2YTogWzI0MDYsIDI0MTVdLAogICAgZnVsbHdpZGU6IFs2NTI5NiwgNjUzMDNdLAogICAgZ3VqcjogWzI3OTAsIDI3OTldLAogICAga2htcjogWzYxMTIsIDYxMjFdLAogICAga25kYTogWzMzMDIsIDMzMTFdLAogICAgbGFvbzogWzM3OTIsIDM4MDFdLAogICAgbGltYjogWzY0NzAsIDY0NzldLAogICAgbWx5bTogWzM0MzAsIDM0MzldLAogICAgbW9uZzogWzYxNjAsIDYxNjldLAogICAgbXltcjogWzQxNjAsIDQxNjldLAogICAgb3J5YTogWzI5MTgsIDI5MjddLAogICAgdGFtbGRlYzogWzMwNDYsIDMwNTVdLAogICAgdGVsdTogWzMxNzQsIDMxODNdLAogICAgdGhhaTogWzM2NjQsIDM2NzNdLAogICAgdGlidDogWzM4NzIsIDM4ODFdLAogIH07CgogIGNvbnN0IGhhbmlkZWNDaGFycyA9IG51bWJlcmluZ1N5c3RlbXMuaGFuaWRlYy5yZXBsYWNlKC9bXFt8XF1dL2csICIiKS5zcGxpdCgiIik7CgogIGZ1bmN0aW9uIHBhcnNlRGlnaXRzKHN0cikgewogICAgbGV0IHZhbHVlID0gcGFyc2VJbnQoc3RyLCAxMCk7CiAgICBpZiAoaXNOYU4odmFsdWUpKSB7CiAgICAgIHZhbHVlID0gIiI7CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgY29uc3QgY29kZSA9IHN0ci5jaGFyQ29kZUF0KGkpOwoKICAgICAgICBpZiAoc3RyW2ldLnNlYXJjaChudW1iZXJpbmdTeXN0ZW1zLmhhbmlkZWMpICE9PSAtMSkgewogICAgICAgICAgdmFsdWUgKz0gaGFuaWRlY0NoYXJzLmluZGV4T2Yoc3RyW2ldKTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgZm9yIChjb25zdCBrZXkgaW4gbnVtYmVyaW5nU3lzdGVtc1VURjE2KSB7CiAgICAgICAgICAgIGNvbnN0IFttaW4sIG1heF0gPSBudW1iZXJpbmdTeXN0ZW1zVVRGMTZba2V5XTsKICAgICAgICAgICAgaWYgKGNvZGUgPj0gbWluICYmIGNvZGUgPD0gbWF4KSB7CiAgICAgICAgICAgICAgdmFsdWUgKz0gY29kZSAtIG1pbjsKICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gcGFyc2VJbnQodmFsdWUsIDEwKTsKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybiB2YWx1ZTsKICAgIH0KICB9CgogIGZ1bmN0aW9uIGRpZ2l0UmVnZXgoeyBudW1iZXJpbmdTeXN0ZW0gfSwgYXBwZW5kID0gIiIpIHsKICAgIHJldHVybiBuZXcgUmVnRXhwKGAke251bWJlcmluZ1N5c3RlbXNbbnVtYmVyaW5nU3lzdGVtIHx8ICJsYXRuIl19JHthcHBlbmR9YCk7CiAgfQoKICBjb25zdCBNSVNTSU5HX0ZUUCA9ICJtaXNzaW5nIEludGwuRGF0ZVRpbWVGb3JtYXQuZm9ybWF0VG9QYXJ0cyBzdXBwb3J0IjsKCiAgZnVuY3Rpb24gaW50VW5pdChyZWdleCwgcG9zdCA9IChpKSA9PiBpKSB7CiAgICByZXR1cm4geyByZWdleCwgZGVzZXI6IChbc10pID0+IHBvc3QocGFyc2VEaWdpdHMocykpIH07CiAgfQoKICBjb25zdCBOQlNQID0gU3RyaW5nLmZyb21DaGFyQ29kZSgxNjApOwogIGNvbnN0IHNwYWNlT3JOQlNQID0gYFsgJHtOQlNQfV1gOwogIGNvbnN0IHNwYWNlT3JOQlNQUmVnRXhwID0gbmV3IFJlZ0V4cChzcGFjZU9yTkJTUCwgImciKTsKCiAgZnVuY3Rpb24gZml4TGlzdFJlZ2V4KHMpIHsKICAgIC8vIG1ha2UgZG90cyBvcHRpb25hbCBhbmQgYWxzbyBtYWtlIHRoZW0gbGl0ZXJhbAogICAgLy8gbWFrZSBzcGFjZSBhbmQgbm9uIGJyZWFrYWJsZSBzcGFjZSBjaGFyYWN0ZXJzIGludGVyY2hhbmdlYWJsZQogICAgcmV0dXJuIHMucmVwbGFjZSgvXC4vZywgIlxcLj8iKS5yZXBsYWNlKHNwYWNlT3JOQlNQUmVnRXhwLCBzcGFjZU9yTkJTUCk7CiAgfQoKICBmdW5jdGlvbiBzdHJpcEluc2Vuc2l0aXZpdGllcyhzKSB7CiAgICByZXR1cm4gcwogICAgICAucmVwbGFjZSgvXC4vZywgIiIpIC8vIGlnbm9yZSBkb3RzIHRoYXQgd2VyZSBtYWRlIG9wdGlvbmFsCiAgICAgIC5yZXBsYWNlKHNwYWNlT3JOQlNQUmVnRXhwLCAiICIpIC8vIGludGVyY2hhbmdlIHNwYWNlIGFuZCBuYnNwCiAgICAgIC50b0xvd2VyQ2FzZSgpOwogIH0KCiAgZnVuY3Rpb24gb25lT2Yoc3RyaW5ncywgc3RhcnRJbmRleCkgewogICAgaWYgKHN0cmluZ3MgPT09IG51bGwpIHsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9IGVsc2UgewogICAgICByZXR1cm4gewogICAgICAgIHJlZ2V4OiBSZWdFeHAoc3RyaW5ncy5tYXAoZml4TGlzdFJlZ2V4KS5qb2luKCJ8IikpLAogICAgICAgIGRlc2VyOiAoW3NdKSA9PgogICAgICAgICAgc3RyaW5ncy5maW5kSW5kZXgoKGkpID0+IHN0cmlwSW5zZW5zaXRpdml0aWVzKHMpID09PSBzdHJpcEluc2Vuc2l0aXZpdGllcyhpKSkgKyBzdGFydEluZGV4LAogICAgICB9OwogICAgfQogIH0KCiAgZnVuY3Rpb24gb2Zmc2V0KHJlZ2V4LCBncm91cHMpIHsKICAgIHJldHVybiB7IHJlZ2V4LCBkZXNlcjogKFssIGgsIG1dKSA9PiBzaWduZWRPZmZzZXQoaCwgbSksIGdyb3VwcyB9OwogIH0KCiAgZnVuY3Rpb24gc2ltcGxlKHJlZ2V4KSB7CiAgICByZXR1cm4geyByZWdleCwgZGVzZXI6IChbc10pID0+IHMgfTsKICB9CgogIGZ1bmN0aW9uIGVzY2FwZVRva2VuKHZhbHVlKSB7CiAgICByZXR1cm4gdmFsdWUucmVwbGFjZSgvW1wtXFtcXXt9KCkqKz8uLFxcXF4kfCNcc10vZywgIlxcJCYiKTsKICB9CgogIC8qKgogICAqIEBwYXJhbSB0b2tlbgogICAqIEBwYXJhbSB7TG9jYWxlfSBsb2MKICAgKi8KICBmdW5jdGlvbiB1bml0Rm9yVG9rZW4odG9rZW4sIGxvYykgewogICAgY29uc3Qgb25lID0gZGlnaXRSZWdleChsb2MpLAogICAgICB0d28gPSBkaWdpdFJlZ2V4KGxvYywgInsyfSIpLAogICAgICB0aHJlZSA9IGRpZ2l0UmVnZXgobG9jLCAiezN9IiksCiAgICAgIGZvdXIgPSBkaWdpdFJlZ2V4KGxvYywgIns0fSIpLAogICAgICBzaXggPSBkaWdpdFJlZ2V4KGxvYywgIns2fSIpLAogICAgICBvbmVPclR3byA9IGRpZ2l0UmVnZXgobG9jLCAiezEsMn0iKSwKICAgICAgb25lVG9UaHJlZSA9IGRpZ2l0UmVnZXgobG9jLCAiezEsM30iKSwKICAgICAgb25lVG9TaXggPSBkaWdpdFJlZ2V4KGxvYywgInsxLDZ9IiksCiAgICAgIG9uZVRvTmluZSA9IGRpZ2l0UmVnZXgobG9jLCAiezEsOX0iKSwKICAgICAgdHdvVG9Gb3VyID0gZGlnaXRSZWdleChsb2MsICJ7Miw0fSIpLAogICAgICBmb3VyVG9TaXggPSBkaWdpdFJlZ2V4KGxvYywgIns0LDZ9IiksCiAgICAgIGxpdGVyYWwgPSAodCkgPT4gKHsgcmVnZXg6IFJlZ0V4cChlc2NhcGVUb2tlbih0LnZhbCkpLCBkZXNlcjogKFtzXSkgPT4gcywgbGl0ZXJhbDogdHJ1ZSB9KSwKICAgICAgdW5pdGF0ZSA9ICh0KSA9PiB7CiAgICAgICAgaWYgKHRva2VuLmxpdGVyYWwpIHsKICAgICAgICAgIHJldHVybiBsaXRlcmFsKHQpOwogICAgICAgIH0KICAgICAgICBzd2l0Y2ggKHQudmFsKSB7CiAgICAgICAgICAvLyBlcmEKICAgICAgICAgIGNhc2UgIkciOgogICAgICAgICAgICByZXR1cm4gb25lT2YobG9jLmVyYXMoInNob3J0IiksIDApOwogICAgICAgICAgY2FzZSAiR0ciOgogICAgICAgICAgICByZXR1cm4gb25lT2YobG9jLmVyYXMoImxvbmciKSwgMCk7CiAgICAgICAgICAvLyB5ZWFycwogICAgICAgICAgY2FzZSAieSI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KG9uZVRvU2l4KTsKICAgICAgICAgIGNhc2UgInl5IjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQodHdvVG9Gb3VyLCB1bnRydW5jYXRlWWVhcik7CiAgICAgICAgICBjYXNlICJ5eXl5IjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQoZm91cik7CiAgICAgICAgICBjYXNlICJ5eXl5eSI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KGZvdXJUb1NpeCk7CiAgICAgICAgICBjYXNlICJ5eXl5eXkiOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdChzaXgpOwogICAgICAgICAgLy8gbW9udGhzCiAgICAgICAgICBjYXNlICJNIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQob25lT3JUd28pOwogICAgICAgICAgY2FzZSAiTU0iOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdCh0d28pOwogICAgICAgICAgY2FzZSAiTU1NIjoKICAgICAgICAgICAgcmV0dXJuIG9uZU9mKGxvYy5tb250aHMoInNob3J0IiwgdHJ1ZSksIDEpOwogICAgICAgICAgY2FzZSAiTU1NTSI6CiAgICAgICAgICAgIHJldHVybiBvbmVPZihsb2MubW9udGhzKCJsb25nIiwgdHJ1ZSksIDEpOwogICAgICAgICAgY2FzZSAiTCI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KG9uZU9yVHdvKTsKICAgICAgICAgIGNhc2UgIkxMIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQodHdvKTsKICAgICAgICAgIGNhc2UgIkxMTCI6CiAgICAgICAgICAgIHJldHVybiBvbmVPZihsb2MubW9udGhzKCJzaG9ydCIsIGZhbHNlKSwgMSk7CiAgICAgICAgICBjYXNlICJMTExMIjoKICAgICAgICAgICAgcmV0dXJuIG9uZU9mKGxvYy5tb250aHMoImxvbmciLCBmYWxzZSksIDEpOwogICAgICAgICAgLy8gZGF0ZXMKICAgICAgICAgIGNhc2UgImQiOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdChvbmVPclR3byk7CiAgICAgICAgICBjYXNlICJkZCI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KHR3byk7CiAgICAgICAgICAvLyBvcmRpbmFscwogICAgICAgICAgY2FzZSAibyI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KG9uZVRvVGhyZWUpOwogICAgICAgICAgY2FzZSAib29vIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQodGhyZWUpOwogICAgICAgICAgLy8gdGltZQogICAgICAgICAgY2FzZSAiSEgiOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdCh0d28pOwogICAgICAgICAgY2FzZSAiSCI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KG9uZU9yVHdvKTsKICAgICAgICAgIGNhc2UgImhoIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQodHdvKTsKICAgICAgICAgIGNhc2UgImgiOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdChvbmVPclR3byk7CiAgICAgICAgICBjYXNlICJtbSI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KHR3byk7CiAgICAgICAgICBjYXNlICJtIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQob25lT3JUd28pOwogICAgICAgICAgY2FzZSAicSI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KG9uZU9yVHdvKTsKICAgICAgICAgIGNhc2UgInFxIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQodHdvKTsKICAgICAgICAgIGNhc2UgInMiOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdChvbmVPclR3byk7CiAgICAgICAgICBjYXNlICJzcyI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KHR3byk7CiAgICAgICAgICBjYXNlICJTIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQob25lVG9UaHJlZSk7CiAgICAgICAgICBjYXNlICJTU1MiOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdCh0aHJlZSk7CiAgICAgICAgICBjYXNlICJ1IjoKICAgICAgICAgICAgcmV0dXJuIHNpbXBsZShvbmVUb05pbmUpOwogICAgICAgICAgY2FzZSAidXUiOgogICAgICAgICAgICByZXR1cm4gc2ltcGxlKG9uZU9yVHdvKTsKICAgICAgICAgIGNhc2UgInV1dSI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KG9uZSk7CiAgICAgICAgICAvLyBtZXJpZGllbQogICAgICAgICAgY2FzZSAiYSI6CiAgICAgICAgICAgIHJldHVybiBvbmVPZihsb2MubWVyaWRpZW1zKCksIDApOwogICAgICAgICAgLy8gd2Vla1llYXIgKGspCiAgICAgICAgICBjYXNlICJra2trIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQoZm91cik7CiAgICAgICAgICBjYXNlICJrayI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KHR3b1RvRm91ciwgdW50cnVuY2F0ZVllYXIpOwogICAgICAgICAgLy8gd2Vla051bWJlciAoVykKICAgICAgICAgIGNhc2UgIlciOgogICAgICAgICAgICByZXR1cm4gaW50VW5pdChvbmVPclR3byk7CiAgICAgICAgICBjYXNlICJXVyI6CiAgICAgICAgICAgIHJldHVybiBpbnRVbml0KHR3byk7CiAgICAgICAgICAvLyB3ZWVrZGF5cwogICAgICAgICAgY2FzZSAiRSI6CiAgICAgICAgICBjYXNlICJjIjoKICAgICAgICAgICAgcmV0dXJuIGludFVuaXQob25lKTsKICAgICAgICAgIGNhc2UgIkVFRSI6CiAgICAgICAgICAgIHJldHVybiBvbmVPZihsb2Mud2Vla2RheXMoInNob3J0IiwgZmFsc2UpLCAxKTsKICAgICAgICAgIGNhc2UgIkVFRUUiOgogICAgICAgICAgICByZXR1cm4gb25lT2YobG9jLndlZWtkYXlzKCJsb25nIiwgZmFsc2UpLCAxKTsKICAgICAgICAgIGNhc2UgImNjYyI6CiAgICAgICAgICAgIHJldHVybiBvbmVPZihsb2Mud2Vla2RheXMoInNob3J0IiwgdHJ1ZSksIDEpOwogICAgICAgICAgY2FzZSAiY2NjYyI6CiAgICAgICAgICAgIHJldHVybiBvbmVPZihsb2Mud2Vla2RheXMoImxvbmciLCB0cnVlKSwgMSk7CiAgICAgICAgICAvLyBvZmZzZXQvem9uZQogICAgICAgICAgY2FzZSAiWiI6CiAgICAgICAgICBjYXNlICJaWiI6CiAgICAgICAgICAgIHJldHVybiBvZmZzZXQobmV3IFJlZ0V4cChgKFsrLV0ke29uZU9yVHdvLnNvdXJjZX0pKD86Oigke3R3by5zb3VyY2V9KSk/YCksIDIpOwogICAgICAgICAgY2FzZSAiWlpaIjoKICAgICAgICAgICAgcmV0dXJuIG9mZnNldChuZXcgUmVnRXhwKGAoWystXSR7b25lT3JUd28uc291cmNlfSkoJHt0d28uc291cmNlfSk/YCksIDIpOwogICAgICAgICAgLy8gd2UgZG9uJ3Qgc3VwcG9ydCBaWlpaIChQU1QpIG9yIFpaWlpaIChQYWNpZmljIFN0YW5kYXJkIFRpbWUpIGluIHBhcnNpbmcKICAgICAgICAgIC8vIGJlY2F1c2Ugd2UgZG9uJ3QgaGF2ZSBhbnkgd2F5IHRvIGZpZ3VyZSBvdXQgd2hhdCB0aGV5IGFyZQogICAgICAgICAgY2FzZSAieiI6CiAgICAgICAgICAgIHJldHVybiBzaW1wbGUoL1thLXpfKy0vXXsxLDI1Nn0/L2kpOwogICAgICAgICAgLy8gdGhpcyBzcGVjaWFsLWNhc2UgInRva2VuIiByZXByZXNlbnRzIGEgcGxhY2Ugd2hlcmUgYSBtYWNyby10b2tlbiBleHBhbmRlZCBpbnRvIGEgd2hpdGUtc3BhY2UgbGl0ZXJhbAogICAgICAgICAgLy8gaW4gdGhpcyBjYXNlIHdlIGFjY2VwdCBhbnkgbm9uLW5ld2xpbmUgd2hpdGUtc3BhY2UKICAgICAgICAgIGNhc2UgIiAiOgogICAgICAgICAgICByZXR1cm4gc2ltcGxlKC9bXlxTXG5ccl0vKTsKICAgICAgICAgIGRlZmF1bHQ6CiAgICAgICAgICAgIHJldHVybiBsaXRlcmFsKHQpOwogICAgICAgIH0KICAgICAgfTsKCiAgICBjb25zdCB1bml0ID0gdW5pdGF0ZSh0b2tlbikgfHwgewogICAgICBpbnZhbGlkUmVhc29uOiBNSVNTSU5HX0ZUUCwKICAgIH07CgogICAgdW5pdC50b2tlbiA9IHRva2VuOwoKICAgIHJldHVybiB1bml0OwogIH0KCiAgY29uc3QgcGFydFR5cGVTdHlsZVRvVG9rZW5WYWwgPSB7CiAgICB5ZWFyOiB7CiAgICAgICIyLWRpZ2l0IjogInl5IiwKICAgICAgbnVtZXJpYzogInl5eXl5IiwKICAgIH0sCiAgICBtb250aDogewogICAgICBudW1lcmljOiAiTSIsCiAgICAgICIyLWRpZ2l0IjogIk1NIiwKICAgICAgc2hvcnQ6ICJNTU0iLAogICAgICBsb25nOiAiTU1NTSIsCiAgICB9LAogICAgZGF5OiB7CiAgICAgIG51bWVyaWM6ICJkIiwKICAgICAgIjItZGlnaXQiOiAiZGQiLAogICAgfSwKICAgIHdlZWtkYXk6IHsKICAgICAgc2hvcnQ6ICJFRUUiLAogICAgICBsb25nOiAiRUVFRSIsCiAgICB9LAogICAgZGF5cGVyaW9kOiAiYSIsCiAgICBkYXlQZXJpb2Q6ICJhIiwKICAgIGhvdXIxMjogewogICAgICBudW1lcmljOiAiaCIsCiAgICAgICIyLWRpZ2l0IjogImhoIiwKICAgIH0sCiAgICBob3VyMjQ6IHsKICAgICAgbnVtZXJpYzogIkgiLAogICAgICAiMi1kaWdpdCI6ICJISCIsCiAgICB9LAogICAgbWludXRlOiB7CiAgICAgIG51bWVyaWM6ICJtIiwKICAgICAgIjItZGlnaXQiOiAibW0iLAogICAgfSwKICAgIHNlY29uZDogewogICAgICBudW1lcmljOiAicyIsCiAgICAgICIyLWRpZ2l0IjogInNzIiwKICAgIH0sCiAgICB0aW1lWm9uZU5hbWU6IHsKICAgICAgbG9uZzogIlpaWlpaIiwKICAgICAgc2hvcnQ6ICJaWloiLAogICAgfSwKICB9OwoKICBmdW5jdGlvbiB0b2tlbkZvclBhcnQocGFydCwgZm9ybWF0T3B0cywgcmVzb2x2ZWRPcHRzKSB7CiAgICBjb25zdCB7IHR5cGUsIHZhbHVlIH0gPSBwYXJ0OwoKICAgIGlmICh0eXBlID09PSAibGl0ZXJhbCIpIHsKICAgICAgY29uc3QgaXNTcGFjZSA9IC9eXHMrJC8udGVzdCh2YWx1ZSk7CiAgICAgIHJldHVybiB7CiAgICAgICAgbGl0ZXJhbDogIWlzU3BhY2UsCiAgICAgICAgdmFsOiBpc1NwYWNlID8gIiAiIDogdmFsdWUsCiAgICAgIH07CiAgICB9CgogICAgY29uc3Qgc3R5bGUgPSBmb3JtYXRPcHRzW3R5cGVdOwoKICAgIC8vIFRoZSB1c2VyIG1pZ2h0IGhhdmUgZXhwbGljaXRseSBzcGVjaWZpZWQgaG91cjEyIG9yIGhvdXJDeWNsZQogICAgLy8gaWYgc28sIHJlc3BlY3QgdGhlaXIgZGVjaXNpb24KICAgIC8vIGlmIG5vdCwgcmVmZXIgYmFjayB0byB0aGUgcmVzb2x2ZWRPcHRzLCB3aGljaCBhcmUgYmFzZWQgb24gdGhlIGxvY2FsZQogICAgbGV0IGFjdHVhbFR5cGUgPSB0eXBlOwogICAgaWYgKHR5cGUgPT09ICJob3VyIikgewogICAgICBpZiAoZm9ybWF0T3B0cy5ob3VyMTIgIT0gbnVsbCkgewogICAgICAgIGFjdHVhbFR5cGUgPSBmb3JtYXRPcHRzLmhvdXIxMiA/ICJob3VyMTIiIDogImhvdXIyNCI7CiAgICAgIH0gZWxzZSBpZiAoZm9ybWF0T3B0cy5ob3VyQ3ljbGUgIT0gbnVsbCkgewogICAgICAgIGlmIChmb3JtYXRPcHRzLmhvdXJDeWNsZSA9PT0gImgxMSIgfHwgZm9ybWF0T3B0cy5ob3VyQ3ljbGUgPT09ICJoMTIiKSB7CiAgICAgICAgICBhY3R1YWxUeXBlID0gImhvdXIxMiI7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGFjdHVhbFR5cGUgPSAiaG91cjI0IjsKICAgICAgICB9CiAgICAgIH0gZWxzZSB7CiAgICAgICAgLy8gdG9rZW5zIG9ubHkgZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIDI0IGhvdXJzIG9yIG5vdCwKICAgICAgICAvLyBzbyB3ZSBkbyBub3QgbmVlZCB0byBjaGVjayBob3VyQ3ljbGUgaGVyZSwgd2hpY2ggaXMgbGVzcyBzdXBwb3J0ZWQgYW55d2F5cwogICAgICAgIGFjdHVhbFR5cGUgPSByZXNvbHZlZE9wdHMuaG91cjEyID8gImhvdXIxMiIgOiAiaG91cjI0IjsKICAgICAgfQogICAgfQogICAgbGV0IHZhbCA9IHBhcnRUeXBlU3R5bGVUb1Rva2VuVmFsW2FjdHVhbFR5cGVdOwogICAgaWYgKHR5cGVvZiB2YWwgPT09ICJvYmplY3QiKSB7CiAgICAgIHZhbCA9IHZhbFtzdHlsZV07CiAgICB9CgogICAgaWYgKHZhbCkgewogICAgICByZXR1cm4gewogICAgICAgIGxpdGVyYWw6IGZhbHNlLAogICAgICAgIHZhbCwKICAgICAgfTsKICAgIH0KCiAgICByZXR1cm4gdW5kZWZpbmVkOwogIH0KCiAgZnVuY3Rpb24gYnVpbGRSZWdleCh1bml0cykgewogICAgY29uc3QgcmUgPSB1bml0cy5tYXAoKHUpID0+IHUucmVnZXgpLnJlZHVjZSgoZiwgcikgPT4gYCR7Zn0oJHtyLnNvdXJjZX0pYCwgIiIpOwogICAgcmV0dXJuIFtgXiR7cmV9JGAsIHVuaXRzXTsKICB9CgogIGZ1bmN0aW9uIG1hdGNoKGlucHV0LCByZWdleCwgaGFuZGxlcnMpIHsKICAgIGNvbnN0IG1hdGNoZXMgPSBpbnB1dC5tYXRjaChyZWdleCk7CgogICAgaWYgKG1hdGNoZXMpIHsKICAgICAgY29uc3QgYWxsID0ge307CiAgICAgIGxldCBtYXRjaEluZGV4ID0gMTsKICAgICAgZm9yIChjb25zdCBpIGluIGhhbmRsZXJzKSB7CiAgICAgICAgaWYgKGhhc093blByb3BlcnR5KGhhbmRsZXJzLCBpKSkgewogICAgICAgICAgY29uc3QgaCA9IGhhbmRsZXJzW2ldLAogICAgICAgICAgICBncm91cHMgPSBoLmdyb3VwcyA/IGguZ3JvdXBzICsgMSA6IDE7CiAgICAgICAgICBpZiAoIWgubGl0ZXJhbCAmJiBoLnRva2VuKSB7CiAgICAgICAgICAgIGFsbFtoLnRva2VuLnZhbFswXV0gPSBoLmRlc2VyKG1hdGNoZXMuc2xpY2UobWF0Y2hJbmRleCwgbWF0Y2hJbmRleCArIGdyb3VwcykpOwogICAgICAgICAgfQogICAgICAgICAgbWF0Y2hJbmRleCArPSBncm91cHM7CiAgICAgICAgfQogICAgICB9CiAgICAgIHJldHVybiBbbWF0Y2hlcywgYWxsXTsKICAgIH0gZWxzZSB7CiAgICAgIHJldHVybiBbbWF0Y2hlcywge31dOwogICAgfQogIH0KCiAgZnVuY3Rpb24gZGF0ZVRpbWVGcm9tTWF0Y2hlcyhtYXRjaGVzKSB7CiAgICBjb25zdCB0b0ZpZWxkID0gKHRva2VuKSA9PiB7CiAgICAgIHN3aXRjaCAodG9rZW4pIHsKICAgICAgICBjYXNlICJTIjoKICAgICAgICAgIHJldHVybiAibWlsbGlzZWNvbmQiOwogICAgICAgIGNhc2UgInMiOgogICAgICAgICAgcmV0dXJuICJzZWNvbmQiOwogICAgICAgIGNhc2UgIm0iOgogICAgICAgICAgcmV0dXJuICJtaW51dGUiOwogICAgICAgIGNhc2UgImgiOgogICAgICAgIGNhc2UgIkgiOgogICAgICAgICAgcmV0dXJuICJob3VyIjsKICAgICAgICBjYXNlICJkIjoKICAgICAgICAgIHJldHVybiAiZGF5IjsKICAgICAgICBjYXNlICJvIjoKICAgICAgICAgIHJldHVybiAib3JkaW5hbCI7CiAgICAgICAgY2FzZSAiTCI6CiAgICAgICAgY2FzZSAiTSI6CiAgICAgICAgICByZXR1cm4gIm1vbnRoIjsKICAgICAgICBjYXNlICJ5IjoKICAgICAgICAgIHJldHVybiAieWVhciI7CiAgICAgICAgY2FzZSAiRSI6CiAgICAgICAgY2FzZSAiYyI6CiAgICAgICAgICByZXR1cm4gIndlZWtkYXkiOwogICAgICAgIGNhc2UgIlciOgogICAgICAgICAgcmV0dXJuICJ3ZWVrTnVtYmVyIjsKICAgICAgICBjYXNlICJrIjoKICAgICAgICAgIHJldHVybiAid2Vla1llYXIiOwogICAgICAgIGNhc2UgInEiOgogICAgICAgICAgcmV0dXJuICJxdWFydGVyIjsKICAgICAgICBkZWZhdWx0OgogICAgICAgICAgcmV0dXJuIG51bGw7CiAgICAgIH0KICAgIH07CgogICAgbGV0IHpvbmUgPSBudWxsOwogICAgbGV0IHNwZWNpZmljT2Zmc2V0OwogICAgaWYgKCFpc1VuZGVmaW5lZChtYXRjaGVzLnopKSB7CiAgICAgIHpvbmUgPSBJQU5BWm9uZS5jcmVhdGUobWF0Y2hlcy56KTsKICAgIH0KCiAgICBpZiAoIWlzVW5kZWZpbmVkKG1hdGNoZXMuWikpIHsKICAgICAgaWYgKCF6b25lKSB7CiAgICAgICAgem9uZSA9IG5ldyBGaXhlZE9mZnNldFpvbmUobWF0Y2hlcy5aKTsKICAgICAgfQogICAgICBzcGVjaWZpY09mZnNldCA9IG1hdGNoZXMuWjsKICAgIH0KCiAgICBpZiAoIWlzVW5kZWZpbmVkKG1hdGNoZXMucSkpIHsKICAgICAgbWF0Y2hlcy5NID0gKG1hdGNoZXMucSAtIDEpICogMyArIDE7CiAgICB9CgogICAgaWYgKCFpc1VuZGVmaW5lZChtYXRjaGVzLmgpKSB7CiAgICAgIGlmIChtYXRjaGVzLmggPCAxMiAmJiBtYXRjaGVzLmEgPT09IDEpIHsKICAgICAgICBtYXRjaGVzLmggKz0gMTI7CiAgICAgIH0gZWxzZSBpZiAobWF0Y2hlcy5oID09PSAxMiAmJiBtYXRjaGVzLmEgPT09IDApIHsKICAgICAgICBtYXRjaGVzLmggPSAwOwogICAgICB9CiAgICB9CgogICAgaWYgKG1hdGNoZXMuRyA9PT0gMCAmJiBtYXRjaGVzLnkpIHsKICAgICAgbWF0Y2hlcy55ID0gLW1hdGNoZXMueTsKICAgIH0KCiAgICBpZiAoIWlzVW5kZWZpbmVkKG1hdGNoZXMudSkpIHsKICAgICAgbWF0Y2hlcy5TID0gcGFyc2VNaWxsaXMobWF0Y2hlcy51KTsKICAgIH0KCiAgICBjb25zdCB2YWxzID0gT2JqZWN0LmtleXMobWF0Y2hlcykucmVkdWNlKChyLCBrKSA9PiB7CiAgICAgIGNvbnN0IGYgPSB0b0ZpZWxkKGspOwogICAgICBpZiAoZikgewogICAgICAgIHJbZl0gPSBtYXRjaGVzW2tdOwogICAgICB9CgogICAgICByZXR1cm4gcjsKICAgIH0sIHt9KTsKCiAgICByZXR1cm4gW3ZhbHMsIHpvbmUsIHNwZWNpZmljT2Zmc2V0XTsKICB9CgogIGxldCBkdW1teURhdGVUaW1lQ2FjaGUgPSBudWxsOwoKICBmdW5jdGlvbiBnZXREdW1teURhdGVUaW1lKCkgewogICAgaWYgKCFkdW1teURhdGVUaW1lQ2FjaGUpIHsKICAgICAgZHVtbXlEYXRlVGltZUNhY2hlID0gRGF0ZVRpbWUuZnJvbU1pbGxpcygxNTU1NTU1NTU1NTU1KTsKICAgIH0KCiAgICByZXR1cm4gZHVtbXlEYXRlVGltZUNhY2hlOwogIH0KCiAgZnVuY3Rpb24gbWF5YmVFeHBhbmRNYWNyb1Rva2VuKHRva2VuLCBsb2NhbGUpIHsKICAgIGlmICh0b2tlbi5saXRlcmFsKSB7CiAgICAgIHJldHVybiB0b2tlbjsKICAgIH0KCiAgICBjb25zdCBmb3JtYXRPcHRzID0gRm9ybWF0dGVyLm1hY3JvVG9rZW5Ub0Zvcm1hdE9wdHModG9rZW4udmFsKTsKICAgIGNvbnN0IHRva2VucyA9IGZvcm1hdE9wdHNUb1Rva2Vucyhmb3JtYXRPcHRzLCBsb2NhbGUpOwoKICAgIGlmICh0b2tlbnMgPT0gbnVsbCB8fCB0b2tlbnMuaW5jbHVkZXModW5kZWZpbmVkKSkgewogICAgICByZXR1cm4gdG9rZW47CiAgICB9CgogICAgcmV0dXJuIHRva2VuczsKICB9CgogIGZ1bmN0aW9uIGV4cGFuZE1hY3JvVG9rZW5zKHRva2VucywgbG9jYWxlKSB7CiAgICByZXR1cm4gQXJyYXkucHJvdG90eXBlLmNvbmNhdCguLi50b2tlbnMubWFwKCh0KSA9PiBtYXliZUV4cGFuZE1hY3JvVG9rZW4odCwgbG9jYWxlKSkpOwogIH0KCiAgLyoqCiAgICogQHByaXZhdGUKICAgKi8KCiAgZnVuY3Rpb24gZXhwbGFpbkZyb21Ub2tlbnMobG9jYWxlLCBpbnB1dCwgZm9ybWF0KSB7CiAgICBjb25zdCB0b2tlbnMgPSBleHBhbmRNYWNyb1Rva2VucyhGb3JtYXR0ZXIucGFyc2VGb3JtYXQoZm9ybWF0KSwgbG9jYWxlKSwKICAgICAgdW5pdHMgPSB0b2tlbnMubWFwKCh0KSA9PiB1bml0Rm9yVG9rZW4odCwgbG9jYWxlKSksCiAgICAgIGRpc3F1YWxpZnlpbmdVbml0ID0gdW5pdHMuZmluZCgodCkgPT4gdC5pbnZhbGlkUmVhc29uKTsKCiAgICBpZiAoZGlzcXVhbGlmeWluZ1VuaXQpIHsKICAgICAgcmV0dXJuIHsgaW5wdXQsIHRva2VucywgaW52YWxpZFJlYXNvbjogZGlzcXVhbGlmeWluZ1VuaXQuaW52YWxpZFJlYXNvbiB9OwogICAgfSBlbHNlIHsKICAgICAgY29uc3QgW3JlZ2V4U3RyaW5nLCBoYW5kbGVyc10gPSBidWlsZFJlZ2V4KHVuaXRzKSwKICAgICAgICByZWdleCA9IFJlZ0V4cChyZWdleFN0cmluZywgImkiKSwKICAgICAgICBbcmF3TWF0Y2hlcywgbWF0Y2hlc10gPSBtYXRjaChpbnB1dCwgcmVnZXgsIGhhbmRsZXJzKSwKICAgICAgICBbcmVzdWx0LCB6b25lLCBzcGVjaWZpY09mZnNldF0gPSBtYXRjaGVzCiAgICAgICAgICA/IGRhdGVUaW1lRnJvbU1hdGNoZXMobWF0Y2hlcykKICAgICAgICAgIDogW251bGwsIG51bGwsIHVuZGVmaW5lZF07CiAgICAgIGlmIChoYXNPd25Qcm9wZXJ0eShtYXRjaGVzLCAiYSIpICYmIGhhc093blByb3BlcnR5KG1hdGNoZXMsICJIIikpIHsKICAgICAgICB0aHJvdyBuZXcgQ29uZmxpY3RpbmdTcGVjaWZpY2F0aW9uRXJyb3IoCiAgICAgICAgICAiQ2FuJ3QgaW5jbHVkZSBtZXJpZGllbSB3aGVuIHNwZWNpZnlpbmcgMjQtaG91ciBmb3JtYXQiCiAgICAgICAgKTsKICAgICAgfQogICAgICByZXR1cm4geyBpbnB1dCwgdG9rZW5zLCByZWdleCwgcmF3TWF0Y2hlcywgbWF0Y2hlcywgcmVzdWx0LCB6b25lLCBzcGVjaWZpY09mZnNldCB9OwogICAgfQogIH0KCiAgZnVuY3Rpb24gcGFyc2VGcm9tVG9rZW5zKGxvY2FsZSwgaW5wdXQsIGZvcm1hdCkgewogICAgY29uc3QgeyByZXN1bHQsIHpvbmUsIHNwZWNpZmljT2Zmc2V0LCBpbnZhbGlkUmVhc29uIH0gPSBleHBsYWluRnJvbVRva2Vucyhsb2NhbGUsIGlucHV0LCBmb3JtYXQpOwogICAgcmV0dXJuIFtyZXN1bHQsIHpvbmUsIHNwZWNpZmljT2Zmc2V0LCBpbnZhbGlkUmVhc29uXTsKICB9CgogIGZ1bmN0aW9uIGZvcm1hdE9wdHNUb1Rva2Vucyhmb3JtYXRPcHRzLCBsb2NhbGUpIHsKICAgIGlmICghZm9ybWF0T3B0cykgewogICAgICByZXR1cm4gbnVsbDsKICAgIH0KCiAgICBjb25zdCBmb3JtYXR0ZXIgPSBGb3JtYXR0ZXIuY3JlYXRlKGxvY2FsZSwgZm9ybWF0T3B0cyk7CiAgICBjb25zdCBkZiA9IGZvcm1hdHRlci5kdEZvcm1hdHRlcihnZXREdW1teURhdGVUaW1lKCkpOwogICAgY29uc3QgcGFydHMgPSBkZi5mb3JtYXRUb1BhcnRzKCk7CiAgICBjb25zdCByZXNvbHZlZE9wdHMgPSBkZi5yZXNvbHZlZE9wdGlvbnMoKTsKICAgIHJldHVybiBwYXJ0cy5tYXAoKHApID0+IHRva2VuRm9yUGFydChwLCBmb3JtYXRPcHRzLCByZXNvbHZlZE9wdHMpKTsKICB9CgogIGNvbnN0IG5vbkxlYXBMYWRkZXIgPSBbMCwgMzEsIDU5LCA5MCwgMTIwLCAxNTEsIDE4MSwgMjEyLCAyNDMsIDI3MywgMzA0LCAzMzRdLAogICAgbGVhcExhZGRlciA9IFswLCAzMSwgNjAsIDkxLCAxMjEsIDE1MiwgMTgyLCAyMTMsIDI0NCwgMjc0LCAzMDUsIDMzNV07CgogIGZ1bmN0aW9uIHVuaXRPdXRPZlJhbmdlKHVuaXQsIHZhbHVlKSB7CiAgICByZXR1cm4gbmV3IEludmFsaWQoCiAgICAgICJ1bml0IG91dCBvZiByYW5nZSIsCiAgICAgIGB5b3Ugc3BlY2lmaWVkICR7dmFsdWV9IChvZiB0eXBlICR7dHlwZW9mIHZhbHVlfSkgYXMgYSAke3VuaXR9LCB3aGljaCBpcyBpbnZhbGlkYAogICAgKTsKICB9CgogIGZ1bmN0aW9uIGRheU9mV2Vlayh5ZWFyLCBtb250aCwgZGF5KSB7CiAgICBjb25zdCBkID0gbmV3IERhdGUoRGF0ZS5VVEMoeWVhciwgbW9udGggLSAxLCBkYXkpKTsKCiAgICBpZiAoeWVhciA8IDEwMCAmJiB5ZWFyID49IDApIHsKICAgICAgZC5zZXRVVENGdWxsWWVhcihkLmdldFVUQ0Z1bGxZZWFyKCkgLSAxOTAwKTsKICAgIH0KCiAgICBjb25zdCBqcyA9IGQuZ2V0VVRDRGF5KCk7CgogICAgcmV0dXJuIGpzID09PSAwID8gNyA6IGpzOwogIH0KCiAgZnVuY3Rpb24gY29tcHV0ZU9yZGluYWwoeWVhciwgbW9udGgsIGRheSkgewogICAgcmV0dXJuIGRheSArIChpc0xlYXBZZWFyKHllYXIpID8gbGVhcExhZGRlciA6IG5vbkxlYXBMYWRkZXIpW21vbnRoIC0gMV07CiAgfQoKICBmdW5jdGlvbiB1bmNvbXB1dGVPcmRpbmFsKHllYXIsIG9yZGluYWwpIHsKICAgIGNvbnN0IHRhYmxlID0gaXNMZWFwWWVhcih5ZWFyKSA/IGxlYXBMYWRkZXIgOiBub25MZWFwTGFkZGVyLAogICAgICBtb250aDAgPSB0YWJsZS5maW5kSW5kZXgoKGkpID0+IGkgPCBvcmRpbmFsKSwKICAgICAgZGF5ID0gb3JkaW5hbCAtIHRhYmxlW21vbnRoMF07CiAgICByZXR1cm4geyBtb250aDogbW9udGgwICsgMSwgZGF5IH07CiAgfQoKICAvKioKICAgKiBAcHJpdmF0ZQogICAqLwoKICBmdW5jdGlvbiBncmVnb3JpYW5Ub1dlZWsoZ3JlZ09iaikgewogICAgY29uc3QgeyB5ZWFyLCBtb250aCwgZGF5IH0gPSBncmVnT2JqLAogICAgICBvcmRpbmFsID0gY29tcHV0ZU9yZGluYWwoeWVhciwgbW9udGgsIGRheSksCiAgICAgIHdlZWtkYXkgPSBkYXlPZldlZWsoeWVhciwgbW9udGgsIGRheSk7CgogICAgbGV0IHdlZWtOdW1iZXIgPSBNYXRoLmZsb29yKChvcmRpbmFsIC0gd2Vla2RheSArIDEwKSAvIDcpLAogICAgICB3ZWVrWWVhcjsKCiAgICBpZiAod2Vla051bWJlciA8IDEpIHsKICAgICAgd2Vla1llYXIgPSB5ZWFyIC0gMTsKICAgICAgd2Vla051bWJlciA9IHdlZWtzSW5XZWVrWWVhcih3ZWVrWWVhcik7CiAgICB9IGVsc2UgaWYgKHdlZWtOdW1iZXIgPiB3ZWVrc0luV2Vla1llYXIoeWVhcikpIHsKICAgICAgd2Vla1llYXIgPSB5ZWFyICsgMTsKICAgICAgd2Vla051bWJlciA9IDE7CiAgICB9IGVsc2UgewogICAgICB3ZWVrWWVhciA9IHllYXI7CiAgICB9CgogICAgcmV0dXJuIHsgd2Vla1llYXIsIHdlZWtOdW1iZXIsIHdlZWtkYXksIC4uLnRpbWVPYmplY3QoZ3JlZ09iaikgfTsKICB9CgogIGZ1bmN0aW9uIHdlZWtUb0dyZWdvcmlhbih3ZWVrRGF0YSkgewogICAgY29uc3QgeyB3ZWVrWWVhciwgd2Vla051bWJlciwgd2Vla2RheSB9ID0gd2Vla0RhdGEsCiAgICAgIHdlZWtkYXlPZkphbjQgPSBkYXlPZldlZWsod2Vla1llYXIsIDEsIDQpLAogICAgICB5ZWFySW5EYXlzID0gZGF5c0luWWVhcih3ZWVrWWVhcik7CgogICAgbGV0IG9yZGluYWwgPSB3ZWVrTnVtYmVyICogNyArIHdlZWtkYXkgLSB3ZWVrZGF5T2ZKYW40IC0gMywKICAgICAgeWVhcjsKCiAgICBpZiAob3JkaW5hbCA8IDEpIHsKICAgICAgeWVhciA9IHdlZWtZZWFyIC0gMTsKICAgICAgb3JkaW5hbCArPSBkYXlzSW5ZZWFyKHllYXIpOwogICAgfSBlbHNlIGlmIChvcmRpbmFsID4geWVhckluRGF5cykgewogICAgICB5ZWFyID0gd2Vla1llYXIgKyAxOwogICAgICBvcmRpbmFsIC09IGRheXNJblllYXIod2Vla1llYXIpOwogICAgfSBlbHNlIHsKICAgICAgeWVhciA9IHdlZWtZZWFyOwogICAgfQoKICAgIGNvbnN0IHsgbW9udGgsIGRheSB9ID0gdW5jb21wdXRlT3JkaW5hbCh5ZWFyLCBvcmRpbmFsKTsKICAgIHJldHVybiB7IHllYXIsIG1vbnRoLCBkYXksIC4uLnRpbWVPYmplY3Qod2Vla0RhdGEpIH07CiAgfQoKICBmdW5jdGlvbiBncmVnb3JpYW5Ub09yZGluYWwoZ3JlZ0RhdGEpIHsKICAgIGNvbnN0IHsgeWVhciwgbW9udGgsIGRheSB9ID0gZ3JlZ0RhdGE7CiAgICBjb25zdCBvcmRpbmFsID0gY29tcHV0ZU9yZGluYWwoeWVhciwgbW9udGgsIGRheSk7CiAgICByZXR1cm4geyB5ZWFyLCBvcmRpbmFsLCAuLi50aW1lT2JqZWN0KGdyZWdEYXRhKSB9OwogIH0KCiAgZnVuY3Rpb24gb3JkaW5hbFRvR3JlZ29yaWFuKG9yZGluYWxEYXRhKSB7CiAgICBjb25zdCB7IHllYXIsIG9yZGluYWwgfSA9IG9yZGluYWxEYXRhOwogICAgY29uc3QgeyBtb250aCwgZGF5IH0gPSB1bmNvbXB1dGVPcmRpbmFsKHllYXIsIG9yZGluYWwpOwogICAgcmV0dXJuIHsgeWVhciwgbW9udGgsIGRheSwgLi4udGltZU9iamVjdChvcmRpbmFsRGF0YSkgfTsKICB9CgogIGZ1bmN0aW9uIGhhc0ludmFsaWRXZWVrRGF0YShvYmopIHsKICAgIGNvbnN0IHZhbGlkWWVhciA9IGlzSW50ZWdlcihvYmoud2Vla1llYXIpLAogICAgICB2YWxpZFdlZWsgPSBpbnRlZ2VyQmV0d2VlbihvYmoud2Vla051bWJlciwgMSwgd2Vla3NJbldlZWtZZWFyKG9iai53ZWVrWWVhcikpLAogICAgICB2YWxpZFdlZWtkYXkgPSBpbnRlZ2VyQmV0d2VlbihvYmoud2Vla2RheSwgMSwgNyk7CgogICAgaWYgKCF2YWxpZFllYXIpIHsKICAgICAgcmV0dXJuIHVuaXRPdXRPZlJhbmdlKCJ3ZWVrWWVhciIsIG9iai53ZWVrWWVhcik7CiAgICB9IGVsc2UgaWYgKCF2YWxpZFdlZWspIHsKICAgICAgcmV0dXJuIHVuaXRPdXRPZlJhbmdlKCJ3ZWVrIiwgb2JqLndlZWspOwogICAgfSBlbHNlIGlmICghdmFsaWRXZWVrZGF5KSB7CiAgICAgIHJldHVybiB1bml0T3V0T2ZSYW5nZSgid2Vla2RheSIsIG9iai53ZWVrZGF5KTsKICAgIH0gZWxzZSByZXR1cm4gZmFsc2U7CiAgfQoKICBmdW5jdGlvbiBoYXNJbnZhbGlkT3JkaW5hbERhdGEob2JqKSB7CiAgICBjb25zdCB2YWxpZFllYXIgPSBpc0ludGVnZXIob2JqLnllYXIpLAogICAgICB2YWxpZE9yZGluYWwgPSBpbnRlZ2VyQmV0d2VlbihvYmoub3JkaW5hbCwgMSwgZGF5c0luWWVhcihvYmoueWVhcikpOwoKICAgIGlmICghdmFsaWRZZWFyKSB7CiAgICAgIHJldHVybiB1bml0T3V0T2ZSYW5nZSgieWVhciIsIG9iai55ZWFyKTsKICAgIH0gZWxzZSBpZiAoIXZhbGlkT3JkaW5hbCkgewogICAgICByZXR1cm4gdW5pdE91dE9mUmFuZ2UoIm9yZGluYWwiLCBvYmoub3JkaW5hbCk7CiAgICB9IGVsc2UgcmV0dXJuIGZhbHNlOwogIH0KCiAgZnVuY3Rpb24gaGFzSW52YWxpZEdyZWdvcmlhbkRhdGEob2JqKSB7CiAgICBjb25zdCB2YWxpZFllYXIgPSBpc0ludGVnZXIob2JqLnllYXIpLAogICAgICB2YWxpZE1vbnRoID0gaW50ZWdlckJldHdlZW4ob2JqLm1vbnRoLCAxLCAxMiksCiAgICAgIHZhbGlkRGF5ID0gaW50ZWdlckJldHdlZW4ob2JqLmRheSwgMSwgZGF5c0luTW9udGgob2JqLnllYXIsIG9iai5tb250aCkpOwoKICAgIGlmICghdmFsaWRZZWFyKSB7CiAgICAgIHJldHVybiB1bml0T3V0T2ZSYW5nZSgieWVhciIsIG9iai55ZWFyKTsKICAgIH0gZWxzZSBpZiAoIXZhbGlkTW9udGgpIHsKICAgICAgcmV0dXJuIHVuaXRPdXRPZlJhbmdlKCJtb250aCIsIG9iai5tb250aCk7CiAgICB9IGVsc2UgaWYgKCF2YWxpZERheSkgewogICAgICByZXR1cm4gdW5pdE91dE9mUmFuZ2UoImRheSIsIG9iai5kYXkpOwogICAgfSBlbHNlIHJldHVybiBmYWxzZTsKICB9CgogIGZ1bmN0aW9uIGhhc0ludmFsaWRUaW1lRGF0YShvYmopIHsKICAgIGNvbnN0IHsgaG91ciwgbWludXRlLCBzZWNvbmQsIG1pbGxpc2Vjb25kIH0gPSBvYmo7CiAgICBjb25zdCB2YWxpZEhvdXIgPQogICAgICAgIGludGVnZXJCZXR3ZWVuKGhvdXIsIDAsIDIzKSB8fAogICAgICAgIChob3VyID09PSAyNCAmJiBtaW51dGUgPT09IDAgJiYgc2Vjb25kID09PSAwICYmIG1pbGxpc2Vjb25kID09PSAwKSwKICAgICAgdmFsaWRNaW51dGUgPSBpbnRlZ2VyQmV0d2VlbihtaW51dGUsIDAsIDU5KSwKICAgICAgdmFsaWRTZWNvbmQgPSBpbnRlZ2VyQmV0d2VlbihzZWNvbmQsIDAsIDU5KSwKICAgICAgdmFsaWRNaWxsaXNlY29uZCA9IGludGVnZXJCZXR3ZWVuKG1pbGxpc2Vjb25kLCAwLCA5OTkpOwoKICAgIGlmICghdmFsaWRIb3VyKSB7CiAgICAgIHJldHVybiB1bml0T3V0T2ZSYW5nZSgiaG91ciIsIGhvdXIpOwogICAgfSBlbHNlIGlmICghdmFsaWRNaW51dGUpIHsKICAgICAgcmV0dXJuIHVuaXRPdXRPZlJhbmdlKCJtaW51dGUiLCBtaW51dGUpOwogICAgfSBlbHNlIGlmICghdmFsaWRTZWNvbmQpIHsKICAgICAgcmV0dXJuIHVuaXRPdXRPZlJhbmdlKCJzZWNvbmQiLCBzZWNvbmQpOwogICAgfSBlbHNlIGlmICghdmFsaWRNaWxsaXNlY29uZCkgewogICAgICByZXR1cm4gdW5pdE91dE9mUmFuZ2UoIm1pbGxpc2Vjb25kIiwgbWlsbGlzZWNvbmQpOwogICAgfSBlbHNlIHJldHVybiBmYWxzZTsKICB9CgogIGNvbnN0IElOVkFMSUQgPSAiSW52YWxpZCBEYXRlVGltZSI7CiAgY29uc3QgTUFYX0RBVEUgPSA4LjY0ZTE1OwoKICBmdW5jdGlvbiB1bnN1cHBvcnRlZFpvbmUoem9uZSkgewogICAgcmV0dXJuIG5ldyBJbnZhbGlkKCJ1bnN1cHBvcnRlZCB6b25lIiwgYHRoZSB6b25lICIke3pvbmUubmFtZX0iIGlzIG5vdCBzdXBwb3J0ZWRgKTsKICB9CgogIC8vIHdlIGNhY2hlIHdlZWsgZGF0YSBvbiB0aGUgRFQgb2JqZWN0IGFuZCB0aGlzIGludGVybWVkaWF0ZXMgdGhlIGNhY2hlCiAgZnVuY3Rpb24gcG9zc2libHlDYWNoZWRXZWVrRGF0YShkdCkgewogICAgaWYgKGR0LndlZWtEYXRhID09PSBudWxsKSB7CiAgICAgIGR0LndlZWtEYXRhID0gZ3JlZ29yaWFuVG9XZWVrKGR0LmMpOwogICAgfQogICAgcmV0dXJuIGR0LndlZWtEYXRhOwogIH0KCiAgLy8gY2xvbmUgcmVhbGx5IG1lYW5zLCAibWFrZSBhIG5ldyBvYmplY3Qgd2l0aCB0aGVzZSBtb2RpZmljYXRpb25zIi4gYWxsICJzZXR0ZXJzIiByZWFsbHkgdXNlIHRoaXMKICAvLyB0byBjcmVhdGUgYSBuZXcgb2JqZWN0IHdoaWxlIG9ubHkgY2hhbmdpbmcgc29tZSBvZiB0aGUgcHJvcGVydGllcwogIGZ1bmN0aW9uIGNsb25lKGluc3QsIGFsdHMpIHsKICAgIGNvbnN0IGN1cnJlbnQgPSB7CiAgICAgIHRzOiBpbnN0LnRzLAogICAgICB6b25lOiBpbnN0LnpvbmUsCiAgICAgIGM6IGluc3QuYywKICAgICAgbzogaW5zdC5vLAogICAgICBsb2M6IGluc3QubG9jLAogICAgICBpbnZhbGlkOiBpbnN0LmludmFsaWQsCiAgICB9OwogICAgcmV0dXJuIG5ldyBEYXRlVGltZSh7IC4uLmN1cnJlbnQsIC4uLmFsdHMsIG9sZDogY3VycmVudCB9KTsKICB9CgogIC8vIGZpbmQgdGhlIHJpZ2h0IG9mZnNldCBhIGdpdmVuIGxvY2FsIHRpbWUuIFRoZSBvIGlucHV0IGlzIG91ciBndWVzcywgd2hpY2ggZGV0ZXJtaW5lcyB3aGljaAogIC8vIG9mZnNldCB3ZSdsbCBwaWNrIGluIGFtYmlndW91cyBjYXNlcyAoZS5nLiB0aGVyZSBhcmUgdHdvIDMgQU1zIGIvYyBGYWxsYmFjayBEU1QpCiAgZnVuY3Rpb24gZml4T2Zmc2V0KGxvY2FsVFMsIG8sIHR6KSB7CiAgICAvLyBPdXIgVVRDIHRpbWUgaXMganVzdCBhIGd1ZXNzIGJlY2F1c2Ugb3VyIG9mZnNldCBpcyBqdXN0IGEgZ3Vlc3MKICAgIGxldCB1dGNHdWVzcyA9IGxvY2FsVFMgLSBvICogNjAgKiAxMDAwOwoKICAgIC8vIFRlc3Qgd2hldGhlciB0aGUgem9uZSBtYXRjaGVzIHRoZSBvZmZzZXQgZm9yIHRoaXMgdHMKICAgIGNvbnN0IG8yID0gdHoub2Zmc2V0KHV0Y0d1ZXNzKTsKCiAgICAvLyBJZiBzbywgb2Zmc2V0IGRpZG4ndCBjaGFuZ2UgYW5kIHdlJ3JlIGRvbmUKICAgIGlmIChvID09PSBvMikgewogICAgICByZXR1cm4gW3V0Y0d1ZXNzLCBvXTsKICAgIH0KCiAgICAvLyBJZiBub3QsIGNoYW5nZSB0aGUgdHMgYnkgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIG9mZnNldAogICAgdXRjR3Vlc3MgLT0gKG8yIC0gbykgKiA2MCAqIDEwMDA7CgogICAgLy8gSWYgdGhhdCBnaXZlcyB1cyB0aGUgbG9jYWwgdGltZSB3ZSB3YW50LCB3ZSdyZSBkb25lCiAgICBjb25zdCBvMyA9IHR6Lm9mZnNldCh1dGNHdWVzcyk7CiAgICBpZiAobzIgPT09IG8zKSB7CiAgICAgIHJldHVybiBbdXRjR3Vlc3MsIG8yXTsKICAgIH0KCiAgICAvLyBJZiBpdCdzIGRpZmZlcmVudCwgd2UncmUgaW4gYSBob2xlIHRpbWUuIFRoZSBvZmZzZXQgaGFzIGNoYW5nZWQsIGJ1dCB0aGUgd2UgZG9uJ3QgYWRqdXN0IHRoZSB0aW1lCiAgICByZXR1cm4gW2xvY2FsVFMgLSBNYXRoLm1pbihvMiwgbzMpICogNjAgKiAxMDAwLCBNYXRoLm1heChvMiwgbzMpXTsKICB9CgogIC8vIGNvbnZlcnQgYW4gZXBvY2ggdGltZXN0YW1wIGludG8gYSBjYWxlbmRhciBvYmplY3Qgd2l0aCB0aGUgZ2l2ZW4gb2Zmc2V0CiAgZnVuY3Rpb24gdHNUb09iaih0cywgb2Zmc2V0KSB7CiAgICB0cyArPSBvZmZzZXQgKiA2MCAqIDEwMDA7CgogICAgY29uc3QgZCA9IG5ldyBEYXRlKHRzKTsKCiAgICByZXR1cm4gewogICAgICB5ZWFyOiBkLmdldFVUQ0Z1bGxZZWFyKCksCiAgICAgIG1vbnRoOiBkLmdldFVUQ01vbnRoKCkgKyAxLAogICAgICBkYXk6IGQuZ2V0VVRDRGF0ZSgpLAogICAgICBob3VyOiBkLmdldFVUQ0hvdXJzKCksCiAgICAgIG1pbnV0ZTogZC5nZXRVVENNaW51dGVzKCksCiAgICAgIHNlY29uZDogZC5nZXRVVENTZWNvbmRzKCksCiAgICAgIG1pbGxpc2Vjb25kOiBkLmdldFVUQ01pbGxpc2Vjb25kcygpLAogICAgfTsKICB9CgogIC8vIGNvbnZlcnQgYSBjYWxlbmRhciBvYmplY3QgdG8gYSBlcG9jaCB0aW1lc3RhbXAKICBmdW5jdGlvbiBvYmpUb1RTKG9iaiwgb2Zmc2V0LCB6b25lKSB7CiAgICByZXR1cm4gZml4T2Zmc2V0KG9ialRvTG9jYWxUUyhvYmopLCBvZmZzZXQsIHpvbmUpOwogIH0KCiAgLy8gY3JlYXRlIGEgbmV3IERUIGluc3RhbmNlIGJ5IGFkZGluZyBhIGR1cmF0aW9uLCBhZGp1c3RpbmcgZm9yIERTVHMKICBmdW5jdGlvbiBhZGp1c3RUaW1lKGluc3QsIGR1cikgewogICAgY29uc3Qgb1ByZSA9IGluc3QubywKICAgICAgeWVhciA9IGluc3QuYy55ZWFyICsgTWF0aC50cnVuYyhkdXIueWVhcnMpLAogICAgICBtb250aCA9IGluc3QuYy5tb250aCArIE1hdGgudHJ1bmMoZHVyLm1vbnRocykgKyBNYXRoLnRydW5jKGR1ci5xdWFydGVycykgKiAzLAogICAgICBjID0gewogICAgICAgIC4uLmluc3QuYywKICAgICAgICB5ZWFyLAogICAgICAgIG1vbnRoLAogICAgICAgIGRheToKICAgICAgICAgIE1hdGgubWluKGluc3QuYy5kYXksIGRheXNJbk1vbnRoKHllYXIsIG1vbnRoKSkgKwogICAgICAgICAgTWF0aC50cnVuYyhkdXIuZGF5cykgKwogICAgICAgICAgTWF0aC50cnVuYyhkdXIud2Vla3MpICogNywKICAgICAgfSwKICAgICAgbWlsbGlzVG9BZGQgPSBEdXJhdGlvbi5mcm9tT2JqZWN0KHsKICAgICAgICB5ZWFyczogZHVyLnllYXJzIC0gTWF0aC50cnVuYyhkdXIueWVhcnMpLAogICAgICAgIHF1YXJ0ZXJzOiBkdXIucXVhcnRlcnMgLSBNYXRoLnRydW5jKGR1ci5xdWFydGVycyksCiAgICAgICAgbW9udGhzOiBkdXIubW9udGhzIC0gTWF0aC50cnVuYyhkdXIubW9udGhzKSwKICAgICAgICB3ZWVrczogZHVyLndlZWtzIC0gTWF0aC50cnVuYyhkdXIud2Vla3MpLAogICAgICAgIGRheXM6IGR1ci5kYXlzIC0gTWF0aC50cnVuYyhkdXIuZGF5cyksCiAgICAgICAgaG91cnM6IGR1ci5ob3VycywKICAgICAgICBtaW51dGVzOiBkdXIubWludXRlcywKICAgICAgICBzZWNvbmRzOiBkdXIuc2Vjb25kcywKICAgICAgICBtaWxsaXNlY29uZHM6IGR1ci5taWxsaXNlY29uZHMsCiAgICAgIH0pLmFzKCJtaWxsaXNlY29uZHMiKSwKICAgICAgbG9jYWxUUyA9IG9ialRvTG9jYWxUUyhjKTsKCiAgICBsZXQgW3RzLCBvXSA9IGZpeE9mZnNldChsb2NhbFRTLCBvUHJlLCBpbnN0LnpvbmUpOwoKICAgIGlmIChtaWxsaXNUb0FkZCAhPT0gMCkgewogICAgICB0cyArPSBtaWxsaXNUb0FkZDsKICAgICAgLy8gdGhhdCBjb3VsZCBoYXZlIGNoYW5nZWQgdGhlIG9mZnNldCBieSBnb2luZyBvdmVyIGEgRFNULCBidXQgd2Ugd2FudCB0byBrZWVwIHRoZSB0cyB0aGUgc2FtZQogICAgICBvID0gaW5zdC56b25lLm9mZnNldCh0cyk7CiAgICB9CgogICAgcmV0dXJuIHsgdHMsIG8gfTsKICB9CgogIC8vIGhlbHBlciB1c2VmdWwgaW4gdHVybmluZyB0aGUgcmVzdWx0cyBvZiBwYXJzaW5nIGludG8gcmVhbCBkYXRlcwogIC8vIGJ5IGhhbmRsaW5nIHRoZSB6b25lIG9wdGlvbnMKICBmdW5jdGlvbiBwYXJzZURhdGFUb0RhdGVUaW1lKHBhcnNlZCwgcGFyc2VkWm9uZSwgb3B0cywgZm9ybWF0LCB0ZXh0LCBzcGVjaWZpY09mZnNldCkgewogICAgY29uc3QgeyBzZXRab25lLCB6b25lIH0gPSBvcHRzOwogICAgaWYgKChwYXJzZWQgJiYgT2JqZWN0LmtleXMocGFyc2VkKS5sZW5ndGggIT09IDApIHx8IHBhcnNlZFpvbmUpIHsKICAgICAgY29uc3QgaW50ZXJwcmV0YXRpb25ab25lID0gcGFyc2VkWm9uZSB8fCB6b25lLAogICAgICAgIGluc3QgPSBEYXRlVGltZS5mcm9tT2JqZWN0KHBhcnNlZCwgewogICAgICAgICAgLi4ub3B0cywKICAgICAgICAgIHpvbmU6IGludGVycHJldGF0aW9uWm9uZSwKICAgICAgICAgIHNwZWNpZmljT2Zmc2V0LAogICAgICAgIH0pOwogICAgICByZXR1cm4gc2V0Wm9uZSA/IGluc3QgOiBpbnN0LnNldFpvbmUoem9uZSk7CiAgICB9IGVsc2UgewogICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZCgKICAgICAgICBuZXcgSW52YWxpZCgidW5wYXJzYWJsZSIsIGB0aGUgaW5wdXQgIiR7dGV4dH0iIGNhbid0IGJlIHBhcnNlZCBhcyAke2Zvcm1hdH1gKQogICAgICApOwogICAgfQogIH0KCiAgLy8gaWYgeW91IHdhbnQgdG8gb3V0cHV0IGEgdGVjaG5pY2FsIGZvcm1hdCAoZS5nLiBSRkMgMjgyMiksIHRoaXMgaGVscGVyCiAgLy8gaGVscHMgaGFuZGxlIHRoZSBkZXRhaWxzCiAgZnVuY3Rpb24gdG9UZWNoRm9ybWF0KGR0LCBmb3JtYXQsIGFsbG93WiA9IHRydWUpIHsKICAgIHJldHVybiBkdC5pc1ZhbGlkCiAgICAgID8gRm9ybWF0dGVyLmNyZWF0ZShMb2NhbGUuY3JlYXRlKCJlbi1VUyIpLCB7CiAgICAgICAgICBhbGxvd1osCiAgICAgICAgICBmb3JjZVNpbXBsZTogdHJ1ZSwKICAgICAgICB9KS5mb3JtYXREYXRlVGltZUZyb21TdHJpbmcoZHQsIGZvcm1hdCkKICAgICAgOiBudWxsOwogIH0KCiAgZnVuY3Rpb24gdG9JU09EYXRlKG8sIGV4dGVuZGVkKSB7CiAgICBjb25zdCBsb25nRm9ybWF0ID0gby5jLnllYXIgPiA5OTk5IHx8IG8uYy55ZWFyIDwgMDsKICAgIGxldCBjID0gIiI7CiAgICBpZiAobG9uZ0Zvcm1hdCAmJiBvLmMueWVhciA+PSAwKSBjICs9ICIrIjsKICAgIGMgKz0gcGFkU3RhcnQoby5jLnllYXIsIGxvbmdGb3JtYXQgPyA2IDogNCk7CgogICAgaWYgKGV4dGVuZGVkKSB7CiAgICAgIGMgKz0gIi0iOwogICAgICBjICs9IHBhZFN0YXJ0KG8uYy5tb250aCk7CiAgICAgIGMgKz0gIi0iOwogICAgICBjICs9IHBhZFN0YXJ0KG8uYy5kYXkpOwogICAgfSBlbHNlIHsKICAgICAgYyArPSBwYWRTdGFydChvLmMubW9udGgpOwogICAgICBjICs9IHBhZFN0YXJ0KG8uYy5kYXkpOwogICAgfQogICAgcmV0dXJuIGM7CiAgfQoKICBmdW5jdGlvbiB0b0lTT1RpbWUoCiAgICBvLAogICAgZXh0ZW5kZWQsCiAgICBzdXBwcmVzc1NlY29uZHMsCiAgICBzdXBwcmVzc01pbGxpc2Vjb25kcywKICAgIGluY2x1ZGVPZmZzZXQsCiAgICBleHRlbmRlZFpvbmUKICApIHsKICAgIGxldCBjID0gcGFkU3RhcnQoby5jLmhvdXIpOwogICAgaWYgKGV4dGVuZGVkKSB7CiAgICAgIGMgKz0gIjoiOwogICAgICBjICs9IHBhZFN0YXJ0KG8uYy5taW51dGUpOwogICAgICBpZiAoby5jLm1pbGxpc2Vjb25kICE9PSAwIHx8IG8uYy5zZWNvbmQgIT09IDAgfHwgIXN1cHByZXNzU2Vjb25kcykgewogICAgICAgIGMgKz0gIjoiOwogICAgICB9CiAgICB9IGVsc2UgewogICAgICBjICs9IHBhZFN0YXJ0KG8uYy5taW51dGUpOwogICAgfQoKICAgIGlmIChvLmMubWlsbGlzZWNvbmQgIT09IDAgfHwgby5jLnNlY29uZCAhPT0gMCB8fCAhc3VwcHJlc3NTZWNvbmRzKSB7CiAgICAgIGMgKz0gcGFkU3RhcnQoby5jLnNlY29uZCk7CgogICAgICBpZiAoby5jLm1pbGxpc2Vjb25kICE9PSAwIHx8ICFzdXBwcmVzc01pbGxpc2Vjb25kcykgewogICAgICAgIGMgKz0gIi4iOwogICAgICAgIGMgKz0gcGFkU3RhcnQoby5jLm1pbGxpc2Vjb25kLCAzKTsKICAgICAgfQogICAgfQoKICAgIGlmIChpbmNsdWRlT2Zmc2V0KSB7CiAgICAgIGlmIChvLmlzT2Zmc2V0Rml4ZWQgJiYgby5vZmZzZXQgPT09IDAgJiYgIWV4dGVuZGVkWm9uZSkgewogICAgICAgIGMgKz0gIloiOwogICAgICB9IGVsc2UgaWYgKG8ubyA8IDApIHsKICAgICAgICBjICs9ICItIjsKICAgICAgICBjICs9IHBhZFN0YXJ0KE1hdGgudHJ1bmMoLW8ubyAvIDYwKSk7CiAgICAgICAgYyArPSAiOiI7CiAgICAgICAgYyArPSBwYWRTdGFydChNYXRoLnRydW5jKC1vLm8gJSA2MCkpOwogICAgICB9IGVsc2UgewogICAgICAgIGMgKz0gIisiOwogICAgICAgIGMgKz0gcGFkU3RhcnQoTWF0aC50cnVuYyhvLm8gLyA2MCkpOwogICAgICAgIGMgKz0gIjoiOwogICAgICAgIGMgKz0gcGFkU3RhcnQoTWF0aC50cnVuYyhvLm8gJSA2MCkpOwogICAgICB9CiAgICB9CgogICAgaWYgKGV4dGVuZGVkWm9uZSkgewogICAgICBjICs9ICJbIiArIG8uem9uZS5pYW5hTmFtZSArICJdIjsKICAgIH0KICAgIHJldHVybiBjOwogIH0KCiAgLy8gZGVmYXVsdHMgZm9yIHVuc3BlY2lmaWVkIHVuaXRzIGluIHRoZSBzdXBwb3J0ZWQgY2FsZW5kYXJzCiAgY29uc3QgZGVmYXVsdFVuaXRWYWx1ZXMgPSB7CiAgICAgIG1vbnRoOiAxLAogICAgICBkYXk6IDEsCiAgICAgIGhvdXI6IDAsCiAgICAgIG1pbnV0ZTogMCwKICAgICAgc2Vjb25kOiAwLAogICAgICBtaWxsaXNlY29uZDogMCwKICAgIH0sCiAgICBkZWZhdWx0V2Vla1VuaXRWYWx1ZXMgPSB7CiAgICAgIHdlZWtOdW1iZXI6IDEsCiAgICAgIHdlZWtkYXk6IDEsCiAgICAgIGhvdXI6IDAsCiAgICAgIG1pbnV0ZTogMCwKICAgICAgc2Vjb25kOiAwLAogICAgICBtaWxsaXNlY29uZDogMCwKICAgIH0sCiAgICBkZWZhdWx0T3JkaW5hbFVuaXRWYWx1ZXMgPSB7CiAgICAgIG9yZGluYWw6IDEsCiAgICAgIGhvdXI6IDAsCiAgICAgIG1pbnV0ZTogMCwKICAgICAgc2Vjb25kOiAwLAogICAgICBtaWxsaXNlY29uZDogMCwKICAgIH07CgogIC8vIFVuaXRzIGluIHRoZSBzdXBwb3J0ZWQgY2FsZW5kYXJzLCBzb3J0ZWQgYnkgYmlnbmVzcwogIGNvbnN0IG9yZGVyZWRVbml0cyA9IFsieWVhciIsICJtb250aCIsICJkYXkiLCAiaG91ciIsICJtaW51dGUiLCAic2Vjb25kIiwgIm1pbGxpc2Vjb25kIl0sCiAgICBvcmRlcmVkV2Vla1VuaXRzID0gWwogICAgICAid2Vla1llYXIiLAogICAgICAid2Vla051bWJlciIsCiAgICAgICJ3ZWVrZGF5IiwKICAgICAgImhvdXIiLAogICAgICAibWludXRlIiwKICAgICAgInNlY29uZCIsCiAgICAgICJtaWxsaXNlY29uZCIsCiAgICBdLAogICAgb3JkZXJlZE9yZGluYWxVbml0cyA9IFsieWVhciIsICJvcmRpbmFsIiwgImhvdXIiLCAibWludXRlIiwgInNlY29uZCIsICJtaWxsaXNlY29uZCJdOwoKICAvLyBzdGFuZGFyZGl6ZSBjYXNlIGFuZCBwbHVyYWxpdHkgaW4gdW5pdHMKICBmdW5jdGlvbiBub3JtYWxpemVVbml0KHVuaXQpIHsKICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSB7CiAgICAgIHllYXI6ICJ5ZWFyIiwKICAgICAgeWVhcnM6ICJ5ZWFyIiwKICAgICAgbW9udGg6ICJtb250aCIsCiAgICAgIG1vbnRoczogIm1vbnRoIiwKICAgICAgZGF5OiAiZGF5IiwKICAgICAgZGF5czogImRheSIsCiAgICAgIGhvdXI6ICJob3VyIiwKICAgICAgaG91cnM6ICJob3VyIiwKICAgICAgbWludXRlOiAibWludXRlIiwKICAgICAgbWludXRlczogIm1pbnV0ZSIsCiAgICAgIHF1YXJ0ZXI6ICJxdWFydGVyIiwKICAgICAgcXVhcnRlcnM6ICJxdWFydGVyIiwKICAgICAgc2Vjb25kOiAic2Vjb25kIiwKICAgICAgc2Vjb25kczogInNlY29uZCIsCiAgICAgIG1pbGxpc2Vjb25kOiAibWlsbGlzZWNvbmQiLAogICAgICBtaWxsaXNlY29uZHM6ICJtaWxsaXNlY29uZCIsCiAgICAgIHdlZWtkYXk6ICJ3ZWVrZGF5IiwKICAgICAgd2Vla2RheXM6ICJ3ZWVrZGF5IiwKICAgICAgd2Vla251bWJlcjogIndlZWtOdW1iZXIiLAogICAgICB3ZWVrc251bWJlcjogIndlZWtOdW1iZXIiLAogICAgICB3ZWVrbnVtYmVyczogIndlZWtOdW1iZXIiLAogICAgICB3ZWVreWVhcjogIndlZWtZZWFyIiwKICAgICAgd2Vla3llYXJzOiAid2Vla1llYXIiLAogICAgICBvcmRpbmFsOiAib3JkaW5hbCIsCiAgICB9W3VuaXQudG9Mb3dlckNhc2UoKV07CgogICAgaWYgKCFub3JtYWxpemVkKSB0aHJvdyBuZXcgSW52YWxpZFVuaXRFcnJvcih1bml0KTsKCiAgICByZXR1cm4gbm9ybWFsaXplZDsKICB9CgogIC8vIHRoaXMgaXMgYSBkdW1iZWQgZG93biB2ZXJzaW9uIG9mIGZyb21PYmplY3QoKSB0aGF0IHJ1bnMgYWJvdXQgNjAlIGZhc3RlcgogIC8vIGJ1dCBkb2Vzbid0IGRvIGFueSB2YWxpZGF0aW9uLCBtYWtlcyBhIGJ1bmNoIG9mIGFzc3VtcHRpb25zIGFib3V0IHdoYXQgdW5pdHMKICAvLyBhcmUgcHJlc2VudCwgYW5kIHNvIG9uLgogIGZ1bmN0aW9uIHF1aWNrRFQob2JqLCBvcHRzKSB7CiAgICBjb25zdCB6b25lID0gbm9ybWFsaXplWm9uZShvcHRzLnpvbmUsIFNldHRpbmdzLmRlZmF1bHRab25lKSwKICAgICAgbG9jID0gTG9jYWxlLmZyb21PYmplY3Qob3B0cyksCiAgICAgIHRzTm93ID0gU2V0dGluZ3Mubm93KCk7CgogICAgbGV0IHRzLCBvOwoKICAgIC8vIGFzc3VtZSB3ZSBoYXZlIHRoZSBoaWdoZXItb3JkZXIgdW5pdHMKICAgIGlmICghaXNVbmRlZmluZWQob2JqLnllYXIpKSB7CiAgICAgIGZvciAoY29uc3QgdSBvZiBvcmRlcmVkVW5pdHMpIHsKICAgICAgICBpZiAoaXNVbmRlZmluZWQob2JqW3VdKSkgewogICAgICAgICAgb2JqW3VdID0gZGVmYXVsdFVuaXRWYWx1ZXNbdV07CiAgICAgICAgfQogICAgICB9CgogICAgICBjb25zdCBpbnZhbGlkID0gaGFzSW52YWxpZEdyZWdvcmlhbkRhdGEob2JqKSB8fCBoYXNJbnZhbGlkVGltZURhdGEob2JqKTsKICAgICAgaWYgKGludmFsaWQpIHsKICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZChpbnZhbGlkKTsKICAgICAgfQoKICAgICAgY29uc3Qgb2Zmc2V0UHJvdmlzID0gem9uZS5vZmZzZXQodHNOb3cpOwogICAgICBbdHMsIG9dID0gb2JqVG9UUyhvYmosIG9mZnNldFByb3Zpcywgem9uZSk7CiAgICB9IGVsc2UgewogICAgICB0cyA9IHRzTm93OwogICAgfQoKICAgIHJldHVybiBuZXcgRGF0ZVRpbWUoeyB0cywgem9uZSwgbG9jLCBvIH0pOwogIH0KCiAgZnVuY3Rpb24gZGlmZlJlbGF0aXZlKHN0YXJ0LCBlbmQsIG9wdHMpIHsKICAgIGNvbnN0IHJvdW5kID0gaXNVbmRlZmluZWQob3B0cy5yb3VuZCkgPyB0cnVlIDogb3B0cy5yb3VuZCwKICAgICAgZm9ybWF0ID0gKGMsIHVuaXQpID0+IHsKICAgICAgICBjID0gcm91bmRUbyhjLCByb3VuZCB8fCBvcHRzLmNhbGVuZGFyeSA/IDAgOiAyLCB0cnVlKTsKICAgICAgICBjb25zdCBmb3JtYXR0ZXIgPSBlbmQubG9jLmNsb25lKG9wdHMpLnJlbEZvcm1hdHRlcihvcHRzKTsKICAgICAgICByZXR1cm4gZm9ybWF0dGVyLmZvcm1hdChjLCB1bml0KTsKICAgICAgfSwKICAgICAgZGlmZmVyID0gKHVuaXQpID0+IHsKICAgICAgICBpZiAob3B0cy5jYWxlbmRhcnkpIHsKICAgICAgICAgIGlmICghZW5kLmhhc1NhbWUoc3RhcnQsIHVuaXQpKSB7CiAgICAgICAgICAgIHJldHVybiBlbmQuc3RhcnRPZih1bml0KS5kaWZmKHN0YXJ0LnN0YXJ0T2YodW5pdCksIHVuaXQpLmdldCh1bml0KTsKICAgICAgICAgIH0gZWxzZSByZXR1cm4gMDsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgcmV0dXJuIGVuZC5kaWZmKHN0YXJ0LCB1bml0KS5nZXQodW5pdCk7CiAgICAgICAgfQogICAgICB9OwoKICAgIGlmIChvcHRzLnVuaXQpIHsKICAgICAgcmV0dXJuIGZvcm1hdChkaWZmZXIob3B0cy51bml0KSwgb3B0cy51bml0KTsKICAgIH0KCiAgICBmb3IgKGNvbnN0IHVuaXQgb2Ygb3B0cy51bml0cykgewogICAgICBjb25zdCBjb3VudCA9IGRpZmZlcih1bml0KTsKICAgICAgaWYgKE1hdGguYWJzKGNvdW50KSA+PSAxKSB7CiAgICAgICAgcmV0dXJuIGZvcm1hdChjb3VudCwgdW5pdCk7CiAgICAgIH0KICAgIH0KICAgIHJldHVybiBmb3JtYXQoc3RhcnQgPiBlbmQgPyAtMCA6IDAsIG9wdHMudW5pdHNbb3B0cy51bml0cy5sZW5ndGggLSAxXSk7CiAgfQoKICBmdW5jdGlvbiBsYXN0T3B0cyhhcmdMaXN0KSB7CiAgICBsZXQgb3B0cyA9IHt9LAogICAgICBhcmdzOwogICAgaWYgKGFyZ0xpc3QubGVuZ3RoID4gMCAmJiB0eXBlb2YgYXJnTGlzdFthcmdMaXN0Lmxlbmd0aCAtIDFdID09PSAib2JqZWN0IikgewogICAgICBvcHRzID0gYXJnTGlzdFthcmdMaXN0Lmxlbmd0aCAtIDFdOwogICAgICBhcmdzID0gQXJyYXkuZnJvbShhcmdMaXN0KS5zbGljZSgwLCBhcmdMaXN0Lmxlbmd0aCAtIDEpOwogICAgfSBlbHNlIHsKICAgICAgYXJncyA9IEFycmF5LmZyb20oYXJnTGlzdCk7CiAgICB9CiAgICByZXR1cm4gW29wdHMsIGFyZ3NdOwogIH0KCiAgLyoqCiAgICogQSBEYXRlVGltZSBpcyBhbiBpbW11dGFibGUgZGF0YSBzdHJ1Y3R1cmUgcmVwcmVzZW50aW5nIGEgc3BlY2lmaWMgZGF0ZSBhbmQgdGltZSBhbmQgYWNjb21wYW55aW5nIG1ldGhvZHMuIEl0IGNvbnRhaW5zIGNsYXNzIGFuZCBpbnN0YW5jZSBtZXRob2RzIGZvciBjcmVhdGluZywgcGFyc2luZywgaW50ZXJyb2dhdGluZywgdHJhbnNmb3JtaW5nLCBhbmQgZm9ybWF0dGluZyB0aGVtLgogICAqCiAgICogQSBEYXRlVGltZSBjb21wcmlzZXMgb2Y6CiAgICogKiBBIHRpbWVzdGFtcC4gRWFjaCBEYXRlVGltZSBpbnN0YW5jZSByZWZlcnMgdG8gYSBzcGVjaWZpYyBtaWxsaXNlY29uZCBvZiB0aGUgVW5peCBlcG9jaC4KICAgKiAqIEEgdGltZSB6b25lLiBFYWNoIGluc3RhbmNlIGlzIGNvbnNpZGVyZWQgaW4gdGhlIGNvbnRleHQgb2YgYSBzcGVjaWZpYyB6b25lIChieSBkZWZhdWx0IHRoZSBsb2NhbCBzeXN0ZW0ncyB6b25lKS4KICAgKiAqIENvbmZpZ3VyYXRpb24gcHJvcGVydGllcyB0aGF0IGVmZmVjdCBob3cgb3V0cHV0IHN0cmluZ3MgYXJlIGZvcm1hdHRlZCwgc3VjaCBhcyBgbG9jYWxlYCwgYG51bWJlcmluZ1N5c3RlbWAsIGFuZCBgb3V0cHV0Q2FsZW5kYXJgLgogICAqCiAgICogSGVyZSBpcyBhIGJyaWVmIG92ZXJ2aWV3IG9mIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgZnVuY3Rpb25hbGl0eSBpdCBwcm92aWRlczoKICAgKgogICAqICogKipDcmVhdGlvbioqOiBUbyBjcmVhdGUgYSBEYXRlVGltZSBmcm9tIGl0cyBjb21wb25lbnRzLCB1c2Ugb25lIG9mIGl0cyBmYWN0b3J5IGNsYXNzIG1ldGhvZHM6IHtAbGluayBEYXRlVGltZS5sb2NhbH0sIHtAbGluayBEYXRlVGltZS51dGN9LCBhbmQgKG1vc3QgZmxleGlibHkpIHtAbGluayBEYXRlVGltZS5mcm9tT2JqZWN0fS4gVG8gY3JlYXRlIG9uZSBmcm9tIGEgc3RhbmRhcmQgc3RyaW5nIGZvcm1hdCwgdXNlIHtAbGluayBEYXRlVGltZS5mcm9tSVNPfSwge0BsaW5rIERhdGVUaW1lLmZyb21IVFRQfSwgYW5kIHtAbGluayBEYXRlVGltZS5mcm9tUkZDMjgyMn0uIFRvIGNyZWF0ZSBvbmUgZnJvbSBhIGN1c3RvbSBzdHJpbmcgZm9ybWF0LCB1c2Uge0BsaW5rIERhdGVUaW1lLmZyb21Gb3JtYXR9LiBUbyBjcmVhdGUgb25lIGZyb20gYSBuYXRpdmUgSlMgZGF0ZSwgdXNlIHtAbGluayBEYXRlVGltZS5mcm9tSlNEYXRlfS4KICAgKiAqICoqR3JlZ29yaWFuIGNhbGVuZGFyIGFuZCB0aW1lKio6IFRvIGV4YW1pbmUgdGhlIEdyZWdvcmlhbiBwcm9wZXJ0aWVzIG9mIGEgRGF0ZVRpbWUgaW5kaXZpZHVhbGx5IChpLmUgYXMgb3Bwb3NlZCB0byBjb2xsZWN0aXZlbHkgdGhyb3VnaCB7QGxpbmsgRGF0ZVRpbWUjdG9PYmplY3R9KSwgdXNlIHRoZSB7QGxpbmsgRGF0ZVRpbWUjeWVhcn0sIHtAbGluayBEYXRlVGltZSNtb250aH0sCiAgICoge0BsaW5rIERhdGVUaW1lI2RheX0sIHtAbGluayBEYXRlVGltZSNob3VyfSwge0BsaW5rIERhdGVUaW1lI21pbnV0ZX0sIHtAbGluayBEYXRlVGltZSNzZWNvbmR9LCB7QGxpbmsgRGF0ZVRpbWUjbWlsbGlzZWNvbmR9IGFjY2Vzc29ycy4KICAgKiAqICoqV2VlayBjYWxlbmRhcioqOiBGb3IgSVNPIHdlZWsgY2FsZW5kYXIgYXR0cmlidXRlcywgc2VlIHRoZSB7QGxpbmsgRGF0ZVRpbWUjd2Vla1llYXJ9LCB7QGxpbmsgRGF0ZVRpbWUjd2Vla051bWJlcn0sIGFuZCB7QGxpbmsgRGF0ZVRpbWUjd2Vla2RheX0gYWNjZXNzb3JzLgogICAqICogKipDb25maWd1cmF0aW9uKiogU2VlIHRoZSB7QGxpbmsgRGF0ZVRpbWUjbG9jYWxlfSBhbmQge0BsaW5rIERhdGVUaW1lI251bWJlcmluZ1N5c3RlbX0gYWNjZXNzb3JzLgogICAqICogKipUcmFuc2Zvcm1hdGlvbioqOiBUbyB0cmFuc2Zvcm0gdGhlIERhdGVUaW1lIGludG8gb3RoZXIgRGF0ZVRpbWVzLCB1c2Uge0BsaW5rIERhdGVUaW1lI3NldH0sIHtAbGluayBEYXRlVGltZSNyZWNvbmZpZ3VyZX0sIHtAbGluayBEYXRlVGltZSNzZXRab25lfSwge0BsaW5rIERhdGVUaW1lI3NldExvY2FsZX0sIHtAbGluayBEYXRlVGltZS5wbHVzfSwge0BsaW5rIERhdGVUaW1lI21pbnVzfSwge0BsaW5rIERhdGVUaW1lI2VuZE9mfSwge0BsaW5rIERhdGVUaW1lI3N0YXJ0T2Z9LCB7QGxpbmsgRGF0ZVRpbWUjdG9VVEN9LCBhbmQge0BsaW5rIERhdGVUaW1lI3RvTG9jYWx9LgogICAqICogKipPdXRwdXQqKjogVG8gY29udmVydCB0aGUgRGF0ZVRpbWUgdG8gb3RoZXIgcmVwcmVzZW50YXRpb25zLCB1c2UgdGhlIHtAbGluayBEYXRlVGltZSN0b1JlbGF0aXZlfSwge0BsaW5rIERhdGVUaW1lI3RvUmVsYXRpdmVDYWxlbmRhcn0sIHtAbGluayBEYXRlVGltZSN0b0pTT059LCB7QGxpbmsgRGF0ZVRpbWUjdG9JU099LCB7QGxpbmsgRGF0ZVRpbWUjdG9IVFRQfSwge0BsaW5rIERhdGVUaW1lI3RvT2JqZWN0fSwge0BsaW5rIERhdGVUaW1lI3RvUkZDMjgyMn0sIHtAbGluayBEYXRlVGltZSN0b1N0cmluZ30sIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30sIHtAbGluayBEYXRlVGltZSN0b0Zvcm1hdH0sIHtAbGluayBEYXRlVGltZSN0b01pbGxpc30gYW5kIHtAbGluayBEYXRlVGltZSN0b0pTRGF0ZX0uCiAgICoKICAgKiBUaGVyZSdzIHBsZW50eSBvdGhlcnMgZG9jdW1lbnRlZCBiZWxvdy4gSW4gYWRkaXRpb24sIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHN1YnRsZXIgdG9waWNzIGxpa2UgaW50ZXJuYXRpb25hbGl6YXRpb24sIHRpbWUgem9uZXMsIGFsdGVybmF0aXZlIGNhbGVuZGFycywgdmFsaWRpdHksIGFuZCBzbyBvbiwgc2VlIHRoZSBleHRlcm5hbCBkb2N1bWVudGF0aW9uLgogICAqLwogIGNsYXNzIERhdGVUaW1lIHsKICAgIC8qKgogICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgKi8KICAgIGNvbnN0cnVjdG9yKGNvbmZpZykgewogICAgICBjb25zdCB6b25lID0gY29uZmlnLnpvbmUgfHwgU2V0dGluZ3MuZGVmYXVsdFpvbmU7CgogICAgICBsZXQgaW52YWxpZCA9CiAgICAgICAgY29uZmlnLmludmFsaWQgfHwKICAgICAgICAoTnVtYmVyLmlzTmFOKGNvbmZpZy50cykgPyBuZXcgSW52YWxpZCgiaW52YWxpZCBpbnB1dCIpIDogbnVsbCkgfHwKICAgICAgICAoIXpvbmUuaXNWYWxpZCA/IHVuc3VwcG9ydGVkWm9uZSh6b25lKSA6IG51bGwpOwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLnRzID0gaXNVbmRlZmluZWQoY29uZmlnLnRzKSA/IFNldHRpbmdzLm5vdygpIDogY29uZmlnLnRzOwoKICAgICAgbGV0IGMgPSBudWxsLAogICAgICAgIG8gPSBudWxsOwogICAgICBpZiAoIWludmFsaWQpIHsKICAgICAgICBjb25zdCB1bmNoYW5nZWQgPSBjb25maWcub2xkICYmIGNvbmZpZy5vbGQudHMgPT09IHRoaXMudHMgJiYgY29uZmlnLm9sZC56b25lLmVxdWFscyh6b25lKTsKCiAgICAgICAgaWYgKHVuY2hhbmdlZCkgewogICAgICAgICAgW2MsIG9dID0gW2NvbmZpZy5vbGQuYywgY29uZmlnLm9sZC5vXTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgY29uc3Qgb3QgPSB6b25lLm9mZnNldCh0aGlzLnRzKTsKICAgICAgICAgIGMgPSB0c1RvT2JqKHRoaXMudHMsIG90KTsKICAgICAgICAgIGludmFsaWQgPSBOdW1iZXIuaXNOYU4oYy55ZWFyKSA/IG5ldyBJbnZhbGlkKCJpbnZhbGlkIGlucHV0IikgOiBudWxsOwogICAgICAgICAgYyA9IGludmFsaWQgPyBudWxsIDogYzsKICAgICAgICAgIG8gPSBpbnZhbGlkID8gbnVsbCA6IG90OwogICAgICAgIH0KICAgICAgfQoKICAgICAgLyoqCiAgICAgICAqIEBhY2Nlc3MgcHJpdmF0ZQogICAgICAgKi8KICAgICAgdGhpcy5fem9uZSA9IHpvbmU7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMubG9jID0gY29uZmlnLmxvYyB8fCBMb2NhbGUuY3JlYXRlKCk7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMuaW52YWxpZCA9IGludmFsaWQ7CiAgICAgIC8qKgogICAgICAgKiBAYWNjZXNzIHByaXZhdGUKICAgICAgICovCiAgICAgIHRoaXMud2Vla0RhdGEgPSBudWxsOwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLmMgPSBjOwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLm8gPSBvOwogICAgICAvKioKICAgICAgICogQGFjY2VzcyBwcml2YXRlCiAgICAgICAqLwogICAgICB0aGlzLmlzTHV4b25EYXRlVGltZSA9IHRydWU7CiAgICB9CgogICAgLy8gQ09OU1RSVUNUCgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEYXRlVGltZSBmb3IgdGhlIGN1cnJlbnQgaW5zdGFudCwgaW4gdGhlIHN5c3RlbSdzIHRpbWUgem9uZS4KICAgICAqCiAgICAgKiBVc2UgU2V0dGluZ3MgdG8gb3ZlcnJpZGUgdGhlc2UgZGVmYXVsdCB2YWx1ZXMgaWYgbmVlZGVkLgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkudG9JU08oKSAvL34+IG5vdyBpbiB0aGUgSVNPIGZvcm1hdAogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBub3coKSB7CiAgICAgIHJldHVybiBuZXcgRGF0ZVRpbWUoe30pOwogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGEgbG9jYWwgRGF0ZVRpbWUKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbeWVhcl0gLSBUaGUgY2FsZW5kYXIgeWVhci4gSWYgb21pdHRlZCAoYXMgaW4sIGNhbGwgYGxvY2FsKClgIHdpdGggbm8gYXJndW1lbnRzKSwgdGhlIGN1cnJlbnQgdGltZSB3aWxsIGJlIHVzZWQKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbbW9udGg9MV0gLSBUaGUgbW9udGgsIDEtaW5kZXhlZAogICAgICogQHBhcmFtIHtudW1iZXJ9IFtkYXk9MV0gLSBUaGUgZGF5IG9mIHRoZSBtb250aCwgMS1pbmRleGVkCiAgICAgKiBAcGFyYW0ge251bWJlcn0gW2hvdXI9MF0gLSBUaGUgaG91ciBvZiB0aGUgZGF5LCBpbiAyNC1ob3VyIHRpbWUKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbbWludXRlPTBdIC0gVGhlIG1pbnV0ZSBvZiB0aGUgaG91ciwgbWVhbmluZyBhIG51bWJlciBiZXR3ZWVuIDAgYW5kIDU5CiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3NlY29uZD0wXSAtIFRoZSBzZWNvbmQgb2YgdGhlIG1pbnV0ZSwgbWVhbmluZyBhIG51bWJlciBiZXR3ZWVuIDAgYW5kIDU5CiAgICAgKiBAcGFyYW0ge251bWJlcn0gW21pbGxpc2Vjb25kPTBdIC0gVGhlIG1pbGxpc2Vjb25kIG9mIHRoZSBzZWNvbmQsIG1lYW5pbmcgYSBudW1iZXIgYmV0d2VlbiAwIGFuZCA5OTkKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiBub3cKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKHsgem9uZTogIkFtZXJpY2EvTmV3X1lvcmsiIH0pICAgICAgLy9+PiBub3csIGluIFVTIGVhc3QgY29hc3QgdGltZQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL34+IDIwMTctMDEtMDFUMDA6MDA6MDAKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDMpICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTAxVDAwOjAwOjAwCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE3LCAzLCAxMiwgeyBsb2NhbGU6ICJmciIgfSkgICAgIC8vfj4gMjAxNy0wMy0xMlQwMDowMDowMCwgd2l0aCBhIEZyZW5jaCBsb2NhbGUKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDMsIDEyLCA1KSAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTEyVDA1OjAwOjAwCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE3LCAzLCAxMiwgNSwgeyB6b25lOiAidXRjIiB9KSAgIC8vfj4gMjAxNy0wMy0xMlQwNTowMDowMCwgaW4gVVRDCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE3LCAzLCAxMiwgNSwgNDUpICAgICAgICAgICAgICAgIC8vfj4gMjAxNy0wMy0xMlQwNTo0NTowMAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgMywgMTIsIDUsIDQ1LCAxMCkgICAgICAgICAgICAvL34+IDIwMTctMDMtMTJUMDU6NDU6MTAKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDMsIDEyLCA1LCA0NSwgMTAsIDc2NSkgICAgICAgLy9+PiAyMDE3LTAzLTEyVDA1OjQ1OjEwLjc2NQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBsb2NhbCgpIHsKICAgICAgY29uc3QgW29wdHMsIGFyZ3NdID0gbGFzdE9wdHMoYXJndW1lbnRzKSwKICAgICAgICBbeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQsIG1pbGxpc2Vjb25kXSA9IGFyZ3M7CiAgICAgIHJldHVybiBxdWlja0RUKHsgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQsIG1pbGxpc2Vjb25kIH0sIG9wdHMpOwogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGEgRGF0ZVRpbWUgaW4gVVRDCiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3llYXJdIC0gVGhlIGNhbGVuZGFyIHllYXIuIElmIG9taXR0ZWQgKGFzIGluLCBjYWxsIGB1dGMoKWAgd2l0aCBubyBhcmd1bWVudHMpLCB0aGUgY3VycmVudCB0aW1lIHdpbGwgYmUgdXNlZAogICAgICogQHBhcmFtIHtudW1iZXJ9IFttb250aD0xXSAtIFRoZSBtb250aCwgMS1pbmRleGVkCiAgICAgKiBAcGFyYW0ge251bWJlcn0gW2RheT0xXSAtIFRoZSBkYXkgb2YgdGhlIG1vbnRoCiAgICAgKiBAcGFyYW0ge251bWJlcn0gW2hvdXI9MF0gLSBUaGUgaG91ciBvZiB0aGUgZGF5LCBpbiAyNC1ob3VyIHRpbWUKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbbWludXRlPTBdIC0gVGhlIG1pbnV0ZSBvZiB0aGUgaG91ciwgbWVhbmluZyBhIG51bWJlciBiZXR3ZWVuIDAgYW5kIDU5CiAgICAgKiBAcGFyYW0ge251bWJlcn0gW3NlY29uZD0wXSAtIFRoZSBzZWNvbmQgb2YgdGhlIG1pbnV0ZSwgbWVhbmluZyBhIG51bWJlciBiZXR3ZWVuIDAgYW5kIDU5CiAgICAgKiBAcGFyYW0ge251bWJlcn0gW21pbGxpc2Vjb25kPTBdIC0gVGhlIG1pbGxpc2Vjb25kIG9mIHRoZSBzZWNvbmQsIG1lYW5pbmcgYSBudW1iZXIgYmV0d2VlbiAwIGFuZCA5OTkKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gY29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgRGF0ZVRpbWUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0aW9ucy5sb2NhbGVdIC0gYSBsb2NhbGUgdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0aW9ucy5vdXRwdXRDYWxlbmRhcl0gLSB0aGUgb3V0cHV0IGNhbGVuZGFyIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdGlvbnMubnVtYmVyaW5nU3lzdGVtXSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS51dGMoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL34+IG5vdwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAxLTAxVDAwOjAwOjAwWgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTAxVDAwOjAwOjAwWgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMsIDEyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTEyVDAwOjAwOjAwWgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMsIDEyLCA1KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTEyVDA1OjAwOjAwWgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMsIDEyLCA1LCA0NSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTEyVDA1OjQ1OjAwWgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMsIDEyLCA1LCA0NSwgeyBsb2NhbGU6ICJmciIgfSkgICAgICAgICAgLy9+PiAyMDE3LTAzLTEyVDA1OjQ1OjAwWiB3aXRoIGEgRnJlbmNoIGxvY2FsZQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMsIDEyLCA1LCA0NSwgMTApICAgICAgICAgICAgICAgICAgICAgICAgLy9+PiAyMDE3LTAzLTEyVDA1OjQ1OjEwWgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTcsIDMsIDEyLCA1LCA0NSwgMTAsIDc2NSwgeyBsb2NhbGU6ICJmciIgfSkgLy9+PiAyMDE3LTAzLTEyVDA1OjQ1OjEwLjc2NVogd2l0aCBhIEZyZW5jaCBsb2NhbGUKICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfQogICAgICovCiAgICBzdGF0aWMgdXRjKCkgewogICAgICBjb25zdCBbb3B0cywgYXJnc10gPSBsYXN0T3B0cyhhcmd1bWVudHMpLAogICAgICAgIFt5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCwgbWlsbGlzZWNvbmRdID0gYXJnczsKCiAgICAgIG9wdHMuem9uZSA9IEZpeGVkT2Zmc2V0Wm9uZS51dGNJbnN0YW5jZTsKICAgICAgcmV0dXJuIHF1aWNrRFQoeyB5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUsIHNlY29uZCwgbWlsbGlzZWNvbmQgfSwgb3B0cyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEYXRlVGltZSBmcm9tIGEgSmF2YVNjcmlwdCBEYXRlIG9iamVjdC4gVXNlcyB0aGUgZGVmYXVsdCB6b25lLgogICAgICogQHBhcmFtIHtEYXRlfSBkYXRlIC0gYSBKYXZhU2NyaXB0IERhdGUgb2JqZWN0CiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIERhdGVUaW1lCiAgICAgKiBAcGFyYW0ge3N0cmluZ3xab25lfSBbb3B0aW9ucy56b25lPSdsb2NhbCddIC0gdGhlIHpvbmUgdG8gcGxhY2UgdGhlIERhdGVUaW1lIGludG8KICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfQogICAgICovCiAgICBzdGF0aWMgZnJvbUpTRGF0ZShkYXRlLCBvcHRpb25zID0ge30pIHsKICAgICAgY29uc3QgdHMgPSBpc0RhdGUoZGF0ZSkgPyBkYXRlLnZhbHVlT2YoKSA6IE5hTjsKICAgICAgaWYgKE51bWJlci5pc05hTih0cykpIHsKICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZCgiaW52YWxpZCBpbnB1dCIpOwogICAgICB9CgogICAgICBjb25zdCB6b25lVG9Vc2UgPSBub3JtYWxpemVab25lKG9wdGlvbnMuem9uZSwgU2V0dGluZ3MuZGVmYXVsdFpvbmUpOwogICAgICBpZiAoIXpvbmVUb1VzZS5pc1ZhbGlkKSB7CiAgICAgICAgcmV0dXJuIERhdGVUaW1lLmludmFsaWQodW5zdXBwb3J0ZWRab25lKHpvbmVUb1VzZSkpOwogICAgICB9CgogICAgICByZXR1cm4gbmV3IERhdGVUaW1lKHsKICAgICAgICB0czogdHMsCiAgICAgICAgem9uZTogem9uZVRvVXNlLAogICAgICAgIGxvYzogTG9jYWxlLmZyb21PYmplY3Qob3B0aW9ucyksCiAgICAgIH0pOwogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGEgRGF0ZVRpbWUgZnJvbSBhIG51bWJlciBvZiBtaWxsaXNlY29uZHMgc2luY2UgdGhlIGVwb2NoIChtZWFuaW5nIHNpbmNlIDEgSmFudWFyeSAxOTcwIDAwOjAwOjAwIFVUQykuIFVzZXMgdGhlIGRlZmF1bHQgem9uZS4KICAgICAqIEBwYXJhbSB7bnVtYmVyfSBtaWxsaXNlY29uZHMgLSBhIG51bWJlciBvZiBtaWxsaXNlY29uZHMgc2luY2UgMTk3MCBVVEMKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gY29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgRGF0ZVRpbWUKICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFtvcHRpb25zLnpvbmU9J2xvY2FsJ10gLSB0aGUgem9uZSB0byBwbGFjZSB0aGUgRGF0ZVRpbWUgaW50bwogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRpb25zLmxvY2FsZV0gLSBhIGxvY2FsZSB0byBzZXQgb24gdGhlIHJlc3VsdGluZyBEYXRlVGltZSBpbnN0YW5jZQogICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdGlvbnMub3V0cHV0Q2FsZW5kYXIgLSB0aGUgb3V0cHV0IGNhbGVuZGFyIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5udW1iZXJpbmdTeXN0ZW0gLSB0aGUgbnVtYmVyaW5nIHN5c3RlbSB0byBzZXQgb24gdGhlIHJlc3VsdGluZyBEYXRlVGltZSBpbnN0YW5jZQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBmcm9tTWlsbGlzKG1pbGxpc2Vjb25kcywgb3B0aW9ucyA9IHt9KSB7CiAgICAgIGlmICghaXNOdW1iZXIobWlsbGlzZWNvbmRzKSkgewogICAgICAgIHRocm93IG5ldyBJbnZhbGlkQXJndW1lbnRFcnJvcigKICAgICAgICAgIGBmcm9tTWlsbGlzIHJlcXVpcmVzIGEgbnVtZXJpY2FsIGlucHV0LCBidXQgcmVjZWl2ZWQgYSAke3R5cGVvZiBtaWxsaXNlY29uZHN9IHdpdGggdmFsdWUgJHttaWxsaXNlY29uZHN9YAogICAgICAgICk7CiAgICAgIH0gZWxzZSBpZiAobWlsbGlzZWNvbmRzIDwgLU1BWF9EQVRFIHx8IG1pbGxpc2Vjb25kcyA+IE1BWF9EQVRFKSB7CiAgICAgICAgLy8gdGhpcyBpc24ndCBwZXJmZWN0IGJlY2F1c2UgYmVjYXVzZSB3ZSBjYW4gc3RpbGwgZW5kIHVwIG91dCBvZiByYW5nZSBiZWNhdXNlIG9mIGFkZGl0aW9uYWwgc2hpZnRpbmcsIGJ1dCBpdCdzIGEgc3RhcnQKICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZCgiVGltZXN0YW1wIG91dCBvZiByYW5nZSIpOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBuZXcgRGF0ZVRpbWUoewogICAgICAgICAgdHM6IG1pbGxpc2Vjb25kcywKICAgICAgICAgIHpvbmU6IG5vcm1hbGl6ZVpvbmUob3B0aW9ucy56b25lLCBTZXR0aW5ncy5kZWZhdWx0Wm9uZSksCiAgICAgICAgICBsb2M6IExvY2FsZS5mcm9tT2JqZWN0KG9wdGlvbnMpLAogICAgICAgIH0pOwogICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEYXRlVGltZSBmcm9tIGEgbnVtYmVyIG9mIHNlY29uZHMgc2luY2UgdGhlIGVwb2NoIChtZWFuaW5nIHNpbmNlIDEgSmFudWFyeSAxOTcwIDAwOjAwOjAwIFVUQykuIFVzZXMgdGhlIGRlZmF1bHQgem9uZS4KICAgICAqIEBwYXJhbSB7bnVtYmVyfSBzZWNvbmRzIC0gYSBudW1iZXIgb2Ygc2Vjb25kcyBzaW5jZSAxOTcwIFVUQwogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBjb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBEYXRlVGltZQogICAgICogQHBhcmFtIHtzdHJpbmd8Wm9uZX0gW29wdGlvbnMuem9uZT0nbG9jYWwnXSAtIHRoZSB6b25lIHRvIHBsYWNlIHRoZSBEYXRlVGltZSBpbnRvCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdGlvbnMubG9jYWxlXSAtIGEgbG9jYWxlIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5vdXRwdXRDYWxlbmRhciAtIHRoZSBvdXRwdXQgY2FsZW5kYXIgdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLm51bWJlcmluZ1N5c3RlbSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcmV0dXJuIHtEYXRlVGltZX0KICAgICAqLwogICAgc3RhdGljIGZyb21TZWNvbmRzKHNlY29uZHMsIG9wdGlvbnMgPSB7fSkgewogICAgICBpZiAoIWlzTnVtYmVyKHNlY29uZHMpKSB7CiAgICAgICAgdGhyb3cgbmV3IEludmFsaWRBcmd1bWVudEVycm9yKCJmcm9tU2Vjb25kcyByZXF1aXJlcyBhIG51bWVyaWNhbCBpbnB1dCIpOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBuZXcgRGF0ZVRpbWUoewogICAgICAgICAgdHM6IHNlY29uZHMgKiAxMDAwLAogICAgICAgICAgem9uZTogbm9ybWFsaXplWm9uZShvcHRpb25zLnpvbmUsIFNldHRpbmdzLmRlZmF1bHRab25lKSwKICAgICAgICAgIGxvYzogTG9jYWxlLmZyb21PYmplY3Qob3B0aW9ucyksCiAgICAgICAgfSk7CiAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZSBhIERhdGVUaW1lIGZyb20gYSBKYXZhU2NyaXB0IG9iamVjdCB3aXRoIGtleXMgbGlrZSAneWVhcicgYW5kICdob3VyJyB3aXRoIHJlYXNvbmFibGUgZGVmYXVsdHMuCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb2JqIC0gdGhlIG9iamVjdCB0byBjcmVhdGUgdGhlIERhdGVUaW1lIGZyb20KICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvYmoueWVhciAtIGEgeWVhciwgc3VjaCBhcyAxOTg3CiAgICAgKiBAcGFyYW0ge251bWJlcn0gb2JqLm1vbnRoIC0gYSBtb250aCwgMS0xMgogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5kYXkgLSBhIGRheSBvZiB0aGUgbW9udGgsIDEtMzEsIGRlcGVuZGluZyBvbiB0aGUgbW9udGgKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvYmoub3JkaW5hbCAtIGRheSBvZiB0aGUgeWVhciwgMS0zNjUgb3IgMzY2CiAgICAgKiBAcGFyYW0ge251bWJlcn0gb2JqLndlZWtZZWFyIC0gYW4gSVNPIHdlZWsgeWVhcgogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai53ZWVrTnVtYmVyIC0gYW4gSVNPIHdlZWsgbnVtYmVyLCBiZXR3ZWVuIDEgYW5kIDUyIG9yIDUzLCBkZXBlbmRpbmcgb24gdGhlIHllYXIKICAgICAqIEBwYXJhbSB7bnVtYmVyfSBvYmoud2Vla2RheSAtIGFuIElTTyB3ZWVrZGF5LCAxLTcsIHdoZXJlIDEgaXMgTW9uZGF5IGFuZCA3IGlzIFN1bmRheQogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5ob3VyIC0gaG91ciBvZiB0aGUgZGF5LCAwLTIzCiAgICAgKiBAcGFyYW0ge251bWJlcn0gb2JqLm1pbnV0ZSAtIG1pbnV0ZSBvZiB0aGUgaG91ciwgMC01OQogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5zZWNvbmQgLSBzZWNvbmQgb2YgdGhlIG1pbnV0ZSwgMC01OQogICAgICogQHBhcmFtIHtudW1iZXJ9IG9iai5taWxsaXNlY29uZCAtIG1pbGxpc2Vjb25kIG9mIHRoZSBzZWNvbmQsIDAtOTk5CiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMgZm9yIGNyZWF0aW5nIHRoaXMgRGF0ZVRpbWUKICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFtvcHRzLnpvbmU9J2xvY2FsJ10gLSBpbnRlcnByZXQgdGhlIG51bWJlcnMgaW4gdGhlIGNvbnRleHQgb2YgYSBwYXJ0aWN1bGFyIHpvbmUuIENhbiB0YWtlIGFueSB2YWx1ZSB0YWtlbiBhcyB0aGUgZmlyc3QgYXJndW1lbnQgdG8gc2V0Wm9uZSgpCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jYWxlPSdzeXN0ZW0ncyBsb2NhbGUnXSAtIGEgbG9jYWxlIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0cy5vdXRwdXRDYWxlbmRhciAtIHRoZSBvdXRwdXQgY2FsZW5kYXIgdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLm51bWJlcmluZ1N5c3RlbSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tT2JqZWN0KHsgeWVhcjogMTk4MiwgbW9udGg6IDUsIGRheTogMjV9KS50b0lTT0RhdGUoKSAvLz0+ICcxOTgyLTA1LTI1JwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbU9iamVjdCh7IHllYXI6IDE5ODIgfSkudG9JU09EYXRlKCkgLy89PiAnMTk4Mi0wMS0wMScKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21PYmplY3QoeyBob3VyOiAxMCwgbWludXRlOiAyNiwgc2Vjb25kOiA2IH0pIC8vfj4gdG9kYXkgYXQgMTA6MjY6MDYKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21PYmplY3QoeyBob3VyOiAxMCwgbWludXRlOiAyNiwgc2Vjb25kOiA2IH0sIHsgem9uZTogJ3V0YycgfSksCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tT2JqZWN0KHsgaG91cjogMTAsIG1pbnV0ZTogMjYsIHNlY29uZDogNiB9LCB7IHpvbmU6ICdsb2NhbCcgfSkKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21PYmplY3QoeyBob3VyOiAxMCwgbWludXRlOiAyNiwgc2Vjb25kOiA2IH0sIHsgem9uZTogJ0FtZXJpY2EvTmV3X1lvcmsnIH0pCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tT2JqZWN0KHsgd2Vla1llYXI6IDIwMTYsIHdlZWtOdW1iZXI6IDIsIHdlZWtkYXk6IDMgfSkudG9JU09EYXRlKCkgLy89PiAnMjAxNi0wMS0xMycKICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfQogICAgICovCiAgICBzdGF0aWMgZnJvbU9iamVjdChvYmosIG9wdHMgPSB7fSkgewogICAgICBvYmogPSBvYmogfHwge307CiAgICAgIGNvbnN0IHpvbmVUb1VzZSA9IG5vcm1hbGl6ZVpvbmUob3B0cy56b25lLCBTZXR0aW5ncy5kZWZhdWx0Wm9uZSk7CiAgICAgIGlmICghem9uZVRvVXNlLmlzVmFsaWQpIHsKICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZCh1bnN1cHBvcnRlZFpvbmUoem9uZVRvVXNlKSk7CiAgICAgIH0KCiAgICAgIGNvbnN0IHRzTm93ID0gU2V0dGluZ3Mubm93KCksCiAgICAgICAgb2Zmc2V0UHJvdmlzID0gIWlzVW5kZWZpbmVkKG9wdHMuc3BlY2lmaWNPZmZzZXQpCiAgICAgICAgICA/IG9wdHMuc3BlY2lmaWNPZmZzZXQKICAgICAgICAgIDogem9uZVRvVXNlLm9mZnNldCh0c05vdyksCiAgICAgICAgbm9ybWFsaXplZCA9IG5vcm1hbGl6ZU9iamVjdChvYmosIG5vcm1hbGl6ZVVuaXQpLAogICAgICAgIGNvbnRhaW5zT3JkaW5hbCA9ICFpc1VuZGVmaW5lZChub3JtYWxpemVkLm9yZGluYWwpLAogICAgICAgIGNvbnRhaW5zR3JlZ29yWWVhciA9ICFpc1VuZGVmaW5lZChub3JtYWxpemVkLnllYXIpLAogICAgICAgIGNvbnRhaW5zR3JlZ29yTUQgPSAhaXNVbmRlZmluZWQobm9ybWFsaXplZC5tb250aCkgfHwgIWlzVW5kZWZpbmVkKG5vcm1hbGl6ZWQuZGF5KSwKICAgICAgICBjb250YWluc0dyZWdvciA9IGNvbnRhaW5zR3JlZ29yWWVhciB8fCBjb250YWluc0dyZWdvck1ELAogICAgICAgIGRlZmluaXRlV2Vla0RlZiA9IG5vcm1hbGl6ZWQud2Vla1llYXIgfHwgbm9ybWFsaXplZC53ZWVrTnVtYmVyLAogICAgICAgIGxvYyA9IExvY2FsZS5mcm9tT2JqZWN0KG9wdHMpOwoKICAgICAgLy8gY2FzZXM6CiAgICAgIC8vIGp1c3QgYSB3ZWVrZGF5IC0+IHRoaXMgd2VlaydzIGluc3RhbmNlIG9mIHRoYXQgd2Vla2RheSwgbm8gd29ycmllcwogICAgICAvLyAoZ3JlZ29yaWFuIGRhdGEgb3Igb3JkaW5hbCkgKyAod2Vla1llYXIgb3Igd2Vla051bWJlcikgLT4gZXJyb3IKICAgICAgLy8gKGdyZWdvcmlhbiBtb250aCBvciBkYXkpICsgb3JkaW5hbCAtPiBlcnJvcgogICAgICAvLyBvdGhlcndpc2UganVzdCB1c2Ugd2Vla3Mgb3Igb3JkaW5hbHMgb3IgZ3JlZ29yaWFuLCBkZXBlbmRpbmcgb24gd2hhdCdzIHNwZWNpZmllZAoKICAgICAgaWYgKChjb250YWluc0dyZWdvciB8fCBjb250YWluc09yZGluYWwpICYmIGRlZmluaXRlV2Vla0RlZikgewogICAgICAgIHRocm93IG5ldyBDb25mbGljdGluZ1NwZWNpZmljYXRpb25FcnJvcigKICAgICAgICAgICJDYW4ndCBtaXggd2Vla1llYXIvd2Vla051bWJlciB1bml0cyB3aXRoIHllYXIvbW9udGgvZGF5IG9yIG9yZGluYWxzIgogICAgICAgICk7CiAgICAgIH0KCiAgICAgIGlmIChjb250YWluc0dyZWdvck1EICYmIGNvbnRhaW5zT3JkaW5hbCkgewogICAgICAgIHRocm93IG5ldyBDb25mbGljdGluZ1NwZWNpZmljYXRpb25FcnJvcigiQ2FuJ3QgbWl4IG9yZGluYWwgZGF0ZXMgd2l0aCBtb250aC9kYXkiKTsKICAgICAgfQoKICAgICAgY29uc3QgdXNlV2Vla0RhdGEgPSBkZWZpbml0ZVdlZWtEZWYgfHwgKG5vcm1hbGl6ZWQud2Vla2RheSAmJiAhY29udGFpbnNHcmVnb3IpOwoKICAgICAgLy8gY29uZmlndXJlIG91cnNlbHZlcyB0byBkZWFsIHdpdGggZ3JlZ29yaWFuIGRhdGVzIG9yIHdlZWsgc3R1ZmYKICAgICAgbGV0IHVuaXRzLAogICAgICAgIGRlZmF1bHRWYWx1ZXMsCiAgICAgICAgb2JqTm93ID0gdHNUb09iaih0c05vdywgb2Zmc2V0UHJvdmlzKTsKICAgICAgaWYgKHVzZVdlZWtEYXRhKSB7CiAgICAgICAgdW5pdHMgPSBvcmRlcmVkV2Vla1VuaXRzOwogICAgICAgIGRlZmF1bHRWYWx1ZXMgPSBkZWZhdWx0V2Vla1VuaXRWYWx1ZXM7CiAgICAgICAgb2JqTm93ID0gZ3JlZ29yaWFuVG9XZWVrKG9iak5vdyk7CiAgICAgIH0gZWxzZSBpZiAoY29udGFpbnNPcmRpbmFsKSB7CiAgICAgICAgdW5pdHMgPSBvcmRlcmVkT3JkaW5hbFVuaXRzOwogICAgICAgIGRlZmF1bHRWYWx1ZXMgPSBkZWZhdWx0T3JkaW5hbFVuaXRWYWx1ZXM7CiAgICAgICAgb2JqTm93ID0gZ3JlZ29yaWFuVG9PcmRpbmFsKG9iak5vdyk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgdW5pdHMgPSBvcmRlcmVkVW5pdHM7CiAgICAgICAgZGVmYXVsdFZhbHVlcyA9IGRlZmF1bHRVbml0VmFsdWVzOwogICAgICB9CgogICAgICAvLyBzZXQgZGVmYXVsdCB2YWx1ZXMgZm9yIG1pc3Npbmcgc3R1ZmYKICAgICAgbGV0IGZvdW5kRmlyc3QgPSBmYWxzZTsKICAgICAgZm9yIChjb25zdCB1IG9mIHVuaXRzKSB7CiAgICAgICAgY29uc3QgdiA9IG5vcm1hbGl6ZWRbdV07CiAgICAgICAgaWYgKCFpc1VuZGVmaW5lZCh2KSkgewogICAgICAgICAgZm91bmRGaXJzdCA9IHRydWU7CiAgICAgICAgfSBlbHNlIGlmIChmb3VuZEZpcnN0KSB7CiAgICAgICAgICBub3JtYWxpemVkW3VdID0gZGVmYXVsdFZhbHVlc1t1XTsKICAgICAgICB9IGVsc2UgewogICAgICAgICAgbm9ybWFsaXplZFt1XSA9IG9iak5vd1t1XTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8vIG1ha2Ugc3VyZSB0aGUgdmFsdWVzIHdlIGhhdmUgYXJlIGluIHJhbmdlCiAgICAgIGNvbnN0IGhpZ2hlck9yZGVySW52YWxpZCA9IHVzZVdlZWtEYXRhCiAgICAgICAgICA/IGhhc0ludmFsaWRXZWVrRGF0YShub3JtYWxpemVkKQogICAgICAgICAgOiBjb250YWluc09yZGluYWwKICAgICAgICAgID8gaGFzSW52YWxpZE9yZGluYWxEYXRhKG5vcm1hbGl6ZWQpCiAgICAgICAgICA6IGhhc0ludmFsaWRHcmVnb3JpYW5EYXRhKG5vcm1hbGl6ZWQpLAogICAgICAgIGludmFsaWQgPSBoaWdoZXJPcmRlckludmFsaWQgfHwgaGFzSW52YWxpZFRpbWVEYXRhKG5vcm1hbGl6ZWQpOwoKICAgICAgaWYgKGludmFsaWQpIHsKICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZChpbnZhbGlkKTsKICAgICAgfQoKICAgICAgLy8gY29tcHV0ZSB0aGUgYWN0dWFsIHRpbWUKICAgICAgY29uc3QgZ3JlZ29yaWFuID0gdXNlV2Vla0RhdGEKICAgICAgICAgID8gd2Vla1RvR3JlZ29yaWFuKG5vcm1hbGl6ZWQpCiAgICAgICAgICA6IGNvbnRhaW5zT3JkaW5hbAogICAgICAgICAgPyBvcmRpbmFsVG9HcmVnb3JpYW4obm9ybWFsaXplZCkKICAgICAgICAgIDogbm9ybWFsaXplZCwKICAgICAgICBbdHNGaW5hbCwgb2Zmc2V0RmluYWxdID0gb2JqVG9UUyhncmVnb3JpYW4sIG9mZnNldFByb3Zpcywgem9uZVRvVXNlKSwKICAgICAgICBpbnN0ID0gbmV3IERhdGVUaW1lKHsKICAgICAgICAgIHRzOiB0c0ZpbmFsLAogICAgICAgICAgem9uZTogem9uZVRvVXNlLAogICAgICAgICAgbzogb2Zmc2V0RmluYWwsCiAgICAgICAgICBsb2MsCiAgICAgICAgfSk7CgogICAgICAvLyBncmVnb3JpYW4gZGF0YSArIHdlZWtkYXkgc2VydmVzIG9ubHkgdG8gdmFsaWRhdGUKICAgICAgaWYgKG5vcm1hbGl6ZWQud2Vla2RheSAmJiBjb250YWluc0dyZWdvciAmJiBvYmoud2Vla2RheSAhPT0gaW5zdC53ZWVrZGF5KSB7CiAgICAgICAgcmV0dXJuIERhdGVUaW1lLmludmFsaWQoCiAgICAgICAgICAibWlzbWF0Y2hlZCB3ZWVrZGF5IiwKICAgICAgICAgIGB5b3UgY2FuJ3Qgc3BlY2lmeSBib3RoIGEgd2Vla2RheSBvZiAke25vcm1hbGl6ZWQud2Vla2RheX0gYW5kIGEgZGF0ZSBvZiAke2luc3QudG9JU08oKX1gCiAgICAgICAgKTsKICAgICAgfQoKICAgICAgcmV0dXJuIGluc3Q7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEYXRlVGltZSBmcm9tIGFuIElTTyA4NjAxIHN0cmluZwogICAgICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSB0aGUgSVNPIHN0cmluZwogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIHRvIGFmZmVjdCB0aGUgY3JlYXRpb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFtvcHRzLnpvbmU9J2xvY2FsJ10gLSB1c2UgdGhpcyB6b25lIGlmIG5vIG9mZnNldCBpcyBzcGVjaWZpZWQgaW4gdGhlIGlucHV0IHN0cmluZyBpdHNlbGYuIFdpbGwgYWxzbyBjb252ZXJ0IHRoZSB0aW1lIHRvIHRoaXMgem9uZQogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5zZXRab25lPWZhbHNlXSAtIG92ZXJyaWRlIHRoZSB6b25lIHdpdGggYSBmaXhlZC1vZmZzZXQgem9uZSBzcGVjaWZpZWQgaW4gdGhlIHN0cmluZyBpdHNlbGYsIGlmIGl0IHNwZWNpZmllcyBvbmUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5sb2NhbGU9J3N5c3RlbSdzIGxvY2FsZSddIC0gYSBsb2NhbGUgdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5vdXRwdXRDYWxlbmRhcl0gLSB0aGUgb3V0cHV0IGNhbGVuZGFyIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubnVtYmVyaW5nU3lzdGVtXSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tSVNPKCcyMDE2LTA1LTI1VDA5OjA4OjM0LjEyMycpCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tSVNPKCcyMDE2LTA1LTI1VDA5OjA4OjM0LjEyMyswNjowMCcpCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tSVNPKCcyMDE2LTA1LTI1VDA5OjA4OjM0LjEyMyswNjowMCcsIHtzZXRab25lOiB0cnVlfSkKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21JU08oJzIwMTYtMDUtMjVUMDk6MDg6MzQuMTIzJywge3pvbmU6ICd1dGMnfSkKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21JU08oJzIwMTYtVzA1LTQnKQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBmcm9tSVNPKHRleHQsIG9wdHMgPSB7fSkgewogICAgICBjb25zdCBbdmFscywgcGFyc2VkWm9uZV0gPSBwYXJzZUlTT0RhdGUodGV4dCk7CiAgICAgIHJldHVybiBwYXJzZURhdGFUb0RhdGVUaW1lKHZhbHMsIHBhcnNlZFpvbmUsIG9wdHMsICJJU08gODYwMSIsIHRleHQpOwogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGEgRGF0ZVRpbWUgZnJvbSBhbiBSRkMgMjgyMiBzdHJpbmcKICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gdGhlIFJGQyAyODIyIHN0cmluZwogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIHRvIGFmZmVjdCB0aGUgY3JlYXRpb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFtvcHRzLnpvbmU9J2xvY2FsJ10gLSBjb252ZXJ0IHRoZSB0aW1lIHRvIHRoaXMgem9uZS4gU2luY2UgdGhlIG9mZnNldCBpcyBhbHdheXMgc3BlY2lmaWVkIGluIHRoZSBzdHJpbmcgaXRzZWxmLCB0aGlzIGhhcyBubyBlZmZlY3Qgb24gdGhlIGludGVycHJldGF0aW9uIG9mIHN0cmluZywgbWVyZWx5IHRoZSB6b25lIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaXMgZXhwcmVzc2VkIGluLgogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5zZXRab25lPWZhbHNlXSAtIG92ZXJyaWRlIHRoZSB6b25lIHdpdGggYSBmaXhlZC1vZmZzZXQgem9uZSBzcGVjaWZpZWQgaW4gdGhlIHN0cmluZyBpdHNlbGYsIGlmIGl0IHNwZWNpZmllcyBvbmUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5sb2NhbGU9J3N5c3RlbSdzIGxvY2FsZSddIC0gYSBsb2NhbGUgdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLm91dHB1dENhbGVuZGFyIC0gdGhlIG91dHB1dCBjYWxlbmRhciB0byBzZXQgb24gdGhlIHJlc3VsdGluZyBEYXRlVGltZSBpbnN0YW5jZQogICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdHMubnVtYmVyaW5nU3lzdGVtIC0gdGhlIG51bWJlcmluZyBzeXN0ZW0gdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21SRkMyODIyKCcyNSBOb3YgMjAxNiAxMzoyMzoxMiBHTVQnKQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbVJGQzI4MjIoJ0ZyaSwgMjUgTm92IDIwMTYgMTM6MjM6MTIgKzA2MDAnKQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbVJGQzI4MjIoJzI1IE5vdiAyMDE2IDEzOjIzIFonKQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBmcm9tUkZDMjgyMih0ZXh0LCBvcHRzID0ge30pIHsKICAgICAgY29uc3QgW3ZhbHMsIHBhcnNlZFpvbmVdID0gcGFyc2VSRkMyODIyRGF0ZSh0ZXh0KTsKICAgICAgcmV0dXJuIHBhcnNlRGF0YVRvRGF0ZVRpbWUodmFscywgcGFyc2VkWm9uZSwgb3B0cywgIlJGQyAyODIyIiwgdGV4dCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEYXRlVGltZSBmcm9tIGFuIEhUVFAgaGVhZGVyIGRhdGUKICAgICAqIEBzZWUgaHR0cHM6Ly93d3cudzMub3JnL1Byb3RvY29scy9yZmMyNjE2L3JmYzI2MTYtc2VjMy5odG1sI3NlYzMuMy4xCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdGV4dCAtIHRoZSBIVFRQIGhlYWRlciBkYXRlCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMgdG8gYWZmZWN0IHRoZSBjcmVhdGlvbgogICAgICogQHBhcmFtIHtzdHJpbmd8Wm9uZX0gW29wdHMuem9uZT0nbG9jYWwnXSAtIGNvbnZlcnQgdGhlIHRpbWUgdG8gdGhpcyB6b25lLiBTaW5jZSBIVFRQIGRhdGVzIGFyZSBhbHdheXMgaW4gVVRDLCB0aGlzIGhhcyBubyBlZmZlY3Qgb24gdGhlIGludGVycHJldGF0aW9uIG9mIHN0cmluZywgbWVyZWx5IHRoZSB6b25lIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaXMgZXhwcmVzc2VkIGluLgogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5zZXRab25lPWZhbHNlXSAtIG92ZXJyaWRlIHRoZSB6b25lIHdpdGggdGhlIGZpeGVkLW9mZnNldCB6b25lIHNwZWNpZmllZCBpbiB0aGUgc3RyaW5nLiBGb3IgSFRUUCBkYXRlcywgdGhpcyBpcyBhbHdheXMgVVRDLCBzbyB0aGlzIG9wdGlvbiBpcyBlcXVpdmFsZW50IHRvIHNldHRpbmcgdGhlIGB6b25lYCBvcHRpb24gdG8gJ3V0YycsIGJ1dCB0aGlzIG9wdGlvbiBpcyBpbmNsdWRlZCBmb3IgY29uc2lzdGVuY3kgd2l0aCBzaW1pbGFyIG1ldGhvZHMuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMubG9jYWxlPSdzeXN0ZW0ncyBsb2NhbGUnXSAtIGEgbG9jYWxlIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0cy5vdXRwdXRDYWxlbmRhciAtIHRoZSBvdXRwdXQgY2FsZW5kYXIgdG8gc2V0IG9uIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUgaW5zdGFuY2UKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLm51bWJlcmluZ1N5c3RlbSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tSFRUUCgnU3VuLCAwNiBOb3YgMTk5NCAwODo0OTozNyBHTVQnKQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbUhUVFAoJ1N1bmRheSwgMDYtTm92LTk0IDA4OjQ5OjM3IEdNVCcpCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tSFRUUCgnU3VuIE5vdiAgNiAwODo0OTozNyAxOTk0JykKICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfQogICAgICovCiAgICBzdGF0aWMgZnJvbUhUVFAodGV4dCwgb3B0cyA9IHt9KSB7CiAgICAgIGNvbnN0IFt2YWxzLCBwYXJzZWRab25lXSA9IHBhcnNlSFRUUERhdGUodGV4dCk7CiAgICAgIHJldHVybiBwYXJzZURhdGFUb0RhdGVUaW1lKHZhbHMsIHBhcnNlZFpvbmUsIG9wdHMsICJIVFRQIiwgb3B0cyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBDcmVhdGUgYSBEYXRlVGltZSBmcm9tIGFuIGlucHV0IHN0cmluZyBhbmQgZm9ybWF0IHN0cmluZy4KICAgICAqIERlZmF1bHRzIHRvIGVuLVVTIGlmIG5vIGxvY2FsZSBoYXMgYmVlbiBzcGVjaWZpZWQsIHJlZ2FyZGxlc3Mgb2YgdGhlIHN5c3RlbSdzIGxvY2FsZS4gRm9yIGEgdGFibGUgb2YgdG9rZW5zIGFuZCB0aGVpciBpbnRlcnByZXRhdGlvbnMsIHNlZSBbaGVyZV0oaHR0cHM6Ly9tb21lbnQuZ2l0aHViLmlvL2x1eG9uLyMvcGFyc2luZz9pZD10YWJsZS1vZi10b2tlbnMpLgogICAgICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSB0aGUgc3RyaW5nIHRvIHBhcnNlCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gZm10IC0gdGhlIGZvcm1hdCB0aGUgc3RyaW5nIGlzIGV4cGVjdGVkIHRvIGJlIGluIChzZWUgdGhlIGxpbmsgYmVsb3cgZm9yIHRoZSBmb3JtYXRzKQogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIHRvIGFmZmVjdCB0aGUgY3JlYXRpb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFtvcHRzLnpvbmU9J2xvY2FsJ10gLSB1c2UgdGhpcyB6b25lIGlmIG5vIG9mZnNldCBpcyBzcGVjaWZpZWQgaW4gdGhlIGlucHV0IHN0cmluZyBpdHNlbGYuIFdpbGwgYWxzbyBjb252ZXJ0IHRoZSBEYXRlVGltZSB0byB0aGlzIHpvbmUKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuc2V0Wm9uZT1mYWxzZV0gLSBvdmVycmlkZSB0aGUgem9uZSB3aXRoIGEgem9uZSBzcGVjaWZpZWQgaW4gdGhlIHN0cmluZyBpdHNlbGYsIGlmIGl0IHNwZWNpZmllcyBvbmUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5sb2NhbGU9J2VuLVVTJ10gLSBhIGxvY2FsZSBzdHJpbmcgdG8gdXNlIHdoZW4gcGFyc2luZy4gV2lsbCBhbHNvIHNldCB0aGUgRGF0ZVRpbWUgdG8gdGhpcyBsb2NhbGUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLm51bWJlcmluZ1N5c3RlbSAtIHRoZSBudW1iZXJpbmcgc3lzdGVtIHRvIHVzZSB3aGVuIHBhcnNpbmcuIFdpbGwgYWxzbyBzZXQgdGhlIHJlc3VsdGluZyBEYXRlVGltZSB0byB0aGlzIG51bWJlcmluZyBzeXN0ZW0KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRzLm91dHB1dENhbGVuZGFyIC0gdGhlIG91dHB1dCBjYWxlbmRhciB0byBzZXQgb24gdGhlIHJlc3VsdGluZyBEYXRlVGltZSBpbnN0YW5jZQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBmcm9tRm9ybWF0KHRleHQsIGZtdCwgb3B0cyA9IHt9KSB7CiAgICAgIGlmIChpc1VuZGVmaW5lZCh0ZXh0KSB8fCBpc1VuZGVmaW5lZChmbXQpKSB7CiAgICAgICAgdGhyb3cgbmV3IEludmFsaWRBcmd1bWVudEVycm9yKCJmcm9tRm9ybWF0IHJlcXVpcmVzIGFuIGlucHV0IHN0cmluZyBhbmQgYSBmb3JtYXQiKTsKICAgICAgfQoKICAgICAgY29uc3QgeyBsb2NhbGUgPSBudWxsLCBudW1iZXJpbmdTeXN0ZW0gPSBudWxsIH0gPSBvcHRzLAogICAgICAgIGxvY2FsZVRvVXNlID0gTG9jYWxlLmZyb21PcHRzKHsKICAgICAgICAgIGxvY2FsZSwKICAgICAgICAgIG51bWJlcmluZ1N5c3RlbSwKICAgICAgICAgIGRlZmF1bHRUb0VOOiB0cnVlLAogICAgICAgIH0pLAogICAgICAgIFt2YWxzLCBwYXJzZWRab25lLCBzcGVjaWZpY09mZnNldCwgaW52YWxpZF0gPSBwYXJzZUZyb21Ub2tlbnMobG9jYWxlVG9Vc2UsIHRleHQsIGZtdCk7CiAgICAgIGlmIChpbnZhbGlkKSB7CiAgICAgICAgcmV0dXJuIERhdGVUaW1lLmludmFsaWQoaW52YWxpZCk7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuIHBhcnNlRGF0YVRvRGF0ZVRpbWUodmFscywgcGFyc2VkWm9uZSwgb3B0cywgYGZvcm1hdCAke2ZtdH1gLCB0ZXh0LCBzcGVjaWZpY09mZnNldCk7CiAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEBkZXByZWNhdGVkIHVzZSBmcm9tRm9ybWF0IGluc3RlYWQKICAgICAqLwogICAgc3RhdGljIGZyb21TdHJpbmcodGV4dCwgZm10LCBvcHRzID0ge30pIHsKICAgICAgcmV0dXJuIERhdGVUaW1lLmZyb21Gb3JtYXQodGV4dCwgZm10LCBvcHRzKTsKICAgIH0KCiAgICAvKioKICAgICAqIENyZWF0ZSBhIERhdGVUaW1lIGZyb20gYSBTUUwgZGF0ZSwgdGltZSwgb3IgZGF0ZXRpbWUKICAgICAqIERlZmF1bHRzIHRvIGVuLVVTIGlmIG5vIGxvY2FsZSBoYXMgYmVlbiBzcGVjaWZpZWQsIHJlZ2FyZGxlc3Mgb2YgdGhlIHN5c3RlbSdzIGxvY2FsZQogICAgICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSB0aGUgc3RyaW5nIHRvIHBhcnNlCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMgdG8gYWZmZWN0IHRoZSBjcmVhdGlvbgogICAgICogQHBhcmFtIHtzdHJpbmd8Wm9uZX0gW29wdHMuem9uZT0nbG9jYWwnXSAtIHVzZSB0aGlzIHpvbmUgaWYgbm8gb2Zmc2V0IGlzIHNwZWNpZmllZCBpbiB0aGUgaW5wdXQgc3RyaW5nIGl0c2VsZi4gV2lsbCBhbHNvIGNvbnZlcnQgdGhlIERhdGVUaW1lIHRvIHRoaXMgem9uZQogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5zZXRab25lPWZhbHNlXSAtIG92ZXJyaWRlIHRoZSB6b25lIHdpdGggYSB6b25lIHNwZWNpZmllZCBpbiB0aGUgc3RyaW5nIGl0c2VsZiwgaWYgaXQgc3BlY2lmaWVzIG9uZQogICAgICogQHBhcmFtIHtzdHJpbmd9IFtvcHRzLmxvY2FsZT0nZW4tVVMnXSAtIGEgbG9jYWxlIHN0cmluZyB0byB1c2Ugd2hlbiBwYXJzaW5nLiBXaWxsIGFsc28gc2V0IHRoZSBEYXRlVGltZSB0byB0aGlzIGxvY2FsZQogICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdHMubnVtYmVyaW5nU3lzdGVtIC0gdGhlIG51bWJlcmluZyBzeXN0ZW0gdG8gdXNlIHdoZW4gcGFyc2luZy4gV2lsbCBhbHNvIHNldCB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIHRvIHRoaXMgbnVtYmVyaW5nIHN5c3RlbQogICAgICogQHBhcmFtIHtzdHJpbmd9IG9wdHMub3V0cHV0Q2FsZW5kYXIgLSB0aGUgb3V0cHV0IGNhbGVuZGFyIHRvIHNldCBvbiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lIGluc3RhbmNlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tU1FMKCcyMDE3LTA1LTE1JykKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmZyb21TUUwoJzIwMTctMDUtMTUgMDk6MTI6MzQnKQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbVNRTCgnMjAxNy0wNS0xNSAwOToxMjozNC4zNDInKQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbVNRTCgnMjAxNy0wNS0xNSAwOToxMjozNC4zNDIrMDY6MDAnKQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUuZnJvbVNRTCgnMjAxNy0wNS0xNSAwOToxMjozNC4zNDIgQW1lcmljYS9Mb3NfQW5nZWxlcycpCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tU1FMKCcyMDE3LTA1LTE1IDA5OjEyOjM0LjM0MiBBbWVyaWNhL0xvc19BbmdlbGVzJywgeyBzZXRab25lOiB0cnVlIH0pCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tU1FMKCcyMDE3LTA1LTE1IDA5OjEyOjM0LjM0MicsIHsgem9uZTogJ0FtZXJpY2EvTG9zX0FuZ2VsZXMnIH0pCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5mcm9tU1FMKCcwOToxMjozNC4zNDInKQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBmcm9tU1FMKHRleHQsIG9wdHMgPSB7fSkgewogICAgICBjb25zdCBbdmFscywgcGFyc2VkWm9uZV0gPSBwYXJzZVNRTCh0ZXh0KTsKICAgICAgcmV0dXJuIHBhcnNlRGF0YVRvRGF0ZVRpbWUodmFscywgcGFyc2VkWm9uZSwgb3B0cywgIlNRTCIsIHRleHQpOwogICAgfQoKICAgIC8qKgogICAgICogQ3JlYXRlIGFuIGludmFsaWQgRGF0ZVRpbWUuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gcmVhc29uIC0gc2ltcGxlIHN0cmluZyBvZiB3aHkgdGhpcyBEYXRlVGltZSBpcyBpbnZhbGlkLiBTaG91bGQgbm90IGNvbnRhaW4gcGFyYW1ldGVycyBvciBhbnl0aGluZyBlbHNlIGRhdGEtZGVwZW5kZW50LgogICAgICogQHBhcmFtIHtzdHJpbmd9IFtleHBsYW5hdGlvbj1udWxsXSAtIGxvbmdlciBleHBsYW5hdGlvbiwgbWF5IGluY2x1ZGUgcGFyYW1ldGVycyBhbmQgb3RoZXIgdXNlZnVsIGRlYnVnZ2luZyBpbmZvcm1hdGlvbgogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXRpYyBpbnZhbGlkKHJlYXNvbiwgZXhwbGFuYXRpb24gPSBudWxsKSB7CiAgICAgIGlmICghcmVhc29uKSB7CiAgICAgICAgdGhyb3cgbmV3IEludmFsaWRBcmd1bWVudEVycm9yKCJuZWVkIHRvIHNwZWNpZnkgYSByZWFzb24gdGhlIERhdGVUaW1lIGlzIGludmFsaWQiKTsKICAgICAgfQoKICAgICAgY29uc3QgaW52YWxpZCA9IHJlYXNvbiBpbnN0YW5jZW9mIEludmFsaWQgPyByZWFzb24gOiBuZXcgSW52YWxpZChyZWFzb24sIGV4cGxhbmF0aW9uKTsKCiAgICAgIGlmIChTZXR0aW5ncy50aHJvd09uSW52YWxpZCkgewogICAgICAgIHRocm93IG5ldyBJbnZhbGlkRGF0ZVRpbWVFcnJvcihpbnZhbGlkKTsKICAgICAgfSBlbHNlIHsKICAgICAgICByZXR1cm4gbmV3IERhdGVUaW1lKHsgaW52YWxpZCB9KTsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogQ2hlY2sgaWYgYW4gb2JqZWN0IGlzIGFuIGluc3RhbmNlIG9mIERhdGVUaW1lLiBXb3JrcyBhY3Jvc3MgY29udGV4dCBib3VuZGFyaWVzCiAgICAgKiBAcGFyYW0ge29iamVjdH0gbwogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgc3RhdGljIGlzRGF0ZVRpbWUobykgewogICAgICByZXR1cm4gKG8gJiYgby5pc0x1eG9uRGF0ZVRpbWUpIHx8IGZhbHNlOwogICAgfQoKICAgIC8qKgogICAgICogUHJvZHVjZSB0aGUgZm9ybWF0IHN0cmluZyBmb3IgYSBzZXQgb2Ygb3B0aW9ucwogICAgICogQHBhcmFtIGZvcm1hdE9wdHMKICAgICAqIEBwYXJhbSBsb2NhbGVPcHRzCiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfQogICAgICovCiAgICBzdGF0aWMgcGFyc2VGb3JtYXRGb3JPcHRzKGZvcm1hdE9wdHMsIGxvY2FsZU9wdHMgPSB7fSkgewogICAgICBjb25zdCB0b2tlbkxpc3QgPSBmb3JtYXRPcHRzVG9Ub2tlbnMoZm9ybWF0T3B0cywgTG9jYWxlLmZyb21PYmplY3QobG9jYWxlT3B0cykpOwogICAgICByZXR1cm4gIXRva2VuTGlzdCA/IG51bGwgOiB0b2tlbkxpc3QubWFwKCh0KSA9PiAodCA/IHQudmFsIDogbnVsbCkpLmpvaW4oIiIpOwogICAgfQoKICAgIC8qKgogICAgICogUHJvZHVjZSB0aGUgdGhlIGZ1bGx5IGV4cGFuZGVkIGZvcm1hdCB0b2tlbiBmb3IgdGhlIGxvY2FsZQogICAgICogRG9lcyBOT1QgcXVvdGUgY2hhcmFjdGVycywgc28gcXVvdGVkIHRva2VucyB3aWxsIG5vdCByb3VuZCB0cmlwIGNvcnJlY3RseQogICAgICogQHBhcmFtIGZtdAogICAgICogQHBhcmFtIGxvY2FsZU9wdHMKICAgICAqIEByZXR1cm5zIHtzdHJpbmd9CiAgICAgKi8KICAgIHN0YXRpYyBleHBhbmRGb3JtYXQoZm10LCBsb2NhbGVPcHRzID0ge30pIHsKICAgICAgY29uc3QgZXhwYW5kZWQgPSBleHBhbmRNYWNyb1Rva2VucyhGb3JtYXR0ZXIucGFyc2VGb3JtYXQoZm10KSwgTG9jYWxlLmZyb21PYmplY3QobG9jYWxlT3B0cykpOwogICAgICByZXR1cm4gZXhwYW5kZWQubWFwKCh0KSA9PiB0LnZhbCkuam9pbigiIik7CiAgICB9CgogICAgLy8gSU5GTwoKICAgIC8qKgogICAgICogR2V0IHRoZSB2YWx1ZSBvZiB1bml0LgogICAgICogQHBhcmFtIHtzdHJpbmd9IHVuaXQgLSBhIHVuaXQgc3VjaCBhcyAnbWludXRlJyBvciAnZGF5JwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgNywgNCkuZ2V0KCdtb250aCcpOyAvLz0+IDcKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDcsIDQpLmdldCgnZGF5Jyk7IC8vPT4gNAogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICBnZXQodW5pdCkgewogICAgICByZXR1cm4gdGhpc1t1bml0XTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgd2hldGhlciB0aGUgRGF0ZVRpbWUgaXMgdmFsaWQuIEludmFsaWQgRGF0ZVRpbWVzIG9jY3VyIHdoZW46CiAgICAgKiAqIFRoZSBEYXRlVGltZSB3YXMgY3JlYXRlZCBmcm9tIGludmFsaWQgY2FsZW5kYXIgaW5mb3JtYXRpb24sIHN1Y2ggYXMgdGhlIDEzdGggbW9udGggb3IgRmVicnVhcnkgMzAKICAgICAqICogVGhlIERhdGVUaW1lIHdhcyBjcmVhdGVkIGJ5IGFuIG9wZXJhdGlvbiBvbiBhbm90aGVyIGludmFsaWQgZGF0ZQogICAgICogQHR5cGUge2Jvb2xlYW59CiAgICAgKi8KICAgIGdldCBpc1ZhbGlkKCkgewogICAgICByZXR1cm4gdGhpcy5pbnZhbGlkID09PSBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBlcnJvciBjb2RlIGlmIHRoaXMgRGF0ZVRpbWUgaXMgaW52YWxpZCwgb3IgbnVsbCBpZiB0aGUgRGF0ZVRpbWUgaXMgdmFsaWQKICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCBpbnZhbGlkUmVhc29uKCkgewogICAgICByZXR1cm4gdGhpcy5pbnZhbGlkID8gdGhpcy5pbnZhbGlkLnJlYXNvbiA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIGV4cGxhbmF0aW9uIG9mIHdoeSB0aGlzIERhdGVUaW1lIGJlY2FtZSBpbnZhbGlkLCBvciBudWxsIGlmIHRoZSBEYXRlVGltZSBpcyB2YWxpZAogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IGludmFsaWRFeHBsYW5hdGlvbigpIHsKICAgICAgcmV0dXJuIHRoaXMuaW52YWxpZCA/IHRoaXMuaW52YWxpZC5leHBsYW5hdGlvbiA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIGxvY2FsZSBvZiBhIERhdGVUaW1lLCBzdWNoICdlbi1HQicuIFRoZSBsb2NhbGUgaXMgdXNlZCB3aGVuIGZvcm1hdHRpbmcgdGhlIERhdGVUaW1lCiAgICAgKgogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IGxvY2FsZSgpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMubG9jLmxvY2FsZSA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIG51bWJlcmluZyBzeXN0ZW0gb2YgYSBEYXRlVGltZSwgc3VjaCAnYmVuZycuIFRoZSBudW1iZXJpbmcgc3lzdGVtIGlzIHVzZWQgd2hlbiBmb3JtYXR0aW5nIHRoZSBEYXRlVGltZQogICAgICoKICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCBudW1iZXJpbmdTeXN0ZW0oKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLmxvYy5udW1iZXJpbmdTeXN0ZW0gOiBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBvdXRwdXQgY2FsZW5kYXIgb2YgYSBEYXRlVGltZSwgc3VjaCAnaXNsYW1pYycuIFRoZSBvdXRwdXQgY2FsZW5kYXIgaXMgdXNlZCB3aGVuIGZvcm1hdHRpbmcgdGhlIERhdGVUaW1lCiAgICAgKgogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IG91dHB1dENhbGVuZGFyKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy5sb2Mub3V0cHV0Q2FsZW5kYXIgOiBudWxsOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSB0aW1lIHpvbmUgYXNzb2NpYXRlZCB3aXRoIHRoaXMgRGF0ZVRpbWUuCiAgICAgKiBAdHlwZSB7Wm9uZX0KICAgICAqLwogICAgZ2V0IHpvbmUoKSB7CiAgICAgIHJldHVybiB0aGlzLl96b25lOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBuYW1lIG9mIHRoZSB0aW1lIHpvbmUuCiAgICAgKiBAdHlwZSB7c3RyaW5nfQogICAgICovCiAgICBnZXQgem9uZU5hbWUoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLnpvbmUubmFtZSA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIHllYXIKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDUsIDI1KS55ZWFyIC8vPT4gMjAxNwogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IHllYXIoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLmMueWVhciA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgcXVhcnRlcgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgNSwgMjUpLnF1YXJ0ZXIgLy89PiAyCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgcXVhcnRlcigpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IE1hdGguY2VpbCh0aGlzLmMubW9udGggLyAzKSA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgbW9udGggKDEtMTIpLgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgNSwgMjUpLm1vbnRoIC8vPT4gNQogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IG1vbnRoKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy5jLm1vbnRoIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBkYXkgb2YgdGhlIG1vbnRoICgxLTMwaXNoKS4KICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDUsIDI1KS5kYXkgLy89PiAyNQogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IGRheSgpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMuYy5kYXkgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIGhvdXIgb2YgdGhlIGRheSAoMC0yMykuCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE3LCA1LCAyNSwgOSkuaG91ciAvLz0+IDkKICAgICAqIEB0eXBlIHtudW1iZXJ9CiAgICAgKi8KICAgIGdldCBob3VyKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy5jLmhvdXIgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIG1pbnV0ZSBvZiB0aGUgaG91ciAoMC01OSkuCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE3LCA1LCAyNSwgOSwgMzApLm1pbnV0ZSAvLz0+IDMwCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgbWludXRlKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gdGhpcy5jLm1pbnV0ZSA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgc2Vjb25kIG9mIHRoZSBtaW51dGUgKDAtNTkpLgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgNSwgMjUsIDksIDMwLCA1Mikuc2Vjb25kIC8vPT4gNTIKICAgICAqIEB0eXBlIHtudW1iZXJ9CiAgICAgKi8KICAgIGdldCBzZWNvbmQoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLmMuc2Vjb25kIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBtaWxsaXNlY29uZCBvZiB0aGUgc2Vjb25kICgwLTk5OSkuCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE3LCA1LCAyNSwgOSwgMzAsIDUyLCA2NTQpLm1pbGxpc2Vjb25kIC8vPT4gNjU0CiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgbWlsbGlzZWNvbmQoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLmMubWlsbGlzZWNvbmQgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIHdlZWsgeWVhcgogICAgICogQHNlZSBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fd2Vla19kYXRlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCAxMiwgMzEpLndlZWtZZWFyIC8vPT4gMjAxNQogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IHdlZWtZZWFyKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gcG9zc2libHlDYWNoZWRXZWVrRGF0YSh0aGlzKS53ZWVrWWVhciA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgd2VlayBudW1iZXIgb2YgdGhlIHdlZWsgeWVhciAoMS01MmlzaCkuCiAgICAgKiBAc2VlIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT193ZWVrX2RhdGUKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDUsIDI1KS53ZWVrTnVtYmVyIC8vPT4gMjEKICAgICAqIEB0eXBlIHtudW1iZXJ9CiAgICAgKi8KICAgIGdldCB3ZWVrTnVtYmVyKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gcG9zc2libHlDYWNoZWRXZWVrRGF0YSh0aGlzKS53ZWVrTnVtYmVyIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBkYXkgb2YgdGhlIHdlZWsuCiAgICAgKiAxIGlzIE1vbmRheSBhbmQgNyBpcyBTdW5kYXkKICAgICAqIEBzZWUgaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSVNPX3dlZWtfZGF0ZQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNCwgMTEsIDMxKS53ZWVrZGF5IC8vPT4gNAogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IHdlZWtkYXkoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyBwb3NzaWJseUNhY2hlZFdlZWtEYXRhKHRoaXMpLndlZWtkYXkgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIG9yZGluYWwgKG1lYW5pbmcgdGhlIGRheSBvZiB0aGUgeWVhcikKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDUsIDI1KS5vcmRpbmFsIC8vPT4gMTQ1CiAgICAgKiBAdHlwZSB7bnVtYmVyfERhdGVUaW1lfQogICAgICovCiAgICBnZXQgb3JkaW5hbCgpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IGdyZWdvcmlhblRvT3JkaW5hbCh0aGlzLmMpLm9yZGluYWwgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIGh1bWFuIHJlYWRhYmxlIHNob3J0IG1vbnRoIG5hbWUsIHN1Y2ggYXMgJ09jdCcuCiAgICAgKiBEZWZhdWx0cyB0byB0aGUgc3lzdGVtJ3MgbG9jYWxlIGlmIG5vIGxvY2FsZSBoYXMgYmVlbiBzcGVjaWZpZWQKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDEwLCAzMCkubW9udGhTaG9ydCAvLz0+IE9jdAogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IG1vbnRoU2hvcnQoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyBJbmZvLm1vbnRocygic2hvcnQiLCB7IGxvY09iajogdGhpcy5sb2MgfSlbdGhpcy5tb250aCAtIDFdIDogbnVsbDsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgaHVtYW4gcmVhZGFibGUgbG9uZyBtb250aCBuYW1lLCBzdWNoIGFzICdPY3RvYmVyJy4KICAgICAqIERlZmF1bHRzIHRvIHRoZSBzeXN0ZW0ncyBsb2NhbGUgaWYgbm8gbG9jYWxlIGhhcyBiZWVuIHNwZWNpZmllZAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgMTAsIDMwKS5tb250aExvbmcgLy89PiBPY3RvYmVyCiAgICAgKiBAdHlwZSB7c3RyaW5nfQogICAgICovCiAgICBnZXQgbW9udGhMb25nKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gSW5mby5tb250aHMoImxvbmciLCB7IGxvY09iajogdGhpcy5sb2MgfSlbdGhpcy5tb250aCAtIDFdIDogbnVsbDsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgaHVtYW4gcmVhZGFibGUgc2hvcnQgd2Vla2RheSwgc3VjaCBhcyAnTW9uJy4KICAgICAqIERlZmF1bHRzIHRvIHRoZSBzeXN0ZW0ncyBsb2NhbGUgaWYgbm8gbG9jYWxlIGhhcyBiZWVuIHNwZWNpZmllZAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgMTAsIDMwKS53ZWVrZGF5U2hvcnQgLy89PiBNb24KICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCB3ZWVrZGF5U2hvcnQoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyBJbmZvLndlZWtkYXlzKCJzaG9ydCIsIHsgbG9jT2JqOiB0aGlzLmxvYyB9KVt0aGlzLndlZWtkYXkgLSAxXSA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIGh1bWFuIHJlYWRhYmxlIGxvbmcgd2Vla2RheSwgc3VjaCBhcyAnTW9uZGF5Jy4KICAgICAqIERlZmF1bHRzIHRvIHRoZSBzeXN0ZW0ncyBsb2NhbGUgaWYgbm8gbG9jYWxlIGhhcyBiZWVuIHNwZWNpZmllZAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgMTAsIDMwKS53ZWVrZGF5TG9uZyAvLz0+IE1vbmRheQogICAgICogQHR5cGUge3N0cmluZ30KICAgICAqLwogICAgZ2V0IHdlZWtkYXlMb25nKCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gSW5mby53ZWVrZGF5cygibG9uZyIsIHsgbG9jT2JqOiB0aGlzLmxvYyB9KVt0aGlzLndlZWtkYXkgLSAxXSA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgdGhlIFVUQyBvZmZzZXQgb2YgdGhpcyBEYXRlVGltZSBpbiBtaW51dGVzCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5vZmZzZXQgLy89PiAtMjQwCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS51dGMoKS5vZmZzZXQgLy89PiAwCiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgb2Zmc2V0KCkgewogICAgICByZXR1cm4gdGhpcy5pc1ZhbGlkID8gK3RoaXMubyA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aGUgc2hvcnQgaHVtYW4gbmFtZSBmb3IgdGhlIHpvbmUncyBjdXJyZW50IG9mZnNldCwgZm9yIGV4YW1wbGUgIkVTVCIgb3IgIkVEVCIuCiAgICAgKiBEZWZhdWx0cyB0byB0aGUgc3lzdGVtJ3MgbG9jYWxlIGlmIG5vIGxvY2FsZSBoYXMgYmVlbiBzcGVjaWZpZWQKICAgICAqIEB0eXBlIHtzdHJpbmd9CiAgICAgKi8KICAgIGdldCBvZmZzZXROYW1lU2hvcnQoKSB7CiAgICAgIGlmICh0aGlzLmlzVmFsaWQpIHsKICAgICAgICByZXR1cm4gdGhpcy56b25lLm9mZnNldE5hbWUodGhpcy50cywgewogICAgICAgICAgZm9ybWF0OiAic2hvcnQiLAogICAgICAgICAgbG9jYWxlOiB0aGlzLmxvY2FsZSwKICAgICAgICB9KTsKICAgICAgfSBlbHNlIHsKICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgfQogICAgfQoKICAgIC8qKgogICAgICogR2V0IHRoZSBsb25nIGh1bWFuIG5hbWUgZm9yIHRoZSB6b25lJ3MgY3VycmVudCBvZmZzZXQsIGZvciBleGFtcGxlICJFYXN0ZXJuIFN0YW5kYXJkIFRpbWUiIG9yICJFYXN0ZXJuIERheWxpZ2h0IFRpbWUiLgogICAgICogRGVmYXVsdHMgdG8gdGhlIHN5c3RlbSdzIGxvY2FsZSBpZiBubyBsb2NhbGUgaGFzIGJlZW4gc3BlY2lmaWVkCiAgICAgKiBAdHlwZSB7c3RyaW5nfQogICAgICovCiAgICBnZXQgb2Zmc2V0TmFtZUxvbmcoKSB7CiAgICAgIGlmICh0aGlzLmlzVmFsaWQpIHsKICAgICAgICByZXR1cm4gdGhpcy56b25lLm9mZnNldE5hbWUodGhpcy50cywgewogICAgICAgICAgZm9ybWF0OiAibG9uZyIsCiAgICAgICAgICBsb2NhbGU6IHRoaXMubG9jYWxlLAogICAgICAgIH0pOwogICAgICB9IGVsc2UgewogICAgICAgIHJldHVybiBudWxsOwogICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgd2hldGhlciB0aGlzIHpvbmUncyBvZmZzZXQgZXZlciBjaGFuZ2VzLCBhcyBpbiBhIERTVC4KICAgICAqIEB0eXBlIHtib29sZWFufQogICAgICovCiAgICBnZXQgaXNPZmZzZXRGaXhlZCgpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMuem9uZS5pc1VuaXZlcnNhbCA6IG51bGw7CiAgICB9CgogICAgLyoqCiAgICAgKiBHZXQgd2hldGhlciB0aGUgRGF0ZVRpbWUgaXMgaW4gYSBEU1QuCiAgICAgKiBAdHlwZSB7Ym9vbGVhbn0KICAgICAqLwogICAgZ2V0IGlzSW5EU1QoKSB7CiAgICAgIGlmICh0aGlzLmlzT2Zmc2V0Rml4ZWQpIHsKICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgIH0gZWxzZSB7CiAgICAgICAgcmV0dXJuICgKICAgICAgICAgIHRoaXMub2Zmc2V0ID4gdGhpcy5zZXQoeyBtb250aDogMSwgZGF5OiAxIH0pLm9mZnNldCB8fAogICAgICAgICAgdGhpcy5vZmZzZXQgPiB0aGlzLnNldCh7IG1vbnRoOiA1IH0pLm9mZnNldAogICAgICAgICk7CiAgICAgIH0KICAgIH0KCiAgICAvKioKICAgICAqIEdldCB0aG9zZSBEYXRlVGltZXMgd2hpY2ggaGF2ZSB0aGUgc2FtZSBsb2NhbCB0aW1lIGFzIHRoaXMgRGF0ZVRpbWUsIGJ1dCBhIGRpZmZlcmVudCBvZmZzZXQgZnJvbSBVVEMKICAgICAqIGluIHRoaXMgRGF0ZVRpbWUncyB6b25lLiBEdXJpbmcgRFNUIGNoYW5nZXMgbG9jYWwgdGltZSBjYW4gYmUgYW1iaWd1b3VzLCBmb3IgZXhhbXBsZQogICAgICogYDIwMjMtMTAtMjlUMDI6MzA6MDBgIGluIGBFdXJvcGUvQmVybGluYCBjYW4gaGF2ZSBvZmZzZXQgYCswMTowMGAgb3IgYCswMjowMGAuCiAgICAgKiBUaGlzIG1ldGhvZCB3aWxsIHJldHVybiBib3RoIHBvc3NpYmxlIERhdGVUaW1lcyBpZiB0aGlzIERhdGVUaW1lJ3MgbG9jYWwgdGltZSBpcyBhbWJpZ3VvdXMuCiAgICAgKiBAcmV0dXJucyB7RGF0ZVRpbWVbXX0KICAgICAqLwogICAgZ2V0UG9zc2libGVPZmZzZXRzKCkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCB8fCB0aGlzLmlzT2Zmc2V0Rml4ZWQpIHsKICAgICAgICByZXR1cm4gW3RoaXNdOwogICAgICB9CiAgICAgIGNvbnN0IGRheU1zID0gODY0MDAwMDA7CiAgICAgIGNvbnN0IG1pbnV0ZU1zID0gNjAwMDA7CiAgICAgIGNvbnN0IGxvY2FsVFMgPSBvYmpUb0xvY2FsVFModGhpcy5jKTsKICAgICAgY29uc3Qgb0VhcmxpZXIgPSB0aGlzLnpvbmUub2Zmc2V0KGxvY2FsVFMgLSBkYXlNcyk7CiAgICAgIGNvbnN0IG9MYXRlciA9IHRoaXMuem9uZS5vZmZzZXQobG9jYWxUUyArIGRheU1zKTsKCiAgICAgIGNvbnN0IG8xID0gdGhpcy56b25lLm9mZnNldChsb2NhbFRTIC0gb0VhcmxpZXIgKiBtaW51dGVNcyk7CiAgICAgIGNvbnN0IG8yID0gdGhpcy56b25lLm9mZnNldChsb2NhbFRTIC0gb0xhdGVyICogbWludXRlTXMpOwogICAgICBpZiAobzEgPT09IG8yKSB7CiAgICAgICAgcmV0dXJuIFt0aGlzXTsKICAgICAgfQogICAgICBjb25zdCB0czEgPSBsb2NhbFRTIC0gbzEgKiBtaW51dGVNczsKICAgICAgY29uc3QgdHMyID0gbG9jYWxUUyAtIG8yICogbWludXRlTXM7CiAgICAgIGNvbnN0IGMxID0gdHNUb09iaih0czEsIG8xKTsKICAgICAgY29uc3QgYzIgPSB0c1RvT2JqKHRzMiwgbzIpOwogICAgICBpZiAoCiAgICAgICAgYzEuaG91ciA9PT0gYzIuaG91ciAmJgogICAgICAgIGMxLm1pbnV0ZSA9PT0gYzIubWludXRlICYmCiAgICAgICAgYzEuc2Vjb25kID09PSBjMi5zZWNvbmQgJiYKICAgICAgICBjMS5taWxsaXNlY29uZCA9PT0gYzIubWlsbGlzZWNvbmQKICAgICAgKSB7CiAgICAgICAgcmV0dXJuIFtjbG9uZSh0aGlzLCB7IHRzOiB0czEgfSksIGNsb25lKHRoaXMsIHsgdHM6IHRzMiB9KV07CiAgICAgIH0KICAgICAgcmV0dXJuIFt0aGlzXTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgdHJ1ZSBpZiB0aGlzIERhdGVUaW1lIGlzIGluIGEgbGVhcCB5ZWFyLCBmYWxzZSBvdGhlcndpc2UKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTYpLmlzSW5MZWFwWWVhciAvLz0+IHRydWUKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTMpLmlzSW5MZWFwWWVhciAvLz0+IGZhbHNlCiAgICAgKiBAdHlwZSB7Ym9vbGVhbn0KICAgICAqLwogICAgZ2V0IGlzSW5MZWFwWWVhcigpIHsKICAgICAgcmV0dXJuIGlzTGVhcFllYXIodGhpcy55ZWFyKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgdGhlIG51bWJlciBvZiBkYXlzIGluIHRoaXMgRGF0ZVRpbWUncyBtb250aAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNiwgMikuZGF5c0luTW9udGggLy89PiAyOQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNiwgMykuZGF5c0luTW9udGggLy89PiAzMQogICAgICogQHR5cGUge251bWJlcn0KICAgICAqLwogICAgZ2V0IGRheXNJbk1vbnRoKCkgewogICAgICByZXR1cm4gZGF5c0luTW9udGgodGhpcy55ZWFyLCB0aGlzLm1vbnRoKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgdGhlIG51bWJlciBvZiBkYXlzIGluIHRoaXMgRGF0ZVRpbWUncyB5ZWFyCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE2KS5kYXlzSW5ZZWFyIC8vPT4gMzY2CiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDEzKS5kYXlzSW5ZZWFyIC8vPT4gMzY1CiAgICAgKiBAdHlwZSB7bnVtYmVyfQogICAgICovCiAgICBnZXQgZGF5c0luWWVhcigpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IGRheXNJblllYXIodGhpcy55ZWFyKSA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgdGhlIG51bWJlciBvZiB3ZWVrcyBpbiB0aGlzIERhdGVUaW1lJ3MgeWVhcgogICAgICogQHNlZSBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9JU09fd2Vla19kYXRlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDA0KS53ZWVrc0luV2Vla1llYXIgLy89PiA1MwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxMykud2Vla3NJbldlZWtZZWFyIC8vPT4gNTIKICAgICAqIEB0eXBlIHtudW1iZXJ9CiAgICAgKi8KICAgIGdldCB3ZWVrc0luV2Vla1llYXIoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB3ZWVrc0luV2Vla1llYXIodGhpcy53ZWVrWWVhcikgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHRoZSByZXNvbHZlZCBJbnRsIG9wdGlvbnMgZm9yIHRoaXMgRGF0ZVRpbWUuCiAgICAgKiBUaGlzIGlzIHVzZWZ1bCBpbiB1bmRlcnN0YW5kaW5nIHRoZSBiZWhhdmlvciBvZiBmb3JtYXR0aW5nIG1ldGhvZHMKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRzIC0gdGhlIHNhbWUgb3B0aW9ucyBhcyB0b0xvY2FsZVN0cmluZwogICAgICogQHJldHVybiB7T2JqZWN0fQogICAgICovCiAgICByZXNvbHZlZExvY2FsZU9wdGlvbnMob3B0cyA9IHt9KSB7CiAgICAgIGNvbnN0IHsgbG9jYWxlLCBudW1iZXJpbmdTeXN0ZW0sIGNhbGVuZGFyIH0gPSBGb3JtYXR0ZXIuY3JlYXRlKAogICAgICAgIHRoaXMubG9jLmNsb25lKG9wdHMpLAogICAgICAgIG9wdHMKICAgICAgKS5yZXNvbHZlZE9wdGlvbnModGhpcyk7CiAgICAgIHJldHVybiB7IGxvY2FsZSwgbnVtYmVyaW5nU3lzdGVtLCBvdXRwdXRDYWxlbmRhcjogY2FsZW5kYXIgfTsKICAgIH0KCiAgICAvLyBUUkFOU0ZPUk0KCiAgICAvKioKICAgICAqICJTZXQiIHRoZSBEYXRlVGltZSdzIHpvbmUgdG8gVVRDLiBSZXR1cm5zIGEgbmV3bHktY29uc3RydWN0ZWQgRGF0ZVRpbWUuCiAgICAgKgogICAgICogRXF1aXZhbGVudCB0byB7QGxpbmsgRGF0ZVRpbWUjc2V0Wm9uZX0oJ3V0YycpCiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29mZnNldD0wXSAtIG9wdGlvbmFsbHksIGFuIG9mZnNldCBmcm9tIFVUQyBpbiBtaW51dGVzCiAgICAgKiBAcGFyYW0ge09iamVjdH0gW29wdHM9e31dIC0gb3B0aW9ucyB0byBwYXNzIHRvIGBzZXRab25lKClgCiAgICAgKiBAcmV0dXJuIHtEYXRlVGltZX0KICAgICAqLwogICAgdG9VVEMob2Zmc2V0ID0gMCwgb3B0cyA9IHt9KSB7CiAgICAgIHJldHVybiB0aGlzLnNldFpvbmUoRml4ZWRPZmZzZXRab25lLmluc3RhbmNlKG9mZnNldCksIG9wdHMpOwogICAgfQoKICAgIC8qKgogICAgICogIlNldCIgdGhlIERhdGVUaW1lJ3Mgem9uZSB0byB0aGUgaG9zdCdzIGxvY2FsIHpvbmUuIFJldHVybnMgYSBuZXdseS1jb25zdHJ1Y3RlZCBEYXRlVGltZS4KICAgICAqCiAgICAgKiBFcXVpdmFsZW50IHRvIGBzZXRab25lKCdsb2NhbCcpYAogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHRvTG9jYWwoKSB7CiAgICAgIHJldHVybiB0aGlzLnNldFpvbmUoU2V0dGluZ3MuZGVmYXVsdFpvbmUpOwogICAgfQoKICAgIC8qKgogICAgICogIlNldCIgdGhlIERhdGVUaW1lJ3Mgem9uZSB0byBzcGVjaWZpZWQgem9uZS4gUmV0dXJucyBhIG5ld2x5LWNvbnN0cnVjdGVkIERhdGVUaW1lLgogICAgICoKICAgICAqIEJ5IGRlZmF1bHQsIHRoZSBzZXR0ZXIga2VlcHMgdGhlIHVuZGVybHlpbmcgdGltZSB0aGUgc2FtZSAoYXMgaW4sIHRoZSBzYW1lIHRpbWVzdGFtcCksIGJ1dCB0aGUgbmV3IGluc3RhbmNlIHdpbGwgcmVwb3J0IGRpZmZlcmVudCBsb2NhbCB0aW1lcyBhbmQgY29uc2lkZXIgRFNUcyB3aGVuIG1ha2luZyBjb21wdXRhdGlvbnMsIGFzIHdpdGgge0BsaW5rIERhdGVUaW1lI3BsdXN9LiBZb3UgbWF5IHdpc2ggdG8gdXNlIHtAbGluayBEYXRlVGltZSN0b0xvY2FsfSBhbmQge0BsaW5rIERhdGVUaW1lI3RvVVRDfSB3aGljaCBwcm92aWRlIHNpbXBsZSBjb252ZW5pZW5jZSB3cmFwcGVycyBmb3IgY29tbW9ubHkgdXNlZCB6b25lcy4KICAgICAqIEBwYXJhbSB7c3RyaW5nfFpvbmV9IFt6b25lPSdsb2NhbCddIC0gYSB6b25lIGlkZW50aWZpZXIuIEFzIGEgc3RyaW5nLCB0aGF0IGNhbiBiZSBhbnkgSUFOQSB6b25lIHN1cHBvcnRlZCBieSB0aGUgaG9zdCBlbnZpcm9ubWVudCwgb3IgYSBmaXhlZC1vZmZzZXQgbmFtZSBvZiB0aGUgZm9ybSAnVVRDKzMnLCBvciB0aGUgc3RyaW5ncyAnbG9jYWwnIG9yICd1dGMnLiBZb3UgbWF5IGFsc28gc3VwcGx5IGFuIGluc3RhbmNlIG9mIGEge0BsaW5rIERhdGVUaW1lI1pvbmV9IGNsYXNzLgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmtlZXBMb2NhbFRpbWU9ZmFsc2VdIC0gSWYgdHJ1ZSwgYWRqdXN0IHRoZSB1bmRlcmx5aW5nIHRpbWUgc28gdGhhdCB0aGUgbG9jYWwgdGltZSBzdGF5cyB0aGUgc2FtZSwgYnV0IGluIHRoZSB0YXJnZXQgem9uZS4gWW91IHNob3VsZCByYXJlbHkgbmVlZCB0aGlzLgogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHNldFpvbmUoem9uZSwgeyBrZWVwTG9jYWxUaW1lID0gZmFsc2UsIGtlZXBDYWxlbmRhclRpbWUgPSBmYWxzZSB9ID0ge30pIHsKICAgICAgem9uZSA9IG5vcm1hbGl6ZVpvbmUoem9uZSwgU2V0dGluZ3MuZGVmYXVsdFpvbmUpOwogICAgICBpZiAoem9uZS5lcXVhbHModGhpcy56b25lKSkgewogICAgICAgIHJldHVybiB0aGlzOwogICAgICB9IGVsc2UgaWYgKCF6b25lLmlzVmFsaWQpIHsKICAgICAgICByZXR1cm4gRGF0ZVRpbWUuaW52YWxpZCh1bnN1cHBvcnRlZFpvbmUoem9uZSkpOwogICAgICB9IGVsc2UgewogICAgICAgIGxldCBuZXdUUyA9IHRoaXMudHM7CiAgICAgICAgaWYgKGtlZXBMb2NhbFRpbWUgfHwga2VlcENhbGVuZGFyVGltZSkgewogICAgICAgICAgY29uc3Qgb2Zmc2V0R3Vlc3MgPSB6b25lLm9mZnNldCh0aGlzLnRzKTsKICAgICAgICAgIGNvbnN0IGFzT2JqID0gdGhpcy50b09iamVjdCgpOwogICAgICAgICAgW25ld1RTXSA9IG9ialRvVFMoYXNPYmosIG9mZnNldEd1ZXNzLCB6b25lKTsKICAgICAgICB9CiAgICAgICAgcmV0dXJuIGNsb25lKHRoaXMsIHsgdHM6IG5ld1RTLCB6b25lIH0pOwogICAgICB9CiAgICB9CgogICAgLyoqCiAgICAgKiAiU2V0IiB0aGUgbG9jYWxlLCBudW1iZXJpbmdTeXN0ZW0sIG9yIG91dHB1dENhbGVuZGFyLiBSZXR1cm5zIGEgbmV3bHktY29uc3RydWN0ZWQgRGF0ZVRpbWUuCiAgICAgKiBAcGFyYW0ge09iamVjdH0gcHJvcGVydGllcyAtIHRoZSBwcm9wZXJ0aWVzIHRvIHNldAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNywgNSwgMjUpLnJlY29uZmlndXJlKHsgbG9jYWxlOiAnZW4tR0InIH0pCiAgICAgKiBAcmV0dXJuIHtEYXRlVGltZX0KICAgICAqLwogICAgcmVjb25maWd1cmUoeyBsb2NhbGUsIG51bWJlcmluZ1N5c3RlbSwgb3V0cHV0Q2FsZW5kYXIgfSA9IHt9KSB7CiAgICAgIGNvbnN0IGxvYyA9IHRoaXMubG9jLmNsb25lKHsgbG9jYWxlLCBudW1iZXJpbmdTeXN0ZW0sIG91dHB1dENhbGVuZGFyIH0pOwogICAgICByZXR1cm4gY2xvbmUodGhpcywgeyBsb2MgfSk7CiAgICB9CgogICAgLyoqCiAgICAgKiAiU2V0IiB0aGUgbG9jYWxlLiBSZXR1cm5zIGEgbmV3bHktY29uc3RydWN0ZWQgRGF0ZVRpbWUuCiAgICAgKiBKdXN0IGEgY29udmVuaWVudCBhbGlhcyBmb3IgcmVjb25maWd1cmUoeyBsb2NhbGUgfSkKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTcsIDUsIDI1KS5zZXRMb2NhbGUoJ2VuLUdCJykKICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfQogICAgICovCiAgICBzZXRMb2NhbGUobG9jYWxlKSB7CiAgICAgIHJldHVybiB0aGlzLnJlY29uZmlndXJlKHsgbG9jYWxlIH0pOwogICAgfQoKICAgIC8qKgogICAgICogIlNldCIgdGhlIHZhbHVlcyBvZiBzcGVjaWZpZWQgdW5pdHMuIFJldHVybnMgYSBuZXdseS1jb25zdHJ1Y3RlZCBEYXRlVGltZS4KICAgICAqIFlvdSBjYW4gb25seSBzZXQgdW5pdHMgd2l0aCB0aGlzIG1ldGhvZDsgZm9yICJzZXR0aW5nIiBtZXRhZGF0YSwgc2VlIHtAbGluayBEYXRlVGltZSNyZWNvbmZpZ3VyZX0gYW5kIHtAbGluayBEYXRlVGltZSNzZXRab25lfS4KICAgICAqIEBwYXJhbSB7T2JqZWN0fSB2YWx1ZXMgLSBhIG1hcHBpbmcgb2YgdW5pdHMgdG8gbnVtYmVycwogICAgICogQGV4YW1wbGUgZHQuc2V0KHsgeWVhcjogMjAxNyB9KQogICAgICogQGV4YW1wbGUgZHQuc2V0KHsgaG91cjogOCwgbWludXRlOiAzMCB9KQogICAgICogQGV4YW1wbGUgZHQuc2V0KHsgd2Vla2RheTogNSB9KQogICAgICogQGV4YW1wbGUgZHQuc2V0KHsgeWVhcjogMjAwNSwgb3JkaW5hbDogMjM0IH0pCiAgICAgKiBAcmV0dXJuIHtEYXRlVGltZX0KICAgICAqLwogICAgc2V0KHZhbHVlcykgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CgogICAgICBjb25zdCBub3JtYWxpemVkID0gbm9ybWFsaXplT2JqZWN0KHZhbHVlcywgbm9ybWFsaXplVW5pdCksCiAgICAgICAgc2V0dGluZ1dlZWtTdHVmZiA9CiAgICAgICAgICAhaXNVbmRlZmluZWQobm9ybWFsaXplZC53ZWVrWWVhcikgfHwKICAgICAgICAgICFpc1VuZGVmaW5lZChub3JtYWxpemVkLndlZWtOdW1iZXIpIHx8CiAgICAgICAgICAhaXNVbmRlZmluZWQobm9ybWFsaXplZC53ZWVrZGF5KSwKICAgICAgICBjb250YWluc09yZGluYWwgPSAhaXNVbmRlZmluZWQobm9ybWFsaXplZC5vcmRpbmFsKSwKICAgICAgICBjb250YWluc0dyZWdvclllYXIgPSAhaXNVbmRlZmluZWQobm9ybWFsaXplZC55ZWFyKSwKICAgICAgICBjb250YWluc0dyZWdvck1EID0gIWlzVW5kZWZpbmVkKG5vcm1hbGl6ZWQubW9udGgpIHx8ICFpc1VuZGVmaW5lZChub3JtYWxpemVkLmRheSksCiAgICAgICAgY29udGFpbnNHcmVnb3IgPSBjb250YWluc0dyZWdvclllYXIgfHwgY29udGFpbnNHcmVnb3JNRCwKICAgICAgICBkZWZpbml0ZVdlZWtEZWYgPSBub3JtYWxpemVkLndlZWtZZWFyIHx8IG5vcm1hbGl6ZWQud2Vla051bWJlcjsKCiAgICAgIGlmICgoY29udGFpbnNHcmVnb3IgfHwgY29udGFpbnNPcmRpbmFsKSAmJiBkZWZpbml0ZVdlZWtEZWYpIHsKICAgICAgICB0aHJvdyBuZXcgQ29uZmxpY3RpbmdTcGVjaWZpY2F0aW9uRXJyb3IoCiAgICAgICAgICAiQ2FuJ3QgbWl4IHdlZWtZZWFyL3dlZWtOdW1iZXIgdW5pdHMgd2l0aCB5ZWFyL21vbnRoL2RheSBvciBvcmRpbmFscyIKICAgICAgICApOwogICAgICB9CgogICAgICBpZiAoY29udGFpbnNHcmVnb3JNRCAmJiBjb250YWluc09yZGluYWwpIHsKICAgICAgICB0aHJvdyBuZXcgQ29uZmxpY3RpbmdTcGVjaWZpY2F0aW9uRXJyb3IoIkNhbid0IG1peCBvcmRpbmFsIGRhdGVzIHdpdGggbW9udGgvZGF5Iik7CiAgICAgIH0KCiAgICAgIGxldCBtaXhlZDsKICAgICAgaWYgKHNldHRpbmdXZWVrU3R1ZmYpIHsKICAgICAgICBtaXhlZCA9IHdlZWtUb0dyZWdvcmlhbih7IC4uLmdyZWdvcmlhblRvV2Vlayh0aGlzLmMpLCAuLi5ub3JtYWxpemVkIH0pOwogICAgICB9IGVsc2UgaWYgKCFpc1VuZGVmaW5lZChub3JtYWxpemVkLm9yZGluYWwpKSB7CiAgICAgICAgbWl4ZWQgPSBvcmRpbmFsVG9HcmVnb3JpYW4oeyAuLi5ncmVnb3JpYW5Ub09yZGluYWwodGhpcy5jKSwgLi4ubm9ybWFsaXplZCB9KTsKICAgICAgfSBlbHNlIHsKICAgICAgICBtaXhlZCA9IHsgLi4udGhpcy50b09iamVjdCgpLCAuLi5ub3JtYWxpemVkIH07CgogICAgICAgIC8vIGlmIHdlIGRpZG4ndCBzZXQgdGhlIGRheSBidXQgd2UgZW5kZWQgdXAgb24gYW4gb3ZlcmZsb3cgZGF0ZSwKICAgICAgICAvLyB1c2UgdGhlIGxhc3QgZGF5IG9mIHRoZSByaWdodCBtb250aAogICAgICAgIGlmIChpc1VuZGVmaW5lZChub3JtYWxpemVkLmRheSkpIHsKICAgICAgICAgIG1peGVkLmRheSA9IE1hdGgubWluKGRheXNJbk1vbnRoKG1peGVkLnllYXIsIG1peGVkLm1vbnRoKSwgbWl4ZWQuZGF5KTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIGNvbnN0IFt0cywgb10gPSBvYmpUb1RTKG1peGVkLCB0aGlzLm8sIHRoaXMuem9uZSk7CiAgICAgIHJldHVybiBjbG9uZSh0aGlzLCB7IHRzLCBvIH0pOwogICAgfQoKICAgIC8qKgogICAgICogQWRkIGEgcGVyaW9kIG9mIHRpbWUgdG8gdGhpcyBEYXRlVGltZSBhbmQgcmV0dXJuIHRoZSByZXN1bHRpbmcgRGF0ZVRpbWUKICAgICAqCiAgICAgKiBBZGRpbmcgaG91cnMsIG1pbnV0ZXMsIHNlY29uZHMsIG9yIG1pbGxpc2Vjb25kcyBpbmNyZWFzZXMgdGhlIHRpbWVzdGFtcCBieSB0aGUgcmlnaHQgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcy4gQWRkaW5nIGRheXMsIG1vbnRocywgb3IgeWVhcnMgc2hpZnRzIHRoZSBjYWxlbmRhciwgYWNjb3VudGluZyBmb3IgRFNUcyBhbmQgbGVhcCB5ZWFycyBhbG9uZyB0aGUgd2F5LiBUaHVzLCBgZHQucGx1cyh7IGhvdXJzOiAyNCB9KWAgbWF5IHJlc3VsdCBpbiBhIGRpZmZlcmVudCB0aW1lIHRoYW4gYGR0LnBsdXMoeyBkYXlzOiAxIH0pYCBpZiB0aGVyZSdzIGEgRFNUIHNoaWZ0IGluIGJldHdlZW4uCiAgICAgKiBAcGFyYW0ge0R1cmF0aW9ufE9iamVjdHxudW1iZXJ9IGR1cmF0aW9uIC0gVGhlIGFtb3VudCB0byBhZGQuIEVpdGhlciBhIEx1eG9uIER1cmF0aW9uLCBhIG51bWJlciBvZiBtaWxsaXNlY29uZHMsIHRoZSBvYmplY3QgYXJndW1lbnQgdG8gRHVyYXRpb24uZnJvbU9iamVjdCgpCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5wbHVzKDEyMykgLy9+PiBpbiAxMjMgbWlsbGlzZWNvbmRzCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5wbHVzKHsgbWludXRlczogMTUgfSkgLy9+PiBpbiAxNSBtaW51dGVzCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5wbHVzKHsgZGF5czogMSB9KSAvL34+IHRoaXMgdGltZSB0b21vcnJvdwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkucGx1cyh7IGRheXM6IC0xIH0pIC8vfj4gdGhpcyB0aW1lIHllc3RlcmRheQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkucGx1cyh7IGhvdXJzOiAzLCBtaW51dGVzOiAxMyB9KSAvL34+IGluIDMgaHIsIDEzIG1pbgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkucGx1cyhEdXJhdGlvbi5mcm9tT2JqZWN0KHsgaG91cnM6IDMsIG1pbnV0ZXM6IDEzIH0pKSAvL34+IGluIDMgaHIsIDEzIG1pbgogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHBsdXMoZHVyYXRpb24pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB0aGlzOwogICAgICBjb25zdCBkdXIgPSBEdXJhdGlvbi5mcm9tRHVyYXRpb25MaWtlKGR1cmF0aW9uKTsKICAgICAgcmV0dXJuIGNsb25lKHRoaXMsIGFkanVzdFRpbWUodGhpcywgZHVyKSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBTdWJ0cmFjdCBhIHBlcmlvZCBvZiB0aW1lIHRvIHRoaXMgRGF0ZVRpbWUgYW5kIHJldHVybiB0aGUgcmVzdWx0aW5nIERhdGVUaW1lCiAgICAgKiBTZWUge0BsaW5rIERhdGVUaW1lI3BsdXN9CiAgICAgKiBAcGFyYW0ge0R1cmF0aW9ufE9iamVjdHxudW1iZXJ9IGR1cmF0aW9uIC0gVGhlIGFtb3VudCB0byBzdWJ0cmFjdC4gRWl0aGVyIGEgTHV4b24gRHVyYXRpb24sIGEgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcywgdGhlIG9iamVjdCBhcmd1bWVudCB0byBEdXJhdGlvbi5mcm9tT2JqZWN0KCkKICAgICBAcmV0dXJuIHtEYXRlVGltZX0KICAgICAqLwogICAgbWludXMoZHVyYXRpb24pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB0aGlzOwogICAgICBjb25zdCBkdXIgPSBEdXJhdGlvbi5mcm9tRHVyYXRpb25MaWtlKGR1cmF0aW9uKS5uZWdhdGUoKTsKICAgICAgcmV0dXJuIGNsb25lKHRoaXMsIGFkanVzdFRpbWUodGhpcywgZHVyKSk7CiAgICB9CgogICAgLyoqCiAgICAgKiAiU2V0IiB0aGlzIERhdGVUaW1lIHRvIHRoZSBiZWdpbm5pbmcgb2YgYSB1bml0IG9mIHRpbWUuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdW5pdCAtIFRoZSB1bml0IHRvIGdvIHRvIHRoZSBiZWdpbm5pbmcgb2YuIENhbiBiZSAneWVhcicsICdxdWFydGVyJywgJ21vbnRoJywgJ3dlZWsnLCAnZGF5JywgJ2hvdXInLCAnbWludXRlJywgJ3NlY29uZCcsIG9yICdtaWxsaXNlY29uZCcuCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCAzLCAzKS5zdGFydE9mKCdtb250aCcpLnRvSVNPRGF0ZSgpOyAvLz0+ICcyMDE0LTAzLTAxJwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNCwgMywgMykuc3RhcnRPZigneWVhcicpLnRvSVNPRGF0ZSgpOyAvLz0+ICcyMDE0LTAxLTAxJwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNCwgMywgMykuc3RhcnRPZignd2VlaycpLnRvSVNPRGF0ZSgpOyAvLz0+ICcyMDE0LTAzLTAzJywgd2Vla3MgYWx3YXlzIHN0YXJ0IG9uIE1vbmRheXMKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTQsIDMsIDMsIDUsIDMwKS5zdGFydE9mKCdkYXknKS50b0lTT1RpbWUoKTsgLy89PiAnMDA6MDAuMDAwLTA1OjAwJwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubG9jYWwoMjAxNCwgMywgMywgNSwgMzApLnN0YXJ0T2YoJ2hvdXInKS50b0lTT1RpbWUoKTsgLy89PiAnMDU6MDA6MDAuMDAwLTA1OjAwJwogICAgICogQHJldHVybiB7RGF0ZVRpbWV9CiAgICAgKi8KICAgIHN0YXJ0T2YodW5pdCkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgcmV0dXJuIHRoaXM7CiAgICAgIGNvbnN0IG8gPSB7fSwKICAgICAgICBub3JtYWxpemVkVW5pdCA9IER1cmF0aW9uLm5vcm1hbGl6ZVVuaXQodW5pdCk7CiAgICAgIHN3aXRjaCAobm9ybWFsaXplZFVuaXQpIHsKICAgICAgICBjYXNlICJ5ZWFycyI6CiAgICAgICAgICBvLm1vbnRoID0gMTsKICAgICAgICAvLyBmYWxscyB0aHJvdWdoCiAgICAgICAgY2FzZSAicXVhcnRlcnMiOgogICAgICAgIGNhc2UgIm1vbnRocyI6CiAgICAgICAgICBvLmRheSA9IDE7CiAgICAgICAgLy8gZmFsbHMgdGhyb3VnaAogICAgICAgIGNhc2UgIndlZWtzIjoKICAgICAgICBjYXNlICJkYXlzIjoKICAgICAgICAgIG8uaG91ciA9IDA7CiAgICAgICAgLy8gZmFsbHMgdGhyb3VnaAogICAgICAgIGNhc2UgImhvdXJzIjoKICAgICAgICAgIG8ubWludXRlID0gMDsKICAgICAgICAvLyBmYWxscyB0aHJvdWdoCiAgICAgICAgY2FzZSAibWludXRlcyI6CiAgICAgICAgICBvLnNlY29uZCA9IDA7CiAgICAgICAgLy8gZmFsbHMgdGhyb3VnaAogICAgICAgIGNhc2UgInNlY29uZHMiOgogICAgICAgICAgby5taWxsaXNlY29uZCA9IDA7CiAgICAgICAgICBicmVhazsKICAgICAgICAvLyBubyBkZWZhdWx0LCBpbnZhbGlkIHVuaXRzIHRocm93IGluIG5vcm1hbGl6ZVVuaXQoKQogICAgICB9CgogICAgICBpZiAobm9ybWFsaXplZFVuaXQgPT09ICJ3ZWVrcyIpIHsKICAgICAgICBvLndlZWtkYXkgPSAxOwogICAgICB9CgogICAgICBpZiAobm9ybWFsaXplZFVuaXQgPT09ICJxdWFydGVycyIpIHsKICAgICAgICBjb25zdCBxID0gTWF0aC5jZWlsKHRoaXMubW9udGggLyAzKTsKICAgICAgICBvLm1vbnRoID0gKHEgLSAxKSAqIDMgKyAxOwogICAgICB9CgogICAgICByZXR1cm4gdGhpcy5zZXQobyk7CiAgICB9CgogICAgLyoqCiAgICAgKiAiU2V0IiB0aGlzIERhdGVUaW1lIHRvIHRoZSBlbmQgKG1lYW5pbmcgdGhlIGxhc3QgbWlsbGlzZWNvbmQpIG9mIGEgdW5pdCBvZiB0aW1lCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdW5pdCAtIFRoZSB1bml0IHRvIGdvIHRvIHRoZSBlbmQgb2YuIENhbiBiZSAneWVhcicsICdxdWFydGVyJywgJ21vbnRoJywgJ3dlZWsnLCAnZGF5JywgJ2hvdXInLCAnbWludXRlJywgJ3NlY29uZCcsIG9yICdtaWxsaXNlY29uZCcuCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCAzLCAzKS5lbmRPZignbW9udGgnKS50b0lTTygpOyAvLz0+ICcyMDE0LTAzLTMxVDIzOjU5OjU5Ljk5OS0wNTowMCcKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTQsIDMsIDMpLmVuZE9mKCd5ZWFyJykudG9JU08oKTsgLy89PiAnMjAxNC0xMi0zMVQyMzo1OTo1OS45OTktMDU6MDAnCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCAzLCAzKS5lbmRPZignd2VlaycpLnRvSVNPKCk7IC8vID0+ICcyMDE0LTAzLTA5VDIzOjU5OjU5Ljk5OS0wNTowMCcsIHdlZWtzIHN0YXJ0IG9uIE1vbmRheXMKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTQsIDMsIDMsIDUsIDMwKS5lbmRPZignZGF5JykudG9JU08oKTsgLy89PiAnMjAxNC0wMy0wM1QyMzo1OTo1OS45OTktMDU6MDAnCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCAzLCAzLCA1LCAzMCkuZW5kT2YoJ2hvdXInKS50b0lTTygpOyAvLz0+ICcyMDE0LTAzLTAzVDA1OjU5OjU5Ljk5OS0wNTowMCcKICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfQogICAgICovCiAgICBlbmRPZih1bml0KSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQKICAgICAgICA/IHRoaXMucGx1cyh7IFt1bml0XTogMSB9KQogICAgICAgICAgICAuc3RhcnRPZih1bml0KQogICAgICAgICAgICAubWludXMoMSkKICAgICAgICA6IHRoaXM7CiAgICB9CgogICAgLy8gT1VUUFVUCgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoaXMgRGF0ZVRpbWUgZm9ybWF0dGVkIGFjY29yZGluZyB0byB0aGUgc3BlY2lmaWVkIGZvcm1hdCBzdHJpbmcuCiAgICAgKiAqKllvdSBtYXkgbm90IHdhbnQgdGhpcy4qKiBTZWUge0BsaW5rIERhdGVUaW1lI3RvTG9jYWxlU3RyaW5nfSBmb3IgYSBtb3JlIGZsZXhpYmxlIGZvcm1hdHRpbmcgdG9vbC4gRm9yIGEgdGFibGUgb2YgdG9rZW5zIGFuZCB0aGVpciBpbnRlcnByZXRhdGlvbnMsIHNlZSBbaGVyZV0oaHR0cHM6Ly9tb21lbnQuZ2l0aHViLmlvL2x1eG9uLyMvZm9ybWF0dGluZz9pZD10YWJsZS1vZi10b2tlbnMpLgogICAgICogRGVmYXVsdHMgdG8gZW4tVVMgaWYgbm8gbG9jYWxlIGhhcyBiZWVuIHNwZWNpZmllZCwgcmVnYXJkbGVzcyBvZiB0aGUgc3lzdGVtJ3MgbG9jYWxlLgogICAgICogQHBhcmFtIHtzdHJpbmd9IGZtdCAtIHRoZSBmb3JtYXQgc3RyaW5nCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdHMgdG8gb3ZlcnJpZGUgdGhlIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBvbiB0aGlzIERhdGVUaW1lCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0Zvcm1hdCgneXl5eSBMTEwgZGQnKSAvLz0+ICcyMDE3IEFwciAyMicKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnNldExvY2FsZSgnZnInKS50b0Zvcm1hdCgneXl5eSBMTEwgZGQnKSAvLz0+ICcyMDE3IGF2ci4gMjInCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0Zvcm1hdCgneXl5eSBMTEwgZGQnLCB7IGxvY2FsZTogImZyIiB9KSAvLz0+ICcyMDE3IGF2ci4gMjInCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0Zvcm1hdCgiSEggJ2hvdXJzIGFuZCcgbW0gJ21pbnV0ZXMnIikgLy89PiAnMjAgaG91cnMgYW5kIDU1IG1pbnV0ZXMnCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIHRvRm9ybWF0KGZtdCwgb3B0cyA9IHt9KSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQKICAgICAgICA/IEZvcm1hdHRlci5jcmVhdGUodGhpcy5sb2MucmVkZWZhdWx0VG9FTihvcHRzKSkuZm9ybWF0RGF0ZVRpbWVGcm9tU3RyaW5nKHRoaXMsIGZtdCkKICAgICAgICA6IElOVkFMSUQ7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgbG9jYWxpemVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhpcyBkYXRlLiBBY2NlcHRzIHRoZSBzYW1lIG9wdGlvbnMgYXMgdGhlIEludGwuRGF0ZVRpbWVGb3JtYXQgY29uc3RydWN0b3IgYW5kIGFueSBwcmVzZXRzIGRlZmluZWQgYnkgTHV4b24sIHN1Y2ggYXMgYERhdGVUaW1lLkRBVEVfRlVMTGAgb3IgYERhdGVUaW1lLlRJTUVfU0lNUExFYC4KICAgICAqIFRoZSBleGFjdCBiZWhhdmlvciBvZiB0aGlzIG1ldGhvZCBpcyBicm93c2VyLXNwZWNpZmljLCBidXQgaW4gZ2VuZXJhbCBpdCB3aWxsIHJldHVybiBhbiBhcHByb3ByaWF0ZSByZXByZXNlbnRhdGlvbgogICAgICogb2YgdGhlIERhdGVUaW1lIGluIHRoZSBhc3NpZ25lZCBsb2NhbGUuCiAgICAgKiBEZWZhdWx0cyB0byB0aGUgc3lzdGVtJ3MgbG9jYWxlIGlmIG5vIGxvY2FsZSBoYXMgYmVlbiBzcGVjaWZpZWQKICAgICAqIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9SZWZlcmVuY2UvR2xvYmFsX09iamVjdHMvRGF0ZVRpbWVGb3JtYXQKICAgICAqIEBwYXJhbSBmb3JtYXRPcHRzIHtPYmplY3R9IC0gSW50bC5EYXRlVGltZUZvcm1hdCBjb25zdHJ1Y3RvciBvcHRpb25zIGFuZCBjb25maWd1cmF0aW9uIG9wdGlvbnMKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRzIC0gb3B0cyB0byBvdmVycmlkZSB0aGUgY29uZmlndXJhdGlvbiBvcHRpb25zIG9uIHRoaXMgRGF0ZVRpbWUKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvTG9jYWxlU3RyaW5nKCk7IC8vPT4gNC8yMC8yMDE3CiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5zZXRMb2NhbGUoJ2VuLWdiJykudG9Mb2NhbGVTdHJpbmcoKTsgLy89PiAnMjAvMDQvMjAxNycKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvTG9jYWxlU3RyaW5nKERhdGVUaW1lLkRBVEVfRlVMTCk7IC8vPT4gJ0FwcmlsIDIwLCAyMDE3JwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkudG9Mb2NhbGVTdHJpbmcoRGF0ZVRpbWUuREFURV9GVUxMLCB7IGxvY2FsZTogJ2ZyJyB9KTsgLy89PiAnMjggYW/Du3QgMjAyMicKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvTG9jYWxlU3RyaW5nKERhdGVUaW1lLlRJTUVfU0lNUExFKTsgLy89PiAnMTE6MzIgQU0nCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0xvY2FsZVN0cmluZyhEYXRlVGltZS5EQVRFVElNRV9TSE9SVCk7IC8vPT4gJzQvMjAvMjAxNywgMTE6MzIgQU0nCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0xvY2FsZVN0cmluZyh7IHdlZWtkYXk6ICdsb25nJywgbW9udGg6ICdsb25nJywgZGF5OiAnMi1kaWdpdCcgfSk7IC8vPT4gJ1RodXJzZGF5LCBBcHJpbCAyMCcKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvTG9jYWxlU3RyaW5nKHsgd2Vla2RheTogJ3Nob3J0JywgbW9udGg6ICdzaG9ydCcsIGRheTogJzItZGlnaXQnLCBob3VyOiAnMi1kaWdpdCcsIG1pbnV0ZTogJzItZGlnaXQnIH0pOyAvLz0+ICdUaHUsIEFwciAyMCwgMTE6MjcgQU0nCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0xvY2FsZVN0cmluZyh7IGhvdXI6ICcyLWRpZ2l0JywgbWludXRlOiAnMi1kaWdpdCcsIGhvdXJDeWNsZTogJ2gyMycgfSk7IC8vPT4gJzExOjMyJwogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0xvY2FsZVN0cmluZyhmb3JtYXRPcHRzID0gREFURV9TSE9SVCwgb3B0cyA9IHt9KSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQKICAgICAgICA/IEZvcm1hdHRlci5jcmVhdGUodGhpcy5sb2MuY2xvbmUob3B0cyksIGZvcm1hdE9wdHMpLmZvcm1hdERhdGVUaW1lKHRoaXMpCiAgICAgICAgOiBJTlZBTElEOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBhcnJheSBvZiBmb3JtYXQgInBhcnRzIiwgbWVhbmluZyBpbmRpdmlkdWFsIHRva2VucyBhbG9uZyB3aXRoIG1ldGFkYXRhLiBUaGlzIGlzIGFsbG93cyBjYWxsZXJzIHRvIHBvc3QtcHJvY2VzcyBpbmRpdmlkdWFsIHNlY3Rpb25zIG9mIHRoZSBmb3JtYXR0ZWQgb3V0cHV0LgogICAgICogRGVmYXVsdHMgdG8gdGhlIHN5c3RlbSdzIGxvY2FsZSBpZiBubyBsb2NhbGUgaGFzIGJlZW4gc3BlY2lmaWVkCiAgICAgKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0phdmFTY3JpcHQvUmVmZXJlbmNlL0dsb2JhbF9PYmplY3RzL0RhdGVUaW1lRm9ybWF0L2Zvcm1hdFRvUGFydHMKICAgICAqIEBwYXJhbSBvcHRzIHtPYmplY3R9IC0gSW50bC5EYXRlVGltZUZvcm1hdCBjb25zdHJ1Y3RvciBvcHRpb25zLCBzYW1lIGFzIGB0b0xvY2FsZVN0cmluZ2AuCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0xvY2FsZVBhcnRzKCk7IC8vPT4gWwogICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vPT4gICB7IHR5cGU6ICdkYXknLCB2YWx1ZTogJzI1JyB9LAogICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vPT4gICB7IHR5cGU6ICdsaXRlcmFsJywgdmFsdWU6ICcvJyB9LAogICAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vPT4gICB7IHR5cGU6ICdtb250aCcsIHZhbHVlOiAnMDUnIH0sCiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy89PiAgIHsgdHlwZTogJ2xpdGVyYWwnLCB2YWx1ZTogJy8nIH0sCiAgICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy89PiAgIHsgdHlwZTogJ3llYXInLCB2YWx1ZTogJzE5ODInIH0KICAgICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLz0+IF0KICAgICAqLwogICAgdG9Mb2NhbGVQYXJ0cyhvcHRzID0ge30pIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZAogICAgICAgID8gRm9ybWF0dGVyLmNyZWF0ZSh0aGlzLmxvYy5jbG9uZShvcHRzKSwgb3B0cykuZm9ybWF0RGF0ZVRpbWVQYXJ0cyh0aGlzKQogICAgICAgIDogW107CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIElTTyA4NjAxLWNvbXBsaWFudCBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBEYXRlVGltZQogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLnN1cHByZXNzTWlsbGlzZWNvbmRzPWZhbHNlXSAtIGV4Y2x1ZGUgbWlsbGlzZWNvbmRzIGZyb20gdGhlIGZvcm1hdCBpZiB0aGV5J3JlIDAKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuc3VwcHJlc3NTZWNvbmRzPWZhbHNlXSAtIGV4Y2x1ZGUgc2Vjb25kcyBmcm9tIHRoZSBmb3JtYXQgaWYgdGhleSdyZSAwCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmluY2x1ZGVPZmZzZXQ9dHJ1ZV0gLSBpbmNsdWRlIHRoZSBvZmZzZXQsIHN1Y2ggYXMgJ1onIG9yICctMDQ6MDAnCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmV4dGVuZGVkWm9uZT1mYWxzZV0gLSBhZGQgdGhlIHRpbWUgem9uZSBmb3JtYXQgZXh0ZW5zaW9uCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuZm9ybWF0PSdleHRlbmRlZCddIC0gY2hvb3NlIGJldHdlZW4gdGhlIGJhc2ljIGFuZCBleHRlbmRlZCBmb3JtYXQKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLnV0YygxOTgzLCA1LCAyNSkudG9JU08oKSAvLz0+ICcxOTgyLTA1LTI1VDAwOjAwOjAwLjAwMFonCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b0lTTygpIC8vPT4gJzIwMTctMDQtMjJUMjA6NDc6MDUuMzM1LTA0OjAwJwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkudG9JU08oeyBpbmNsdWRlT2Zmc2V0OiBmYWxzZSB9KSAvLz0+ICcyMDE3LTA0LTIyVDIwOjQ3OjA1LjMzNScKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvSVNPKHsgZm9ybWF0OiAnYmFzaWMnIH0pIC8vPT4gJzIwMTcwNDIyVDIwNDcwNS4zMzUtMDQwMCcKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9JU08oewogICAgICBmb3JtYXQgPSAiZXh0ZW5kZWQiLAogICAgICBzdXBwcmVzc1NlY29uZHMgPSBmYWxzZSwKICAgICAgc3VwcHJlc3NNaWxsaXNlY29uZHMgPSBmYWxzZSwKICAgICAgaW5jbHVkZU9mZnNldCA9IHRydWUsCiAgICAgIGV4dGVuZGVkWm9uZSA9IGZhbHNlLAogICAgfSA9IHt9KSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSB7CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICAgIH0KCiAgICAgIGNvbnN0IGV4dCA9IGZvcm1hdCA9PT0gImV4dGVuZGVkIjsKCiAgICAgIGxldCBjID0gdG9JU09EYXRlKHRoaXMsIGV4dCk7CiAgICAgIGMgKz0gIlQiOwogICAgICBjICs9IHRvSVNPVGltZSh0aGlzLCBleHQsIHN1cHByZXNzU2Vjb25kcywgc3VwcHJlc3NNaWxsaXNlY29uZHMsIGluY2x1ZGVPZmZzZXQsIGV4dGVuZGVkWm9uZSk7CiAgICAgIHJldHVybiBjOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhbiBJU08gODYwMS1jb21wbGlhbnQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoaXMgRGF0ZVRpbWUncyBkYXRlIGNvbXBvbmVudAogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdHMuZm9ybWF0PSdleHRlbmRlZCddIC0gY2hvb3NlIGJldHdlZW4gdGhlIGJhc2ljIGFuZCBleHRlbmRlZCBmb3JtYXQKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLnV0YygxOTgyLCA1LCAyNSkudG9JU09EYXRlKCkgLy89PiAnMTk4Mi0wNS0yNScKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLnV0YygxOTgyLCA1LCAyNSkudG9JU09EYXRlKHsgZm9ybWF0OiAnYmFzaWMnIH0pIC8vPT4gJzE5ODIwNTI1JwogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b0lTT0RhdGUoeyBmb3JtYXQgPSAiZXh0ZW5kZWQiIH0gPSB7fSkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgewogICAgICAgIHJldHVybiBudWxsOwogICAgICB9CgogICAgICByZXR1cm4gdG9JU09EYXRlKHRoaXMsIGZvcm1hdCA9PT0gImV4dGVuZGVkIik7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIElTTyA4NjAxLWNvbXBsaWFudCBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBEYXRlVGltZSdzIHdlZWsgZGF0ZQogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDE5ODIsIDUsIDI1KS50b0lTT1dlZWtEYXRlKCkgLy89PiAnMTk4Mi1XMjEtMicKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9JU09XZWVrRGF0ZSgpIHsKICAgICAgcmV0dXJuIHRvVGVjaEZvcm1hdCh0aGlzLCAia2tray0nVydXVy1jIik7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIElTTyA4NjAxLWNvbXBsaWFudCBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBEYXRlVGltZSdzIHRpbWUgY29tcG9uZW50CiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuc3VwcHJlc3NNaWxsaXNlY29uZHM9ZmFsc2VdIC0gZXhjbHVkZSBtaWxsaXNlY29uZHMgZnJvbSB0aGUgZm9ybWF0IGlmIHRoZXkncmUgMAogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5zdXBwcmVzc1NlY29uZHM9ZmFsc2VdIC0gZXhjbHVkZSBzZWNvbmRzIGZyb20gdGhlIGZvcm1hdCBpZiB0aGV5J3JlIDAKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuaW5jbHVkZU9mZnNldD10cnVlXSAtIGluY2x1ZGUgdGhlIG9mZnNldCwgc3VjaCBhcyAnWicgb3IgJy0wNDowMCcKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuZXh0ZW5kZWRab25lPXRydWVdIC0gYWRkIHRoZSB0aW1lIHpvbmUgZm9ybWF0IGV4dGVuc2lvbgogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5pbmNsdWRlUHJlZml4PWZhbHNlXSAtIGluY2x1ZGUgdGhlIGBUYCBwcmVmaXgKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5mb3JtYXQ9J2V4dGVuZGVkJ10gLSBjaG9vc2UgYmV0d2VlbiB0aGUgYmFzaWMgYW5kIGV4dGVuZGVkIGZvcm1hdAogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKCkuc2V0KHsgaG91cjogNywgbWludXRlOiAzNCB9KS50b0lTT1RpbWUoKSAvLz0+ICcwNzozNDoxOS4zNjFaJwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKCkuc2V0KHsgaG91cjogNywgbWludXRlOiAzNCwgc2Vjb25kczogMCwgbWlsbGlzZWNvbmRzOiAwIH0pLnRvSVNPVGltZSh7IHN1cHByZXNzU2Vjb25kczogdHJ1ZSB9KSAvLz0+ICcwNzozNFonCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS51dGMoKS5zZXQoeyBob3VyOiA3LCBtaW51dGU6IDM0IH0pLnRvSVNPVGltZSh7IGZvcm1hdDogJ2Jhc2ljJyB9KSAvLz0+ICcwNzM0MTkuMzYxWicKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLnV0YygpLnNldCh7IGhvdXI6IDcsIG1pbnV0ZTogMzQgfSkudG9JU09UaW1lKHsgaW5jbHVkZVByZWZpeDogdHJ1ZSB9KSAvLz0+ICdUMDc6MzQ6MTkuMzYxWicKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9JU09UaW1lKHsKICAgICAgc3VwcHJlc3NNaWxsaXNlY29uZHMgPSBmYWxzZSwKICAgICAgc3VwcHJlc3NTZWNvbmRzID0gZmFsc2UsCiAgICAgIGluY2x1ZGVPZmZzZXQgPSB0cnVlLAogICAgICBpbmNsdWRlUHJlZml4ID0gZmFsc2UsCiAgICAgIGV4dGVuZGVkWm9uZSA9IGZhbHNlLAogICAgICBmb3JtYXQgPSAiZXh0ZW5kZWQiLAogICAgfSA9IHt9KSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSB7CiAgICAgICAgcmV0dXJuIG51bGw7CiAgICAgIH0KCiAgICAgIGxldCBjID0gaW5jbHVkZVByZWZpeCA/ICJUIiA6ICIiOwogICAgICByZXR1cm4gKAogICAgICAgIGMgKwogICAgICAgIHRvSVNPVGltZSgKICAgICAgICAgIHRoaXMsCiAgICAgICAgICBmb3JtYXQgPT09ICJleHRlbmRlZCIsCiAgICAgICAgICBzdXBwcmVzc1NlY29uZHMsCiAgICAgICAgICBzdXBwcmVzc01pbGxpc2Vjb25kcywKICAgICAgICAgIGluY2x1ZGVPZmZzZXQsCiAgICAgICAgICBleHRlbmRlZFpvbmUKICAgICAgICApCiAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGFuIFJGQyAyODIyLWNvbXBhdGlibGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoaXMgRGF0ZVRpbWUKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLnV0YygyMDE0LCA3LCAxMykudG9SRkMyODIyKCkgLy89PiAnU3VuLCAxMyBKdWwgMjAxNCAwMDowMDowMCArMDAwMCcKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTQsIDcsIDEzKS50b1JGQzI4MjIoKSAvLz0+ICdTdW4sIDEzIEp1bCAyMDE0IDAwOjAwOjAwIC0wNDAwJwogICAgICogQHJldHVybiB7c3RyaW5nfQogICAgICovCiAgICB0b1JGQzI4MjIoKSB7CiAgICAgIHJldHVybiB0b1RlY2hGb3JtYXQodGhpcywgIkVFRSwgZGQgTExMIHl5eXkgSEg6bW06c3MgWlpaIiwgZmFsc2UpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIERhdGVUaW1lIGFwcHJvcHJpYXRlIGZvciB1c2UgaW4gSFRUUCBoZWFkZXJzLiBUaGUgb3V0cHV0IGlzIGFsd2F5cyBleHByZXNzZWQgaW4gR01ULgogICAgICogU3BlY2lmaWNhbGx5LCB0aGUgc3RyaW5nIGNvbmZvcm1zIHRvIFJGQyAxMTIzLgogICAgICogQHNlZSBodHRwczovL3d3dy53My5vcmcvUHJvdG9jb2xzL3JmYzI2MTYvcmZjMjYxNi1zZWMzLmh0bWwjc2VjMy4zLjEKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLnV0YygyMDE0LCA3LCAxMykudG9IVFRQKCkgLy89PiAnU3VuLCAxMyBKdWwgMjAxNCAwMDowMDowMCBHTVQnCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS51dGMoMjAxNCwgNywgMTMsIDE5KS50b0hUVFAoKSAvLz0+ICdTdW4sIDEzIEp1bCAyMDE0IDE5OjAwOjAwIEdNVCcKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9IVFRQKCkgewogICAgICByZXR1cm4gdG9UZWNoRm9ybWF0KHRoaXMudG9VVEMoKSwgIkVFRSwgZGQgTExMIHl5eXkgSEg6bW06c3MgJ0dNVCciKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBEYXRlVGltZSBhcHByb3ByaWF0ZSBmb3IgdXNlIGluIFNRTCBEYXRlCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS51dGMoMjAxNCwgNywgMTMpLnRvU1FMRGF0ZSgpIC8vPT4gJzIwMTQtMDctMTMnCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIHRvU1FMRGF0ZSgpIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHsKICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgfQogICAgICByZXR1cm4gdG9JU09EYXRlKHRoaXMsIHRydWUpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIERhdGVUaW1lIGFwcHJvcHJpYXRlIGZvciB1c2UgaW4gU1FMIFRpbWUKICAgICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRzIC0gb3B0aW9ucwogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5pbmNsdWRlWm9uZT1mYWxzZV0gLSBpbmNsdWRlIHRoZSB6b25lLCBzdWNoIGFzICdBbWVyaWNhL05ld19Zb3JrJy4gT3ZlcnJpZGVzIGluY2x1ZGVPZmZzZXQuCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmluY2x1ZGVPZmZzZXQ9dHJ1ZV0gLSBpbmNsdWRlIHRoZSBvZmZzZXQsIHN1Y2ggYXMgJ1onIG9yICctMDQ6MDAnCiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRzLmluY2x1ZGVPZmZzZXRTcGFjZT10cnVlXSAtIGluY2x1ZGUgdGhlIHNwYWNlIGJldHdlZW4gdGhlIHRpbWUgYW5kIHRoZSBvZmZzZXQsIHN1Y2ggYXMgJzA1OjE1OjE2LjM0NSAtMDQ6MDAnCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS51dGMoKS50b1NRTCgpIC8vPT4gJzA1OjE1OjE2LjM0NScKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvU1FMKCkgLy89PiAnMDU6MTU6MTYuMzQ1IC0wNDowMCcKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnRvU1FMKHsgaW5jbHVkZU9mZnNldDogZmFsc2UgfSkgLy89PiAnMDU6MTU6MTYuMzQ1JwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkudG9TUUwoeyBpbmNsdWRlWm9uZTogZmFsc2UgfSkgLy89PiAnMDU6MTU6MTYuMzQ1IEFtZXJpY2EvTmV3X1lvcmsnCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIHRvU1FMVGltZSh7IGluY2x1ZGVPZmZzZXQgPSB0cnVlLCBpbmNsdWRlWm9uZSA9IGZhbHNlLCBpbmNsdWRlT2Zmc2V0U3BhY2UgPSB0cnVlIH0gPSB7fSkgewogICAgICBsZXQgZm10ID0gIkhIOm1tOnNzLlNTUyI7CgogICAgICBpZiAoaW5jbHVkZVpvbmUgfHwgaW5jbHVkZU9mZnNldCkgewogICAgICAgIGlmIChpbmNsdWRlT2Zmc2V0U3BhY2UpIHsKICAgICAgICAgIGZtdCArPSAiICI7CiAgICAgICAgfQogICAgICAgIGlmIChpbmNsdWRlWm9uZSkgewogICAgICAgICAgZm10ICs9ICJ6IjsKICAgICAgICB9IGVsc2UgaWYgKGluY2x1ZGVPZmZzZXQpIHsKICAgICAgICAgIGZtdCArPSAiWloiOwogICAgICAgIH0KICAgICAgfQoKICAgICAgcmV0dXJuIHRvVGVjaEZvcm1hdCh0aGlzLCBmbXQsIHRydWUpOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIERhdGVUaW1lIGFwcHJvcHJpYXRlIGZvciB1c2UgaW4gU1FMIERhdGVUaW1lCiAgICAgKiBAcGFyYW0ge09iamVjdH0gb3B0cyAtIG9wdGlvbnMKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuaW5jbHVkZVpvbmU9ZmFsc2VdIC0gaW5jbHVkZSB0aGUgem9uZSwgc3VjaCBhcyAnQW1lcmljYS9OZXdfWW9yaycuIE92ZXJyaWRlcyBpbmNsdWRlT2Zmc2V0LgogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5pbmNsdWRlT2Zmc2V0PXRydWVdIC0gaW5jbHVkZSB0aGUgb2Zmc2V0LCBzdWNoIGFzICdaJyBvciAnLTA0OjAwJwogICAgICogQHBhcmFtIHtib29sZWFufSBbb3B0cy5pbmNsdWRlT2Zmc2V0U3BhY2U9dHJ1ZV0gLSBpbmNsdWRlIHRoZSBzcGFjZSBiZXR3ZWVuIHRoZSB0aW1lIGFuZCB0aGUgb2Zmc2V0LCBzdWNoIGFzICcwNToxNToxNi4zNDUgLTA0OjAwJwogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUudXRjKDIwMTQsIDcsIDEzKS50b1NRTCgpIC8vPT4gJzIwMTQtMDctMTMgMDA6MDA6MDAuMDAwIFonCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCA3LCAxMykudG9TUUwoKSAvLz0+ICcyMDE0LTA3LTEzIDAwOjAwOjAwLjAwMCAtMDQ6MDAnCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5sb2NhbCgyMDE0LCA3LCAxMykudG9TUUwoeyBpbmNsdWRlT2Zmc2V0OiBmYWxzZSB9KSAvLz0+ICcyMDE0LTA3LTEzIDAwOjAwOjAwLjAwMCcKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLmxvY2FsKDIwMTQsIDcsIDEzKS50b1NRTCh7IGluY2x1ZGVab25lOiB0cnVlIH0pIC8vPT4gJzIwMTQtMDctMTMgMDA6MDA6MDAuMDAwIEFtZXJpY2EvTmV3X1lvcmsnCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIHRvU1FMKG9wdHMgPSB7fSkgewogICAgICBpZiAoIXRoaXMuaXNWYWxpZCkgewogICAgICAgIHJldHVybiBudWxsOwogICAgICB9CgogICAgICByZXR1cm4gYCR7dGhpcy50b1NRTERhdGUoKX0gJHt0aGlzLnRvU1FMVGltZShvcHRzKX1gOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGlzIERhdGVUaW1lIGFwcHJvcHJpYXRlIGZvciBkZWJ1Z2dpbmcKICAgICAqIEByZXR1cm4ge3N0cmluZ30KICAgICAqLwogICAgdG9TdHJpbmcoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyB0aGlzLnRvSVNPKCkgOiBJTlZBTElEOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyB0aGUgZXBvY2ggbWlsbGlzZWNvbmRzIG9mIHRoaXMgRGF0ZVRpbWUuIEFsaWFzIG9mIHtAbGluayBEYXRlVGltZSN0b01pbGxpc30KICAgICAqIEByZXR1cm4ge251bWJlcn0KICAgICAqLwogICAgdmFsdWVPZigpIHsKICAgICAgcmV0dXJuIHRoaXMudG9NaWxsaXMoKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgdGhlIGVwb2NoIG1pbGxpc2Vjb25kcyBvZiB0aGlzIERhdGVUaW1lLgogICAgICogQHJldHVybiB7bnVtYmVyfQogICAgICovCiAgICB0b01pbGxpcygpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMudHMgOiBOYU47CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIHRoZSBlcG9jaCBzZWNvbmRzIG9mIHRoaXMgRGF0ZVRpbWUuCiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9CiAgICAgKi8KICAgIHRvU2Vjb25kcygpIHsKICAgICAgcmV0dXJuIHRoaXMuaXNWYWxpZCA/IHRoaXMudHMgLyAxMDAwIDogTmFOOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyB0aGUgZXBvY2ggc2Vjb25kcyAoYXMgYSB3aG9sZSBudW1iZXIpIG9mIHRoaXMgRGF0ZVRpbWUuCiAgICAgKiBAcmV0dXJuIHtudW1iZXJ9CiAgICAgKi8KICAgIHRvVW5peEludGVnZXIoKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyBNYXRoLmZsb29yKHRoaXMudHMgLyAxMDAwKSA6IE5hTjsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYW4gSVNPIDg2MDEgcmVwcmVzZW50YXRpb24gb2YgdGhpcyBEYXRlVGltZSBhcHByb3ByaWF0ZSBmb3IgdXNlIGluIEpTT04uCiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9CiAgICAgKi8KICAgIHRvSlNPTigpIHsKICAgICAgcmV0dXJuIHRoaXMudG9JU08oKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybnMgYSBCU09OIHNlcmlhbGl6YWJsZSBlcXVpdmFsZW50IHRvIHRoaXMgRGF0ZVRpbWUuCiAgICAgKiBAcmV0dXJuIHtEYXRlfQogICAgICovCiAgICB0b0JTT04oKSB7CiAgICAgIHJldHVybiB0aGlzLnRvSlNEYXRlKCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgSmF2YVNjcmlwdCBvYmplY3Qgd2l0aCB0aGlzIERhdGVUaW1lJ3MgeWVhciwgbW9udGgsIGRheSwgYW5kIHNvIG9uLgogICAgICogQHBhcmFtIG9wdHMgLSBvcHRpb25zIGZvciBnZW5lcmF0aW5nIHRoZSBvYmplY3QKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdHMuaW5jbHVkZUNvbmZpZz1mYWxzZV0gLSBpbmNsdWRlIGNvbmZpZ3VyYXRpb24gYXR0cmlidXRlcyBpbiB0aGUgb3V0cHV0CiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS50b09iamVjdCgpIC8vPT4geyB5ZWFyOiAyMDE3LCBtb250aDogNCwgZGF5OiAyMiwgaG91cjogMjAsIG1pbnV0ZTogNDksIHNlY29uZDogNDIsIG1pbGxpc2Vjb25kOiAyNjggfQogICAgICogQHJldHVybiB7T2JqZWN0fQogICAgICovCiAgICB0b09iamVjdChvcHRzID0ge30pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiB7fTsKCiAgICAgIGNvbnN0IGJhc2UgPSB7IC4uLnRoaXMuYyB9OwoKICAgICAgaWYgKG9wdHMuaW5jbHVkZUNvbmZpZykgewogICAgICAgIGJhc2Uub3V0cHV0Q2FsZW5kYXIgPSB0aGlzLm91dHB1dENhbGVuZGFyOwogICAgICAgIGJhc2UubnVtYmVyaW5nU3lzdGVtID0gdGhpcy5sb2MubnVtYmVyaW5nU3lzdGVtOwogICAgICAgIGJhc2UubG9jYWxlID0gdGhpcy5sb2MubG9jYWxlOwogICAgICB9CiAgICAgIHJldHVybiBiYXNlOwogICAgfQoKICAgIC8qKgogICAgICogUmV0dXJucyBhIEphdmFTY3JpcHQgRGF0ZSBlcXVpdmFsZW50IHRvIHRoaXMgRGF0ZVRpbWUuCiAgICAgKiBAcmV0dXJuIHtEYXRlfQogICAgICovCiAgICB0b0pTRGF0ZSgpIHsKICAgICAgcmV0dXJuIG5ldyBEYXRlKHRoaXMuaXNWYWxpZCA/IHRoaXMudHMgOiBOYU4pOwogICAgfQoKICAgIC8vIENPTVBBUkUKCiAgICAvKioKICAgICAqIFJldHVybiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBEYXRlVGltZXMgYXMgYSBEdXJhdGlvbi4KICAgICAqIEBwYXJhbSB7RGF0ZVRpbWV9IG90aGVyRGF0ZVRpbWUgLSB0aGUgRGF0ZVRpbWUgdG8gY29tcGFyZSB0aGlzIG9uZSB0bwogICAgICogQHBhcmFtIHtzdHJpbmd8c3RyaW5nW119IFt1bml0PVsnbWlsbGlzZWNvbmRzJ11dIC0gdGhlIHVuaXQgb3IgYXJyYXkgb2YgdW5pdHMgKHN1Y2ggYXMgJ2hvdXJzJyBvciAnZGF5cycpIHRvIGluY2x1ZGUgaW4gdGhlIGR1cmF0aW9uLgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIHRoYXQgYWZmZWN0IHRoZSBjcmVhdGlvbiBvZiB0aGUgRHVyYXRpb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5jb252ZXJzaW9uQWNjdXJhY3k9J2Nhc3VhbCddIC0gdGhlIGNvbnZlcnNpb24gc3lzdGVtIHRvIHVzZQogICAgICogQGV4YW1wbGUKICAgICAqIHZhciBpMSA9IERhdGVUaW1lLmZyb21JU08oJzE5ODItMDUtMjVUMDk6NDUnKSwKICAgICAqICAgICBpMiA9IERhdGVUaW1lLmZyb21JU08oJzE5ODMtMTAtMTRUMTA6MzAnKTsKICAgICAqIGkyLmRpZmYoaTEpLnRvT2JqZWN0KCkgLy89PiB7IG1pbGxpc2Vjb25kczogNDM4MDc1MDAwMDAgfQogICAgICogaTIuZGlmZihpMSwgJ2hvdXJzJykudG9PYmplY3QoKSAvLz0+IHsgaG91cnM6IDEyMTY4Ljc1IH0KICAgICAqIGkyLmRpZmYoaTEsIFsnbW9udGhzJywgJ2RheXMnXSkudG9PYmplY3QoKSAvLz0+IHsgbW9udGhzOiAxNiwgZGF5czogMTkuMDMxMjUgfQogICAgICogaTIuZGlmZihpMSwgWydtb250aHMnLCAnZGF5cycsICdob3VycyddKS50b09iamVjdCgpIC8vPT4geyBtb250aHM6IDE2LCBkYXlzOiAxOSwgaG91cnM6IDAuNzUgfQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIGRpZmYob3RoZXJEYXRlVGltZSwgdW5pdCA9ICJtaWxsaXNlY29uZHMiLCBvcHRzID0ge30pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQgfHwgIW90aGVyRGF0ZVRpbWUuaXNWYWxpZCkgewogICAgICAgIHJldHVybiBEdXJhdGlvbi5pbnZhbGlkKCJjcmVhdGVkIGJ5IGRpZmZpbmcgYW4gaW52YWxpZCBEYXRlVGltZSIpOwogICAgICB9CgogICAgICBjb25zdCBkdXJPcHRzID0geyBsb2NhbGU6IHRoaXMubG9jYWxlLCBudW1iZXJpbmdTeXN0ZW06IHRoaXMubnVtYmVyaW5nU3lzdGVtLCAuLi5vcHRzIH07CgogICAgICBjb25zdCB1bml0cyA9IG1heWJlQXJyYXkodW5pdCkubWFwKER1cmF0aW9uLm5vcm1hbGl6ZVVuaXQpLAogICAgICAgIG90aGVySXNMYXRlciA9IG90aGVyRGF0ZVRpbWUudmFsdWVPZigpID4gdGhpcy52YWx1ZU9mKCksCiAgICAgICAgZWFybGllciA9IG90aGVySXNMYXRlciA/IHRoaXMgOiBvdGhlckRhdGVUaW1lLAogICAgICAgIGxhdGVyID0gb3RoZXJJc0xhdGVyID8gb3RoZXJEYXRlVGltZSA6IHRoaXMsCiAgICAgICAgZGlmZmVkID0gZGlmZihlYXJsaWVyLCBsYXRlciwgdW5pdHMsIGR1ck9wdHMpOwoKICAgICAgcmV0dXJuIG90aGVySXNMYXRlciA/IGRpZmZlZC5uZWdhdGUoKSA6IGRpZmZlZDsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoaXMgRGF0ZVRpbWUgYW5kIHJpZ2h0IG5vdy4KICAgICAqIFNlZSB7QGxpbmsgRGF0ZVRpbWUjZGlmZn0KICAgICAqIEBwYXJhbSB7c3RyaW5nfHN0cmluZ1tdfSBbdW5pdD1bJ21pbGxpc2Vjb25kcyddXSAtIHRoZSB1bml0IG9yIHVuaXRzIHVuaXRzIChzdWNoIGFzICdob3Vycycgb3IgJ2RheXMnKSB0byBpbmNsdWRlIGluIHRoZSBkdXJhdGlvbgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdHMgLSBvcHRpb25zIHRoYXQgYWZmZWN0IHRoZSBjcmVhdGlvbiBvZiB0aGUgRHVyYXRpb24KICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbb3B0cy5jb252ZXJzaW9uQWNjdXJhY3k9J2Nhc3VhbCddIC0gdGhlIGNvbnZlcnNpb24gc3lzdGVtIHRvIHVzZQogICAgICogQHJldHVybiB7RHVyYXRpb259CiAgICAgKi8KICAgIGRpZmZOb3codW5pdCA9ICJtaWxsaXNlY29uZHMiLCBvcHRzID0ge30pIHsKICAgICAgcmV0dXJuIHRoaXMuZGlmZihEYXRlVGltZS5ub3coKSwgdW5pdCwgb3B0cyk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gYW4gSW50ZXJ2YWwgc3Bhbm5pbmcgYmV0d2VlbiB0aGlzIERhdGVUaW1lIGFuZCBhbm90aGVyIERhdGVUaW1lCiAgICAgKiBAcGFyYW0ge0RhdGVUaW1lfSBvdGhlckRhdGVUaW1lIC0gdGhlIG90aGVyIGVuZCBwb2ludCBvZiB0aGUgSW50ZXJ2YWwKICAgICAqIEByZXR1cm4ge0ludGVydmFsfQogICAgICovCiAgICB1bnRpbChvdGhlckRhdGVUaW1lKSB7CiAgICAgIHJldHVybiB0aGlzLmlzVmFsaWQgPyBJbnRlcnZhbC5mcm9tRGF0ZVRpbWVzKHRoaXMsIG90aGVyRGF0ZVRpbWUpIDogdGhpczsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB3aGV0aGVyIHRoaXMgRGF0ZVRpbWUgaXMgaW4gdGhlIHNhbWUgdW5pdCBvZiB0aW1lIGFzIGFub3RoZXIgRGF0ZVRpbWUuCiAgICAgKiBIaWdoZXItb3JkZXIgdW5pdHMgbXVzdCBhbHNvIGJlIGlkZW50aWNhbCBmb3IgdGhpcyBmdW5jdGlvbiB0byByZXR1cm4gYHRydWVgLgogICAgICogTm90ZSB0aGF0IHRpbWUgem9uZXMgYXJlICoqaWdub3JlZCoqIGluIHRoaXMgY29tcGFyaXNvbiwgd2hpY2ggY29tcGFyZXMgdGhlICoqbG9jYWwqKiBjYWxlbmRhciB0aW1lLiBVc2Uge0BsaW5rIERhdGVUaW1lI3NldFpvbmV9IHRvIGNvbnZlcnQgb25lIG9mIHRoZSBkYXRlcyBpZiBuZWVkZWQuCiAgICAgKiBAcGFyYW0ge0RhdGVUaW1lfSBvdGhlckRhdGVUaW1lIC0gdGhlIG90aGVyIERhdGVUaW1lCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdW5pdCAtIHRoZSB1bml0IG9mIHRpbWUgdG8gY2hlY2sgc2FtZW5lc3Mgb24KICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLmhhc1NhbWUob3RoZXJEVCwgJ2RheScpOyAvL34+IHRydWUgaWYgb3RoZXJEVCBpcyBpbiB0aGUgc2FtZSBjdXJyZW50IGNhbGVuZGFyIGRheQogICAgICogQHJldHVybiB7Ym9vbGVhbn0KICAgICAqLwogICAgaGFzU2FtZShvdGhlckRhdGVUaW1lLCB1bml0KSB7CiAgICAgIGlmICghdGhpcy5pc1ZhbGlkKSByZXR1cm4gZmFsc2U7CgogICAgICBjb25zdCBpbnB1dE1zID0gb3RoZXJEYXRlVGltZS52YWx1ZU9mKCk7CiAgICAgIGNvbnN0IGFkanVzdGVkVG9ab25lID0gdGhpcy5zZXRab25lKG90aGVyRGF0ZVRpbWUuem9uZSwgeyBrZWVwTG9jYWxUaW1lOiB0cnVlIH0pOwogICAgICByZXR1cm4gYWRqdXN0ZWRUb1pvbmUuc3RhcnRPZih1bml0KSA8PSBpbnB1dE1zICYmIGlucHV0TXMgPD0gYWRqdXN0ZWRUb1pvbmUuZW5kT2YodW5pdCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBFcXVhbGl0eSBjaGVjawogICAgICogVHdvIERhdGVUaW1lcyBhcmUgZXF1YWwgaWYgYW5kIG9ubHkgaWYgdGhleSByZXByZXNlbnQgdGhlIHNhbWUgbWlsbGlzZWNvbmQsIGhhdmUgdGhlIHNhbWUgem9uZSBhbmQgbG9jYXRpb24sIGFuZCBhcmUgYm90aCB2YWxpZC4KICAgICAqIFRvIGNvbXBhcmUganVzdCB0aGUgbWlsbGlzZWNvbmQgdmFsdWVzLCB1c2UgYCtkdDEgPT09ICtkdDJgLgogICAgICogQHBhcmFtIHtEYXRlVGltZX0gb3RoZXIgLSB0aGUgb3RoZXIgRGF0ZVRpbWUKICAgICAqIEByZXR1cm4ge2Jvb2xlYW59CiAgICAgKi8KICAgIGVxdWFscyhvdGhlcikgewogICAgICByZXR1cm4gKAogICAgICAgIHRoaXMuaXNWYWxpZCAmJgogICAgICAgIG90aGVyLmlzVmFsaWQgJiYKICAgICAgICB0aGlzLnZhbHVlT2YoKSA9PT0gb3RoZXIudmFsdWVPZigpICYmCiAgICAgICAgdGhpcy56b25lLmVxdWFscyhvdGhlci56b25lKSAmJgogICAgICAgIHRoaXMubG9jLmVxdWFscyhvdGhlci5sb2MpCiAgICAgICk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGEgdGhpcyB0aW1lIHJlbGF0aXZlIHRvIG5vdywgc3VjaCBhcyAiaW4gdHdvIGRheXMiLiBDYW4gb25seSBpbnRlcm5hdGlvbmFsaXplIGlmIHlvdXIKICAgICAqIHBsYXRmb3JtIHN1cHBvcnRzIEludGwuUmVsYXRpdmVUaW1lRm9ybWF0LiBSb3VuZHMgZG93biBieSBkZWZhdWx0LgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBvcHRpb25zIHRoYXQgYWZmZWN0IHRoZSBvdXRwdXQKICAgICAqIEBwYXJhbSB7RGF0ZVRpbWV9IFtvcHRpb25zLmJhc2U9RGF0ZVRpbWUubm93KCldIC0gdGhlIERhdGVUaW1lIHRvIHVzZSBhcyB0aGUgYmFzaXMgdG8gd2hpY2ggdGhpcyB0aW1lIGlzIGNvbXBhcmVkLiBEZWZhdWx0cyB0byBub3cuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW29wdGlvbnMuc3R5bGU9ImxvbmciXSAtIHRoZSBzdHlsZSBvZiB1bml0cywgbXVzdCBiZSAibG9uZyIsICJzaG9ydCIsIG9yICJuYXJyb3ciCiAgICAgKiBAcGFyYW0ge3N0cmluZ3xzdHJpbmdbXX0gb3B0aW9ucy51bml0IC0gdXNlIGEgc3BlY2lmaWMgdW5pdCBvciBhcnJheSBvZiB1bml0czsgaWYgb21pdHRlZCwgb3IgYW4gYXJyYXksIHRoZSBtZXRob2Qgd2lsbCBwaWNrIHRoZSBiZXN0IHVuaXQuIFVzZSBhbiBhcnJheSBvciBvbmUgb2YgInllYXJzIiwgInF1YXJ0ZXJzIiwgIm1vbnRocyIsICJ3ZWVrcyIsICJkYXlzIiwgImhvdXJzIiwgIm1pbnV0ZXMiLCBvciAic2Vjb25kcyIKICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMucm91bmQ9dHJ1ZV0gLSB3aGV0aGVyIHRvIHJvdW5kIHRoZSBudW1iZXJzIGluIHRoZSBvdXRwdXQuCiAgICAgKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMucGFkZGluZz0wXSAtIHBhZGRpbmcgaW4gbWlsbGlzZWNvbmRzLiBUaGlzIGFsbG93cyB5b3UgdG8gcm91bmQgdXAgdGhlIHJlc3VsdCBpZiBpdCBmaXRzIGluc2lkZSB0aGUgdGhyZXNob2xkLiBEb24ndCB1c2UgaW4gY29tYmluYXRpb24gd2l0aCB7cm91bmQ6IGZhbHNlfSBiZWNhdXNlIHRoZSBkZWNpbWFsIG91dHB1dCB3aWxsIGluY2x1ZGUgdGhlIHBhZGRpbmcuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5sb2NhbGUgLSBvdmVycmlkZSB0aGUgbG9jYWxlIG9mIHRoaXMgRGF0ZVRpbWUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLm51bWJlcmluZ1N5c3RlbSAtIG92ZXJyaWRlIHRoZSBudW1iZXJpbmdTeXN0ZW0gb2YgdGhpcyBEYXRlVGltZS4gVGhlIEludGwgc3lzdGVtIG1heSBjaG9vc2Ugbm90IHRvIGhvbm9yIHRoaXMKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnBsdXMoeyBkYXlzOiAxIH0pLnRvUmVsYXRpdmUoKSAvLz0+ICJpbiAxIGRheSIKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnNldExvY2FsZSgiZXMiKS50b1JlbGF0aXZlKHsgZGF5czogMSB9KSAvLz0+ICJkZW50cm8gZGUgMSBkw61hIgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkucGx1cyh7IGRheXM6IDEgfSkudG9SZWxhdGl2ZSh7IGxvY2FsZTogImZyIiB9KSAvLz0+ICJkYW5zIDIzIGhldXJlcyIKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLm1pbnVzKHsgZGF5czogMiB9KS50b1JlbGF0aXZlKCkgLy89PiAiMiBkYXlzIGFnbyIKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLm1pbnVzKHsgZGF5czogMiB9KS50b1JlbGF0aXZlKHsgdW5pdDogImhvdXJzIiB9KSAvLz0+ICI0OCBob3VycyBhZ28iCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5taW51cyh7IGhvdXJzOiAzNiB9KS50b1JlbGF0aXZlKHsgcm91bmQ6IGZhbHNlIH0pIC8vPT4gIjEuNSBkYXlzIGFnbyIKICAgICAqLwogICAgdG9SZWxhdGl2ZShvcHRpb25zID0ge30pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBudWxsOwogICAgICBjb25zdCBiYXNlID0gb3B0aW9ucy5iYXNlIHx8IERhdGVUaW1lLmZyb21PYmplY3Qoe30sIHsgem9uZTogdGhpcy56b25lIH0pLAogICAgICAgIHBhZGRpbmcgPSBvcHRpb25zLnBhZGRpbmcgPyAodGhpcyA8IGJhc2UgPyAtb3B0aW9ucy5wYWRkaW5nIDogb3B0aW9ucy5wYWRkaW5nKSA6IDA7CiAgICAgIGxldCB1bml0cyA9IFsieWVhcnMiLCAibW9udGhzIiwgImRheXMiLCAiaG91cnMiLCAibWludXRlcyIsICJzZWNvbmRzIl07CiAgICAgIGxldCB1bml0ID0gb3B0aW9ucy51bml0OwogICAgICBpZiAoQXJyYXkuaXNBcnJheShvcHRpb25zLnVuaXQpKSB7CiAgICAgICAgdW5pdHMgPSBvcHRpb25zLnVuaXQ7CiAgICAgICAgdW5pdCA9IHVuZGVmaW5lZDsKICAgICAgfQogICAgICByZXR1cm4gZGlmZlJlbGF0aXZlKGJhc2UsIHRoaXMucGx1cyhwYWRkaW5nKSwgewogICAgICAgIC4uLm9wdGlvbnMsCiAgICAgICAgbnVtZXJpYzogImFsd2F5cyIsCiAgICAgICAgdW5pdHMsCiAgICAgICAgdW5pdCwKICAgICAgfSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoaXMgZGF0ZSByZWxhdGl2ZSB0byB0b2RheSwgc3VjaCBhcyAieWVzdGVyZGF5IiBvciAibmV4dCBtb250aCIuCiAgICAgKiBPbmx5IGludGVybmF0aW9uYWxpemVzIG9uIHBsYXRmb3JtcyB0aGF0IHN1cHBvcnRzIEludGwuUmVsYXRpdmVUaW1lRm9ybWF0LgogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBvcHRpb25zIHRoYXQgYWZmZWN0IHRoZSBvdXRwdXQKICAgICAqIEBwYXJhbSB7RGF0ZVRpbWV9IFtvcHRpb25zLmJhc2U9RGF0ZVRpbWUubm93KCldIC0gdGhlIERhdGVUaW1lIHRvIHVzZSBhcyB0aGUgYmFzaXMgdG8gd2hpY2ggdGhpcyB0aW1lIGlzIGNvbXBhcmVkLiBEZWZhdWx0cyB0byBub3cuCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5sb2NhbGUgLSBvdmVycmlkZSB0aGUgbG9jYWxlIG9mIHRoaXMgRGF0ZVRpbWUKICAgICAqIEBwYXJhbSB7c3RyaW5nfSBvcHRpb25zLnVuaXQgLSB1c2UgYSBzcGVjaWZpYyB1bml0OyBpZiBvbWl0dGVkLCB0aGUgbWV0aG9kIHdpbGwgcGljayB0aGUgdW5pdC4gVXNlIG9uZSBvZiAieWVhcnMiLCAicXVhcnRlcnMiLCAibW9udGhzIiwgIndlZWtzIiwgb3IgImRheXMiCiAgICAgKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5udW1iZXJpbmdTeXN0ZW0gLSBvdmVycmlkZSB0aGUgbnVtYmVyaW5nU3lzdGVtIG9mIHRoaXMgRGF0ZVRpbWUuIFRoZSBJbnRsIHN5c3RlbSBtYXkgY2hvb3NlIG5vdCB0byBob25vciB0aGlzCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5wbHVzKHsgZGF5czogMSB9KS50b1JlbGF0aXZlQ2FsZW5kYXIoKSAvLz0+ICJ0b21vcnJvdyIKICAgICAqIEBleGFtcGxlIERhdGVUaW1lLm5vdygpLnNldExvY2FsZSgiZXMiKS5wbHVzKHsgZGF5czogMSB9KS50b1JlbGF0aXZlKCkgLy89PiAiIm1hw7FhbmEiCiAgICAgKiBAZXhhbXBsZSBEYXRlVGltZS5ub3coKS5wbHVzKHsgZGF5czogMSB9KS50b1JlbGF0aXZlQ2FsZW5kYXIoeyBsb2NhbGU6ICJmciIgfSkgLy89PiAiZGVtYWluIgogICAgICogQGV4YW1wbGUgRGF0ZVRpbWUubm93KCkubWludXMoeyBkYXlzOiAyIH0pLnRvUmVsYXRpdmVDYWxlbmRhcigpIC8vPT4gIjIgZGF5cyBhZ28iCiAgICAgKi8KICAgIHRvUmVsYXRpdmVDYWxlbmRhcihvcHRpb25zID0ge30pIHsKICAgICAgaWYgKCF0aGlzLmlzVmFsaWQpIHJldHVybiBudWxsOwoKICAgICAgcmV0dXJuIGRpZmZSZWxhdGl2ZShvcHRpb25zLmJhc2UgfHwgRGF0ZVRpbWUuZnJvbU9iamVjdCh7fSwgeyB6b25lOiB0aGlzLnpvbmUgfSksIHRoaXMsIHsKICAgICAgICAuLi5vcHRpb25zLAogICAgICAgIG51bWVyaWM6ICJhdXRvIiwKICAgICAgICB1bml0czogWyJ5ZWFycyIsICJtb250aHMiLCAiZGF5cyJdLAogICAgICAgIGNhbGVuZGFyeTogdHJ1ZSwKICAgICAgfSk7CiAgICB9CgogICAgLyoqCiAgICAgKiBSZXR1cm4gdGhlIG1pbiBvZiBzZXZlcmFsIGRhdGUgdGltZXMKICAgICAqIEBwYXJhbSB7Li4uRGF0ZVRpbWV9IGRhdGVUaW1lcyAtIHRoZSBEYXRlVGltZXMgZnJvbSB3aGljaCB0byBjaG9vc2UgdGhlIG1pbmltdW0KICAgICAqIEByZXR1cm4ge0RhdGVUaW1lfSB0aGUgbWluIERhdGVUaW1lLCBvciB1bmRlZmluZWQgaWYgY2FsbGVkIHdpdGggbm8gYXJndW1lbnQKICAgICAqLwogICAgc3RhdGljIG1pbiguLi5kYXRlVGltZXMpIHsKICAgICAgaWYgKCFkYXRlVGltZXMuZXZlcnkoRGF0ZVRpbWUuaXNEYXRlVGltZSkpIHsKICAgICAgICB0aHJvdyBuZXcgSW52YWxpZEFyZ3VtZW50RXJyb3IoIm1pbiByZXF1aXJlcyBhbGwgYXJndW1lbnRzIGJlIERhdGVUaW1lcyIpOwogICAgICB9CiAgICAgIHJldHVybiBiZXN0QnkoZGF0ZVRpbWVzLCAoaSkgPT4gaS52YWx1ZU9mKCksIE1hdGgubWluKTsKICAgIH0KCiAgICAvKioKICAgICAqIFJldHVybiB0aGUgbWF4IG9mIHNldmVyYWwgZGF0ZSB0aW1lcwogICAgICogQHBhcmFtIHsuLi5EYXRlVGltZX0gZGF0ZVRpbWVzIC0gdGhlIERhdGVUaW1lcyBmcm9tIHdoaWNoIHRvIGNob29zZSB0aGUgbWF4aW11bQogICAgICogQHJldHVybiB7RGF0ZVRpbWV9IHRoZSBtYXggRGF0ZVRpbWUsIG9yIHVuZGVmaW5lZCBpZiBjYWxsZWQgd2l0aCBubyBhcmd1bWVudAogICAgICovCiAgICBzdGF0aWMgbWF4KC4uLmRhdGVUaW1lcykgewogICAgICBpZiAoIWRhdGVUaW1lcy5ldmVyeShEYXRlVGltZS5pc0RhdGVUaW1lKSkgewogICAgICAgIHRocm93IG5ldyBJbnZhbGlkQXJndW1lbnRFcnJvcigibWF4IHJlcXVpcmVzIGFsbCBhcmd1bWVudHMgYmUgRGF0ZVRpbWVzIik7CiAgICAgIH0KICAgICAgcmV0dXJuIGJlc3RCeShkYXRlVGltZXMsIChpKSA9PiBpLnZhbHVlT2YoKSwgTWF0aC5tYXgpOwogICAgfQoKICAgIC8vIE1JU0MKCiAgICAvKioKICAgICAqIEV4cGxhaW4gaG93IGEgc3RyaW5nIHdvdWxkIGJlIHBhcnNlZCBieSBmcm9tRm9ybWF0KCkKICAgICAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gdGhlIHN0cmluZyB0byBwYXJzZQogICAgICogQHBhcmFtIHtzdHJpbmd9IGZtdCAtIHRoZSBmb3JtYXQgdGhlIHN0cmluZyBpcyBleHBlY3RlZCB0byBiZSBpbiAoc2VlIGRlc2NyaXB0aW9uKQogICAgICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBvcHRpb25zIHRha2VuIGJ5IGZyb21Gb3JtYXQoKQogICAgICogQHJldHVybiB7T2JqZWN0fQogICAgICovCiAgICBzdGF0aWMgZnJvbUZvcm1hdEV4cGxhaW4odGV4dCwgZm10LCBvcHRpb25zID0ge30pIHsKICAgICAgY29uc3QgeyBsb2NhbGUgPSBudWxsLCBudW1iZXJpbmdTeXN0ZW0gPSBudWxsIH0gPSBvcHRpb25zLAogICAgICAgIGxvY2FsZVRvVXNlID0gTG9jYWxlLmZyb21PcHRzKHsKICAgICAgICAgIGxvY2FsZSwKICAgICAgICAgIG51bWJlcmluZ1N5c3RlbSwKICAgICAgICAgIGRlZmF1bHRUb0VOOiB0cnVlLAogICAgICAgIH0pOwogICAgICByZXR1cm4gZXhwbGFpbkZyb21Ub2tlbnMobG9jYWxlVG9Vc2UsIHRleHQsIGZtdCk7CiAgICB9CgogICAgLyoqCiAgICAgKiBAZGVwcmVjYXRlZCB1c2UgZnJvbUZvcm1hdEV4cGxhaW4gaW5zdGVhZAogICAgICovCiAgICBzdGF0aWMgZnJvbVN0cmluZ0V4cGxhaW4odGV4dCwgZm10LCBvcHRpb25zID0ge30pIHsKICAgICAgcmV0dXJuIERhdGVUaW1lLmZyb21Gb3JtYXRFeHBsYWluKHRleHQsIGZtdCwgb3B0aW9ucyk7CiAgICB9CgogICAgLy8gRk9STUFUIFBSRVNFVFMKCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgMTAvMTQvMTk4MwogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFX1NIT1JUKCkgewogICAgICByZXR1cm4gREFURV9TSE9SVDsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJ09jdCAxNCwgMTk4MycKICAgICAqIEB0eXBlIHtPYmplY3R9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgREFURV9NRUQoKSB7CiAgICAgIHJldHVybiBEQVRFX01FRDsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJ0ZyaSwgT2N0IDE0LCAxOTgzJwogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFX01FRF9XSVRIX1dFRUtEQVkoKSB7CiAgICAgIHJldHVybiBEQVRFX01FRF9XSVRIX1dFRUtEQVk7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICdPY3RvYmVyIDE0LCAxOTgzJwogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFX0ZVTEwoKSB7CiAgICAgIHJldHVybiBEQVRFX0ZVTEw7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICdUdWVzZGF5LCBPY3RvYmVyIDE0LCAxOTgzJwogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFX0hVR0UoKSB7CiAgICAgIHJldHVybiBEQVRFX0hVR0U7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICcwOTozMCBBTScuIE9ubHkgMTItaG91ciBpZiB0aGUgbG9jYWxlIGlzLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBUSU1FX1NJTVBMRSgpIHsKICAgICAgcmV0dXJuIFRJTUVfU0lNUExFOwogICAgfQoKICAgIC8qKgogICAgICoge0BsaW5rIERhdGVUaW1lI3RvTG9jYWxlU3RyaW5nfSBmb3JtYXQgbGlrZSAnMDk6MzA6MjMgQU0nLiBPbmx5IDEyLWhvdXIgaWYgdGhlIGxvY2FsZSBpcy4KICAgICAqIEB0eXBlIHtPYmplY3R9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgVElNRV9XSVRIX1NFQ09ORFMoKSB7CiAgICAgIHJldHVybiBUSU1FX1dJVEhfU0VDT05EUzsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJzA5OjMwOjIzIEFNIEVEVCcuIE9ubHkgMTItaG91ciBpZiB0aGUgbG9jYWxlIGlzLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBUSU1FX1dJVEhfU0hPUlRfT0ZGU0VUKCkgewogICAgICByZXR1cm4gVElNRV9XSVRIX1NIT1JUX09GRlNFVDsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJzA5OjMwOjIzIEFNIEVhc3Rlcm4gRGF5bGlnaHQgVGltZScuIE9ubHkgMTItaG91ciBpZiB0aGUgbG9jYWxlIGlzLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBUSU1FX1dJVEhfTE9OR19PRkZTRVQoKSB7CiAgICAgIHJldHVybiBUSU1FX1dJVEhfTE9OR19PRkZTRVQ7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICcwOTozMCcsIGFsd2F5cyAyNC1ob3VyLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBUSU1FXzI0X1NJTVBMRSgpIHsKICAgICAgcmV0dXJuIFRJTUVfMjRfU0lNUExFOwogICAgfQoKICAgIC8qKgogICAgICoge0BsaW5rIERhdGVUaW1lI3RvTG9jYWxlU3RyaW5nfSBmb3JtYXQgbGlrZSAnMDk6MzA6MjMnLCBhbHdheXMgMjQtaG91ci4KICAgICAqIEB0eXBlIHtPYmplY3R9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgVElNRV8yNF9XSVRIX1NFQ09ORFMoKSB7CiAgICAgIHJldHVybiBUSU1FXzI0X1dJVEhfU0VDT05EUzsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJzA5OjMwOjIzIEVEVCcsIGFsd2F5cyAyNC1ob3VyLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBUSU1FXzI0X1dJVEhfU0hPUlRfT0ZGU0VUKCkgewogICAgICByZXR1cm4gVElNRV8yNF9XSVRIX1NIT1JUX09GRlNFVDsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJzA5OjMwOjIzIEVhc3Rlcm4gRGF5bGlnaHQgVGltZScsIGFsd2F5cyAyNC1ob3VyLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBUSU1FXzI0X1dJVEhfTE9OR19PRkZTRVQoKSB7CiAgICAgIHJldHVybiBUSU1FXzI0X1dJVEhfTE9OR19PRkZTRVQ7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICcxMC8xNC8xOTgzLCA5OjMwIEFNJy4gT25seSAxMi1ob3VyIGlmIHRoZSBsb2NhbGUgaXMuCiAgICAgKiBAdHlwZSB7T2JqZWN0fQogICAgICovCiAgICBzdGF0aWMgZ2V0IERBVEVUSU1FX1NIT1JUKCkgewogICAgICByZXR1cm4gREFURVRJTUVfU0hPUlQ7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICcxMC8xNC8xOTgzLCA5OjMwOjMzIEFNJy4gT25seSAxMi1ob3VyIGlmIHRoZSBsb2NhbGUgaXMuCiAgICAgKiBAdHlwZSB7T2JqZWN0fQogICAgICovCiAgICBzdGF0aWMgZ2V0IERBVEVUSU1FX1NIT1JUX1dJVEhfU0VDT05EUygpIHsKICAgICAgcmV0dXJuIERBVEVUSU1FX1NIT1JUX1dJVEhfU0VDT05EUzsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJ09jdCAxNCwgMTk4MywgOTozMCBBTScuIE9ubHkgMTItaG91ciBpZiB0aGUgbG9jYWxlIGlzLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFVElNRV9NRUQoKSB7CiAgICAgIHJldHVybiBEQVRFVElNRV9NRUQ7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICdPY3QgMTQsIDE5ODMsIDk6MzA6MzMgQU0nLiBPbmx5IDEyLWhvdXIgaWYgdGhlIGxvY2FsZSBpcy4KICAgICAqIEB0eXBlIHtPYmplY3R9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgREFURVRJTUVfTUVEX1dJVEhfU0VDT05EUygpIHsKICAgICAgcmV0dXJuIERBVEVUSU1FX01FRF9XSVRIX1NFQ09ORFM7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICdGcmksIDE0IE9jdCAxOTgzLCA5OjMwIEFNJy4gT25seSAxMi1ob3VyIGlmIHRoZSBsb2NhbGUgaXMuCiAgICAgKiBAdHlwZSB7T2JqZWN0fQogICAgICovCiAgICBzdGF0aWMgZ2V0IERBVEVUSU1FX01FRF9XSVRIX1dFRUtEQVkoKSB7CiAgICAgIHJldHVybiBEQVRFVElNRV9NRURfV0lUSF9XRUVLREFZOwogICAgfQoKICAgIC8qKgogICAgICoge0BsaW5rIERhdGVUaW1lI3RvTG9jYWxlU3RyaW5nfSBmb3JtYXQgbGlrZSAnT2N0b2JlciAxNCwgMTk4MywgOTozMCBBTSBFRFQnLiBPbmx5IDEyLWhvdXIgaWYgdGhlIGxvY2FsZSBpcy4KICAgICAqIEB0eXBlIHtPYmplY3R9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgREFURVRJTUVfRlVMTCgpIHsKICAgICAgcmV0dXJuIERBVEVUSU1FX0ZVTEw7CiAgICB9CgogICAgLyoqCiAgICAgKiB7QGxpbmsgRGF0ZVRpbWUjdG9Mb2NhbGVTdHJpbmd9IGZvcm1hdCBsaWtlICdPY3RvYmVyIDE0LCAxOTgzLCA5OjMwOjMzIEFNIEVEVCcuIE9ubHkgMTItaG91ciBpZiB0aGUgbG9jYWxlIGlzLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFVElNRV9GVUxMX1dJVEhfU0VDT05EUygpIHsKICAgICAgcmV0dXJuIERBVEVUSU1FX0ZVTExfV0lUSF9TRUNPTkRTOwogICAgfQoKICAgIC8qKgogICAgICoge0BsaW5rIERhdGVUaW1lI3RvTG9jYWxlU3RyaW5nfSBmb3JtYXQgbGlrZSAnRnJpZGF5LCBPY3RvYmVyIDE0LCAxOTgzLCA5OjMwIEFNIEVhc3Rlcm4gRGF5bGlnaHQgVGltZScuIE9ubHkgMTItaG91ciBpZiB0aGUgbG9jYWxlIGlzLgogICAgICogQHR5cGUge09iamVjdH0KICAgICAqLwogICAgc3RhdGljIGdldCBEQVRFVElNRV9IVUdFKCkgewogICAgICByZXR1cm4gREFURVRJTUVfSFVHRTsKICAgIH0KCiAgICAvKioKICAgICAqIHtAbGluayBEYXRlVGltZSN0b0xvY2FsZVN0cmluZ30gZm9ybWF0IGxpa2UgJ0ZyaWRheSwgT2N0b2JlciAxNCwgMTk4MywgOTozMDozMyBBTSBFYXN0ZXJuIERheWxpZ2h0IFRpbWUnLiBPbmx5IDEyLWhvdXIgaWYgdGhlIGxvY2FsZSBpcy4KICAgICAqIEB0eXBlIHtPYmplY3R9CiAgICAgKi8KICAgIHN0YXRpYyBnZXQgREFURVRJTUVfSFVHRV9XSVRIX1NFQ09ORFMoKSB7CiAgICAgIHJldHVybiBEQVRFVElNRV9IVUdFX1dJVEhfU0VDT05EUzsKICAgIH0KICB9CgogIC8qKgogICAqIEBwcml2YXRlCiAgICovCiAgZnVuY3Rpb24gZnJpZW5kbHlEYXRlVGltZShkYXRlVGltZWlzaCkgewogICAgaWYgKERhdGVUaW1lLmlzRGF0ZVRpbWUoZGF0ZVRpbWVpc2gpKSB7CiAgICAgIHJldHVybiBkYXRlVGltZWlzaDsKICAgIH0gZWxzZSBpZiAoZGF0ZVRpbWVpc2ggJiYgZGF0ZVRpbWVpc2gudmFsdWVPZiAmJiBpc051bWJlcihkYXRlVGltZWlzaC52YWx1ZU9mKCkpKSB7CiAgICAgIHJldHVybiBEYXRlVGltZS5mcm9tSlNEYXRlKGRhdGVUaW1laXNoKTsKICAgIH0gZWxzZSBpZiAoZGF0ZVRpbWVpc2ggJiYgdHlwZW9mIGRhdGVUaW1laXNoID09PSAib2JqZWN0IikgewogICAgICByZXR1cm4gRGF0ZVRpbWUuZnJvbU9iamVjdChkYXRlVGltZWlzaCk7CiAgICB9IGVsc2UgewogICAgICB0aHJvdyBuZXcgSW52YWxpZEFyZ3VtZW50RXJyb3IoCiAgICAgICAgYFVua25vd24gZGF0ZXRpbWUgYXJndW1lbnQ6ICR7ZGF0ZVRpbWVpc2h9LCBvZiB0eXBlICR7dHlwZW9mIGRhdGVUaW1laXNofWAKICAgICAgKTsKICAgIH0KICB9CgogIC8vLy8vLy8vLy8vLy8vLy8vLy8vCiAgLy8gUXVlcnkgU2V0dGluZ3MgLy8KICAvLy8vLy8vLy8vLy8vLy8vLy8vLwogIGNvbnN0IERFRkFVTFRfUVVFUllfU0VUVElOR1MgPSB7CiAgICAgIHJlbmRlck51bGxBczogIlxcLSIsCiAgICAgIHRhc2tDb21wbGV0aW9uVHJhY2tpbmc6IGZhbHNlLAogICAgICB0YXNrQ29tcGxldGlvblVzZUVtb2ppU2hvcnRoYW5kOiBmYWxzZSwKICAgICAgdGFza0NvbXBsZXRpb25UZXh0OiAiY29tcGxldGlvbiIsCiAgICAgIHRhc2tDb21wbGV0aW9uRGF0ZUZvcm1hdDogInl5eXktTU0tZGQiLAogICAgICByZWN1cnNpdmVTdWJUYXNrQ29tcGxldGlvbjogZmFsc2UsCiAgICAgIHdhcm5PbkVtcHR5UmVzdWx0OiB0cnVlLAogICAgICByZWZyZXNoRW5hYmxlZDogdHJ1ZSwKICAgICAgcmVmcmVzaEludGVydmFsOiAyNTAwLAogICAgICBkZWZhdWx0RGF0ZUZvcm1hdDogIk1NTU0gZGQsIHl5eXkiLAogICAgICBkZWZhdWx0RGF0ZVRpbWVGb3JtYXQ6ICJoOm1tIGEgLSBNTU1NIGRkLCB5eXl5IiwKICAgICAgbWF4UmVjdXJzaXZlUmVuZGVyRGVwdGg6IDQsCiAgICAgIHRhYmxlSWRDb2x1bW5OYW1lOiAiRmlsZSIsCiAgICAgIHRhYmxlR3JvdXBDb2x1bW5OYW1lOiAiR3JvdXAiLAogICAgICBzaG93UmVzdWx0Q291bnQ6IHRydWUsCiAgfTsKICBjb25zdCBERUZBVUxUX0VYUE9SVF9TRVRUSU5HUyA9IHsKICAgICAgYWxsb3dIdG1sOiB0cnVlLAogIH07CiAgLyoqIERlZmF1bHQgc2V0dGluZ3MgZm9yIGRhdGF2aWV3IG9uIGluc3RhbGwuICovCiAgKHsKICAgICAgLi4uREVGQVVMVF9RVUVSWV9TRVRUSU5HUywKICAgICAgLi4uREVGQVVMVF9FWFBPUlRfU0VUVElOR1MsCiAgICAgIC4uLnsKICAgICAgICAgIGlubGluZVF1ZXJ5UHJlZml4OiAiPSIsCiAgICAgICAgICBpbmxpbmVKc1F1ZXJ5UHJlZml4OiAiJD0iLAogICAgICAgICAgaW5saW5lUXVlcmllc0luQ29kZWJsb2NrczogdHJ1ZSwKICAgICAgICAgIGVuYWJsZUlubGluZURhdGF2aWV3OiB0cnVlLAogICAgICAgICAgZW5hYmxlRGF0YXZpZXdKczogZmFsc2UsCiAgICAgICAgICBlbmFibGVJbmxpbmVEYXRhdmlld0pzOiBmYWxzZSwKICAgICAgICAgIHByZXR0eVJlbmRlcklubGluZUZpZWxkczogdHJ1ZSwKICAgICAgICAgIHByZXR0eVJlbmRlcklubGluZUZpZWxkc0luTGl2ZVByZXZpZXc6IHRydWUsCiAgICAgICAgICBkYXRhdmlld0pzS2V5d29yZDogImRhdGF2aWV3anMiLAogICAgICB9LAogIH0pOwoKICAvKiogRnVuY3Rpb25hbCByZXR1cm4gdHlwZSBmb3IgZXJyb3IgaGFuZGxpbmcuICovCiAgY2xhc3MgU3VjY2VzcyB7CiAgICAgIHZhbHVlOwogICAgICBzdWNjZXNzZnVsOwogICAgICBjb25zdHJ1Y3Rvcih2YWx1ZSkgewogICAgICAgICAgdGhpcy52YWx1ZSA9IHZhbHVlOwogICAgICAgICAgdGhpcy5zdWNjZXNzZnVsID0gdHJ1ZTsKICAgICAgfQogICAgICBtYXAoZikgewogICAgICAgICAgcmV0dXJuIG5ldyBTdWNjZXNzKGYodGhpcy52YWx1ZSkpOwogICAgICB9CiAgICAgIGZsYXRNYXAoZikgewogICAgICAgICAgcmV0dXJuIGYodGhpcy52YWx1ZSk7CiAgICAgIH0KICAgICAgbWFwRXJyKGYpIHsKICAgICAgICAgIHJldHVybiB0aGlzOwogICAgICB9CiAgICAgIGJpbWFwKHN1Y2MsIF9mYWlsKSB7CiAgICAgICAgICByZXR1cm4gdGhpcy5tYXAoc3VjYyk7CiAgICAgIH0KICAgICAgb3JFbHNlKF92YWx1ZSkgewogICAgICAgICAgcmV0dXJuIHRoaXMudmFsdWU7CiAgICAgIH0KICAgICAgY2FzdCgpIHsKICAgICAgICAgIHJldHVybiB0aGlzOwogICAgICB9CiAgICAgIG9yRWxzZVRocm93KF9tZXNzYWdlKSB7CiAgICAgICAgICByZXR1cm4gdGhpcy52YWx1ZTsKICAgICAgfQogIH0KICAvKiogRnVuY3Rpb25hbCByZXR1cm4gdHlwZSBmb3IgZXJyb3IgaGFuZGxpbmcuICovCiAgY2xhc3MgRmFpbHVyZSB7CiAgICAgIGVycm9yOwogICAgICBzdWNjZXNzZnVsOwogICAgICBjb25zdHJ1Y3RvcihlcnJvcikgewogICAgICAgICAgdGhpcy5lcnJvciA9IGVycm9yOwogICAgICAgICAgdGhpcy5zdWNjZXNzZnVsID0gZmFsc2U7CiAgICAgIH0KICAgICAgbWFwKF9mKSB7CiAgICAgICAgICByZXR1cm4gdGhpczsKICAgICAgfQogICAgICBmbGF0TWFwKF9mKSB7CiAgICAgICAgICByZXR1cm4gdGhpczsKICAgICAgfQogICAgICBtYXBFcnIoZikgewogICAgICAgICAgcmV0dXJuIG5ldyBGYWlsdXJlKGYodGhpcy5lcnJvcikpOwogICAgICB9CiAgICAgIGJpbWFwKF9zdWNjLCBmYWlsKSB7CiAgICAgICAgICByZXR1cm4gdGhpcy5tYXBFcnIoZmFpbCk7CiAgICAgIH0KICAgICAgb3JFbHNlKHZhbHVlKSB7CiAgICAgICAgICByZXR1cm4gdmFsdWU7CiAgICAgIH0KICAgICAgY2FzdCgpIHsKICAgICAgICAgIHJldHVybiB0aGlzOwogICAgICB9CiAgICAgIG9yRWxzZVRocm93KG1lc3NhZ2UpIHsKICAgICAgICAgIGlmIChtZXNzYWdlKQogICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihtZXNzYWdlKHRoaXMuZXJyb3IpKTsKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoIiIgKyB0aGlzLmVycm9yKTsKICAgICAgfQogIH0KICAvKiogTW9uYWRpYyAnUmVzdWx0JyB0eXBlIHdoaWNoIGVuY2Fwc3VsYXRlcyB3aGV0aGVyIGEgcHJvY2VkdXJlIHN1Y2NlZWRlZCBvciBmYWlsZWQsIGFzIHdlbGwgYXMgaXQncyByZXR1cm4gdmFsdWUuICovCiAgdmFyIFJlc3VsdDsKICAoZnVuY3Rpb24gKFJlc3VsdCkgewogICAgICAvKiogQ29uc3RydWN0IGEgbmV3IHN1Y2Nlc3MgcmVzdWx0IHdyYXBwaW5nIHRoZSBnaXZlbiB2YWx1ZS4gKi8KICAgICAgZnVuY3Rpb24gc3VjY2Vzcyh2YWx1ZSkgewogICAgICAgICAgcmV0dXJuIG5ldyBTdWNjZXNzKHZhbHVlKTsKICAgICAgfQogICAgICBSZXN1bHQuc3VjY2VzcyA9IHN1Y2Nlc3M7CiAgICAgIC8qKiBDb25zdHJ1Y3QgYSBuZXcgZmFpbHVyZSB2YWx1ZSB3cmFwcGluZyB0aGUgZ2l2ZW4gZXJyb3IuICovCiAgICAgIGZ1bmN0aW9uIGZhaWx1cmUoZXJyb3IpIHsKICAgICAgICAgIHJldHVybiBuZXcgRmFpbHVyZShlcnJvcik7CiAgICAgIH0KICAgICAgUmVzdWx0LmZhaWx1cmUgPSBmYWlsdXJlOwogICAgICAvKiogSm9pbiB0d28gcmVzdWx0cyB3aXRoIGEgYmktZnVuY3Rpb24gYW5kIHJldHVybiBhIG5ldyByZXN1bHQuICovCiAgICAgIGZ1bmN0aW9uIGZsYXRNYXAyKGZpcnN0LCBzZWNvbmQsIGYpIHsKICAgICAgICAgIGlmIChmaXJzdC5zdWNjZXNzZnVsKSB7CiAgICAgICAgICAgICAgaWYgKHNlY29uZC5zdWNjZXNzZnVsKQogICAgICAgICAgICAgICAgICByZXR1cm4gZihmaXJzdC52YWx1ZSwgc2Vjb25kLnZhbHVlKTsKICAgICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICAgIHJldHVybiBmYWlsdXJlKHNlY29uZC5lcnJvcik7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICByZXR1cm4gZmFpbHVyZShmaXJzdC5lcnJvcik7CiAgICAgICAgICB9CiAgICAgIH0KICAgICAgUmVzdWx0LmZsYXRNYXAyID0gZmxhdE1hcDI7CiAgICAgIC8qKiBKb2luIHR3byByZXN1bHRzIHdpdGggYSBiaS1mdW5jdGlvbiBhbmQgcmV0dXJuIGEgbmV3IHJlc3VsdC4gKi8KICAgICAgZnVuY3Rpb24gbWFwMihmaXJzdCwgc2Vjb25kLCBmKSB7CiAgICAgICAgICByZXR1cm4gZmxhdE1hcDIoZmlyc3QsIHNlY29uZCwgKGEsIGIpID0+IHN1Y2Nlc3MoZihhLCBiKSkpOwogICAgICB9CiAgICAgIFJlc3VsdC5tYXAyID0gbWFwMjsKICB9KShSZXN1bHQgfHwgKFJlc3VsdCA9IHt9KSk7CgogIHZhciBjb21tb25qc0dsb2JhbCA9IHR5cGVvZiBnbG9iYWxUaGlzICE9PSAndW5kZWZpbmVkJyA/IGdsb2JhbFRoaXMgOiB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IHR5cGVvZiBnbG9iYWwgIT09ICd1bmRlZmluZWQnID8gZ2xvYmFsIDogdHlwZW9mIHNlbGYgIT09ICd1bmRlZmluZWQnID8gc2VsZiA6IHt9OwoKICB2YXIgcGFyc2ltbW9uX3VtZF9taW4gPSB7ZXhwb3J0czoge319OwoKICBwYXJzaW1tb25fdW1kX21pbi5leHBvcnRzOwoKICAoZnVuY3Rpb24gKG1vZHVsZSwgZXhwb3J0cykgewogIAkhZnVuY3Rpb24obix0KXttb2R1bGUuZXhwb3J0cz10KCk7fSgidW5kZWZpbmVkIiE9dHlwZW9mIHNlbGY/c2VsZjpjb21tb25qc0dsb2JhbCxmdW5jdGlvbigpe3JldHVybiBmdW5jdGlvbihuKXt2YXIgdD17fTtmdW5jdGlvbiByKGUpe2lmKHRbZV0pcmV0dXJuIHRbZV0uZXhwb3J0czt2YXIgdT10W2VdPXtpOmUsbDohMSxleHBvcnRzOnt9fTtyZXR1cm4gbltlXS5jYWxsKHUuZXhwb3J0cyx1LHUuZXhwb3J0cyxyKSx1Lmw9ITAsdS5leHBvcnRzfXJldHVybiByLm09bixyLmM9dCxyLmQ9ZnVuY3Rpb24obix0LGUpe3IubyhuLHQpfHxPYmplY3QuZGVmaW5lUHJvcGVydHkobix0LHtjb25maWd1cmFibGU6ITEsZW51bWVyYWJsZTohMCxnZXQ6ZX0pO30sci5yPWZ1bmN0aW9uKG4pe09iamVjdC5kZWZpbmVQcm9wZXJ0eShuLCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KTt9LHIubj1mdW5jdGlvbihuKXt2YXIgdD1uJiZuLl9fZXNNb2R1bGU/ZnVuY3Rpb24oKXtyZXR1cm4gbi5kZWZhdWx0fTpmdW5jdGlvbigpe3JldHVybiBufTtyZXR1cm4gci5kKHQsImEiLHQpLHR9LHIubz1mdW5jdGlvbihuLHQpe3JldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwobix0KX0sci5wPSIiLHIoci5zPTApfShbZnVuY3Rpb24obix0LHIpe2Z1bmN0aW9uIGUobil7aWYoISh0aGlzIGluc3RhbmNlb2YgZSkpcmV0dXJuIG5ldyBlKG4pO3RoaXMuXz1uO312YXIgdT1lLnByb3RvdHlwZTtmdW5jdGlvbiBvKG4sdCl7Zm9yKHZhciByPTA7cjxuO3IrKyl0KHIpO31mdW5jdGlvbiBpKG4sdCxyKXtyZXR1cm4gZnVuY3Rpb24obix0KXtvKHQubGVuZ3RoLGZ1bmN0aW9uKHIpe24odFtyXSxyLHQpO30pO30oZnVuY3Rpb24ocixlLHUpe3Q9bih0LHIsZSx1KTt9LHIpLHR9ZnVuY3Rpb24gYShuLHQpe3JldHVybiBpKGZ1bmN0aW9uKHQscixlLHUpe3JldHVybiB0LmNvbmNhdChbbihyLGUsdSldKX0sW10sdCl9ZnVuY3Rpb24gZihuLHQpe3ZhciByPXt2OjAsYnVmOnR9O3JldHVybiBvKG4sZnVuY3Rpb24oKXt2YXIgbjtyPXt2OnIudjw8MXwobj1yLmJ1ZixuWzBdPj43KSxidWY6ZnVuY3Rpb24obil7dmFyIHQ9aShmdW5jdGlvbihuLHQscixlKXtyZXR1cm4gbi5jb25jYXQocj09PWUubGVuZ3RoLTE/QnVmZmVyLmZyb20oW3QsMF0pLnJlYWRVSW50MTZCRSgwKTplLnJlYWRVSW50MTZCRShyKSl9LFtdLG4pO3JldHVybiBCdWZmZXIuZnJvbShhKGZ1bmN0aW9uKG4pe3JldHVybiAobjw8MSY2NTUzNSk+Pjh9LHQpKX0oci5idWYpfTt9KSxyfWZ1bmN0aW9uIGMoKXtyZXR1cm4gInVuZGVmaW5lZCIhPXR5cGVvZiBCdWZmZXJ9ZnVuY3Rpb24gcygpe2lmKCFjKCkpdGhyb3cgbmV3IEVycm9yKCJCdWZmZXIgZ2xvYmFsIGRvZXMgbm90IGV4aXN0OyBwbGVhc2UgdXNlIHdlYnBhY2sgaWYgeW91IG5lZWQgdG8gcGFyc2UgQnVmZmVycyBpbiB0aGUgYnJvd3Nlci4iKX1mdW5jdGlvbiBsKG4pe3MoKTt2YXIgdD1pKGZ1bmN0aW9uKG4sdCl7cmV0dXJuIG4rdH0sMCxuKTtpZih0JTghPTApdGhyb3cgbmV3IEVycm9yKCJUaGUgYml0cyBbIituLmpvaW4oIiwgIikrIl0gYWRkIHVwIHRvICIrdCsiIHdoaWNoIGlzIG5vdCBhbiBldmVuIG51bWJlciBvZiBieXRlczsgdGhlIHRvdGFsIHNob3VsZCBiZSBkaXZpc2libGUgYnkgOCIpO3ZhciByLHU9dC84LG89KHI9ZnVuY3Rpb24obil7cmV0dXJuIG4+NDh9LGkoZnVuY3Rpb24obix0KXtyZXR1cm4gbnx8KHIodCk/dDpuKX0sbnVsbCxuKSk7aWYobyl0aHJvdyBuZXcgRXJyb3IobysiIGJpdCByYW5nZSByZXF1ZXN0ZWQgZXhjZWVkcyA0OCBiaXQgKDYgYnl0ZSkgTnVtYmVyIG1heC4iKTtyZXR1cm4gbmV3IGUoZnVuY3Rpb24odCxyKXt2YXIgZT11K3I7cmV0dXJuIGU+dC5sZW5ndGg/eChyLHUudG9TdHJpbmcoKSsiIGJ5dGVzIik6YihlLGkoZnVuY3Rpb24obix0KXt2YXIgcj1mKHQsbi5idWYpO3JldHVybiB7Y29sbDpuLmNvbGwuY29uY2F0KHIudiksYnVmOnIuYnVmfX0se2NvbGw6W10sYnVmOnQuc2xpY2UocixlKX0sbikuY29sbCl9KX1mdW5jdGlvbiBoKG4sdCl7cmV0dXJuIG5ldyBlKGZ1bmN0aW9uKHIsZSl7cmV0dXJuIHMoKSxlK3Q+ci5sZW5ndGg/eChlLHQrIiBieXRlcyBmb3IgIituKTpiKGUrdCxyLnNsaWNlKGUsZSt0KSl9KX1mdW5jdGlvbiBwKG4sdCl7aWYoIm51bWJlciIhPXR5cGVvZihyPXQpfHxNYXRoLmZsb29yKHIpIT09cnx8dDwwfHx0PjYpdGhyb3cgbmV3IEVycm9yKG4rIiByZXF1aXJlcyBpbnRlZ2VyIGxlbmd0aCBpbiByYW5nZSBbMCwgNl0uIik7dmFyIHI7fWZ1bmN0aW9uIGQobil7cmV0dXJuIHAoInVpbnRCRSIsbiksaCgidWludEJFKCIrbisiKSIsbikubWFwKGZ1bmN0aW9uKHQpe3JldHVybiB0LnJlYWRVSW50QkUoMCxuKX0pfWZ1bmN0aW9uIHYobil7cmV0dXJuIHAoInVpbnRMRSIsbiksaCgidWludExFKCIrbisiKSIsbikubWFwKGZ1bmN0aW9uKHQpe3JldHVybiB0LnJlYWRVSW50TEUoMCxuKX0pfWZ1bmN0aW9uIGcobil7cmV0dXJuIHAoImludEJFIixuKSxoKCJpbnRCRSgiK24rIikiLG4pLm1hcChmdW5jdGlvbih0KXtyZXR1cm4gdC5yZWFkSW50QkUoMCxuKX0pfWZ1bmN0aW9uIG0obil7cmV0dXJuIHAoImludExFIixuKSxoKCJpbnRMRSgiK24rIikiLG4pLm1hcChmdW5jdGlvbih0KXtyZXR1cm4gdC5yZWFkSW50TEUoMCxuKX0pfWZ1bmN0aW9uIHkobil7cmV0dXJuIG4gaW5zdGFuY2VvZiBlfWZ1bmN0aW9uIEUobil7cmV0dXJuICJbb2JqZWN0IEFycmF5XSI9PT17fS50b1N0cmluZy5jYWxsKG4pfWZ1bmN0aW9uIHcobil7cmV0dXJuIGMoKSYmQnVmZmVyLmlzQnVmZmVyKG4pfWZ1bmN0aW9uIGIobix0KXtyZXR1cm4ge3N0YXR1czohMCxpbmRleDpuLHZhbHVlOnQsZnVydGhlc3Q6LTEsZXhwZWN0ZWQ6W119fWZ1bmN0aW9uIHgobix0KXtyZXR1cm4gRSh0KXx8KHQ9W3RdKSx7c3RhdHVzOiExLGluZGV4Oi0xLHZhbHVlOm51bGwsZnVydGhlc3Q6bixleHBlY3RlZDp0fX1mdW5jdGlvbiBCKG4sdCl7aWYoIXQpcmV0dXJuIG47aWYobi5mdXJ0aGVzdD50LmZ1cnRoZXN0KXJldHVybiBuO3ZhciByPW4uZnVydGhlc3Q9PT10LmZ1cnRoZXN0P2Z1bmN0aW9uKG4sdCl7aWYoZnVuY3Rpb24oKXtpZih2b2lkIDAhPT1lLl9zdXBwb3J0c1NldClyZXR1cm4gZS5fc3VwcG9ydHNTZXQ7dmFyIG49InVuZGVmaW5lZCIhPXR5cGVvZiBTZXQ7cmV0dXJuIGUuX3N1cHBvcnRzU2V0PW4sbn0oKSYmQXJyYXkuZnJvbSl7Zm9yKHZhciByPW5ldyBTZXQobiksdT0wO3U8dC5sZW5ndGg7dSsrKXIuYWRkKHRbdV0pO3ZhciBvPUFycmF5LmZyb20ocik7cmV0dXJuIG8uc29ydCgpLG99Zm9yKHZhciBpPXt9LGE9MDthPG4ubGVuZ3RoO2ErKylpW25bYV1dPSEwO2Zvcih2YXIgZj0wO2Y8dC5sZW5ndGg7ZisrKWlbdFtmXV09ITA7dmFyIGM9W107Zm9yKHZhciBzIGluIGkpKHt9KS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGkscykmJmMucHVzaChzKTtyZXR1cm4gYy5zb3J0KCksY30obi5leHBlY3RlZCx0LmV4cGVjdGVkKTp0LmV4cGVjdGVkO3JldHVybiB7c3RhdHVzOm4uc3RhdHVzLGluZGV4Om4uaW5kZXgsdmFsdWU6bi52YWx1ZSxmdXJ0aGVzdDp0LmZ1cnRoZXN0LGV4cGVjdGVkOnJ9fXZhciBqPXt9O2Z1bmN0aW9uIFMobix0KXtpZih3KG4pKXJldHVybiB7b2Zmc2V0OnQsbGluZTotMSxjb2x1bW46LTF9O24gaW4ganx8KGpbbl09e30pO2Zvcih2YXIgcj1qW25dLGU9MCx1PTAsbz0wLGk9dDtpPj0wOyl7aWYoaSBpbiByKXtlPXJbaV0ubGluZSwwPT09byYmKG89cltpXS5saW5lU3RhcnQpO2JyZWFrfSgiXG4iPT09bi5jaGFyQXQoaSl8fCJcciI9PT1uLmNoYXJBdChpKSYmIlxuIiE9PW4uY2hhckF0KGkrMSkpJiYodSsrLDA9PT1vJiYobz1pKzEpKSxpLS07fXZhciBhPWUrdSxmPXQtbztyZXR1cm4gclt0XT17bGluZTphLGxpbmVTdGFydDpvfSx7b2Zmc2V0OnQsbGluZTphKzEsY29sdW1uOmYrMX19ZnVuY3Rpb24gXyhuKXtpZigheShuKSl0aHJvdyBuZXcgRXJyb3IoIm5vdCBhIHBhcnNlcjogIituKX1mdW5jdGlvbiBMKG4sdCl7cmV0dXJuICJzdHJpbmciPT10eXBlb2Ygbj9uLmNoYXJBdCh0KTpuW3RdfWZ1bmN0aW9uIE8obil7aWYoIm51bWJlciIhPXR5cGVvZiBuKXRocm93IG5ldyBFcnJvcigibm90IGEgbnVtYmVyOiAiK24pfWZ1bmN0aW9uIGsobil7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIG4pdGhyb3cgbmV3IEVycm9yKCJub3QgYSBmdW5jdGlvbjogIituKX1mdW5jdGlvbiBQKG4pe2lmKCJzdHJpbmciIT10eXBlb2Ygbil0aHJvdyBuZXcgRXJyb3IoIm5vdCBhIHN0cmluZzogIituKX12YXIgcT0yLEE9MyxJPTgsRj01KkksTT00Kkksej0iICAiO2Z1bmN0aW9uIFIobix0KXtyZXR1cm4gbmV3IEFycmF5KHQrMSkuam9pbihuKX1mdW5jdGlvbiBVKG4sdCxyKXt2YXIgZT10LW4ubGVuZ3RoO3JldHVybiBlPD0wP246UihyLGUpK259ZnVuY3Rpb24gVyhuLHQscixlKXtyZXR1cm4ge2Zyb206bi10PjA/bi10OjAsdG86bityPmU/ZTpuK3J9fWZ1bmN0aW9uIEQobix0KXt2YXIgcixlLHUsbyxmLGM9dC5pbmRleCxzPWMub2Zmc2V0LGw9MTtpZihzPT09bi5sZW5ndGgpcmV0dXJuICJHb3QgdGhlIGVuZCBvZiB0aGUgaW5wdXQiO2lmKHcobikpe3ZhciBoPXMtcyVJLHA9cy1oLGQ9VyhoLEYsTStJLG4ubGVuZ3RoKSx2PWEoZnVuY3Rpb24obil7cmV0dXJuIGEoZnVuY3Rpb24obil7cmV0dXJuIFUobi50b1N0cmluZygxNiksMiwiMCIpfSxuKX0sZnVuY3Rpb24obix0KXt2YXIgcj1uLmxlbmd0aCxlPVtdLHU9MDtpZihyPD10KXJldHVybiBbbi5zbGljZSgpXTtmb3IodmFyIG89MDtvPHI7bysrKWVbdV18fGUucHVzaChbXSksZVt1XS5wdXNoKG5bb10pLChvKzEpJXQ9PTAmJnUrKztyZXR1cm4gZX0obi5zbGljZShkLmZyb20sZC50bykudG9KU09OKCkuZGF0YSxJKSk7bz1mdW5jdGlvbihuKXtyZXR1cm4gMD09PW4uZnJvbSYmMT09PW4udG8/e2Zyb206bi5mcm9tLHRvOm4udG99Ontmcm9tOm4uZnJvbS9JLHRvOk1hdGguZmxvb3Iobi50by9JKX19KGQpLGU9aC9JLHI9MypwLHA+PTQmJihyKz0xKSxsPTIsdT1hKGZ1bmN0aW9uKG4pe3JldHVybiBuLmxlbmd0aDw9ND9uLmpvaW4oIiAiKTpuLnNsaWNlKDAsNCkuam9pbigiICIpKyIgICIrbi5zbGljZSg0KS5qb2luKCIgIil9LHYpLChmPSg4KihvLnRvPjA/by50by0xOm8udG8pKS50b1N0cmluZygxNikubGVuZ3RoKTwyJiYoZj0yKTt9ZWxzZSB7dmFyIGc9bi5zcGxpdCgvXHJcbnxbXG5cclx1MjAyOFx1MjAyOV0vKTtyPWMuY29sdW1uLTEsZT1jLmxpbmUtMSxvPVcoZSxxLEEsZy5sZW5ndGgpLHU9Zy5zbGljZShvLmZyb20sby50byksZj1vLnRvLnRvU3RyaW5nKCkubGVuZ3RoO312YXIgbT1lLW8uZnJvbTtyZXR1cm4gdyhuKSYmKGY9KDgqKG8udG8+MD9vLnRvLTE6by50bykpLnRvU3RyaW5nKDE2KS5sZW5ndGgpPDImJihmPTIpLGkoZnVuY3Rpb24odCxlLHUpe3ZhciBpLGE9dT09PW0sYz1hPyI+ICI6ejtyZXR1cm4gaT13KG4pP1UoKDgqKG8uZnJvbSt1KSkudG9TdHJpbmcoMTYpLGYsIjAiKTpVKChvLmZyb20rdSsxKS50b1N0cmluZygpLGYsIiAiKSxbXS5jb25jYXQodCxbYytpKyIgfCAiK2VdLGE/W3orUigiICIsZikrIiB8ICIrVSgiIixyLCIgIikrUigiXiIsbCldOltdKX0sW10sdSkuam9pbigiXG4iKX1mdW5jdGlvbiBOKG4sdCl7cmV0dXJuIFsiXG4iLCItLSBQQVJTSU5HIEZBSUxFRCAiK1IoIi0iLDUwKSwiXG5cbiIsRChuLHQpLCJcblxuIiwocj10LmV4cGVjdGVkLDE9PT1yLmxlbmd0aD8iRXhwZWN0ZWQ6XG5cbiIrclswXToiRXhwZWN0ZWQgb25lIG9mIHRoZSBmb2xsb3dpbmc6IFxuXG4iK3Iuam9pbigiLCAiKSksIlxuIl0uam9pbigiIik7dmFyIHI7fWZ1bmN0aW9uIEcobil7cmV0dXJuIHZvaWQgMCE9PW4uZmxhZ3M/bi5mbGFnczpbbi5nbG9iYWw/ImciOiIiLG4uaWdub3JlQ2FzZT8iaSI6IiIsbi5tdWx0aWxpbmU/Im0iOiIiLG4udW5pY29kZT8idSI6IiIsbi5zdGlja3k/InkiOiIiXS5qb2luKCIiKX1mdW5jdGlvbiBDKCl7Zm9yKHZhciBuPVtdLnNsaWNlLmNhbGwoYXJndW1lbnRzKSx0PW4ubGVuZ3RoLHI9MDtyPHQ7cis9MSlfKG5bcl0pO3JldHVybiBlKGZ1bmN0aW9uKHIsZSl7Zm9yKHZhciB1LG89bmV3IEFycmF5KHQpLGk9MDtpPHQ7aSs9MSl7aWYoISh1PUIobltpXS5fKHIsZSksdSkpLnN0YXR1cylyZXR1cm4gdTtvW2ldPXUudmFsdWUsZT11LmluZGV4O31yZXR1cm4gQihiKGUsbyksdSl9KX1mdW5jdGlvbiBKKCl7dmFyIG49W10uc2xpY2UuY2FsbChhcmd1bWVudHMpO2lmKDA9PT1uLmxlbmd0aCl0aHJvdyBuZXcgRXJyb3IoInNlcU1hcCBuZWVkcyBhdCBsZWFzdCBvbmUgYXJndW1lbnQiKTt2YXIgdD1uLnBvcCgpO3JldHVybiBrKHQpLEMuYXBwbHkobnVsbCxuKS5tYXAoZnVuY3Rpb24obil7cmV0dXJuIHQuYXBwbHkobnVsbCxuKX0pfWZ1bmN0aW9uIFQoKXt2YXIgbj1bXS5zbGljZS5jYWxsKGFyZ3VtZW50cyksdD1uLmxlbmd0aDtpZigwPT09dClyZXR1cm4gWSgiemVybyBhbHRlcm5hdGVzIik7Zm9yKHZhciByPTA7cjx0O3IrPTEpXyhuW3JdKTtyZXR1cm4gZShmdW5jdGlvbih0LHIpe2Zvcih2YXIgZSx1PTA7dTxuLmxlbmd0aDt1Kz0xKWlmKChlPUIoblt1XS5fKHQsciksZSkpLnN0YXR1cylyZXR1cm4gZTtyZXR1cm4gZX0pfWZ1bmN0aW9uIFYobix0KXtyZXR1cm4gSChuLHQpLm9yKFgoW10pKX1mdW5jdGlvbiBIKG4sdCl7cmV0dXJuIF8obiksXyh0KSxKKG4sdC50aGVuKG4pLm1hbnkoKSxmdW5jdGlvbihuLHQpe3JldHVybiBbbl0uY29uY2F0KHQpfSl9ZnVuY3Rpb24gSyhuKXtQKG4pO3ZhciB0PSInIituKyInIjtyZXR1cm4gZShmdW5jdGlvbihyLGUpe3ZhciB1PWUrbi5sZW5ndGgsbz1yLnNsaWNlKGUsdSk7cmV0dXJuIG89PT1uP2IodSxvKTp4KGUsdCl9KX1mdW5jdGlvbiBRKG4sdCl7IWZ1bmN0aW9uKG4pe2lmKCEobiBpbnN0YW5jZW9mIFJlZ0V4cCkpdGhyb3cgbmV3IEVycm9yKCJub3QgYSByZWdleHA6ICIrbik7Zm9yKHZhciB0PUcobikscj0wO3I8dC5sZW5ndGg7cisrKXt2YXIgZT10LmNoYXJBdChyKTtpZigiaSIhPT1lJiYibSIhPT1lJiYidSIhPT1lJiYicyIhPT1lKXRocm93IG5ldyBFcnJvcigndW5zdXBwb3J0ZWQgcmVnZXhwIGZsYWcgIicrZSsnIjogJytuKX19KG4pLGFyZ3VtZW50cy5sZW5ndGg+PTI/Tyh0KTp0PTA7dmFyIHI9ZnVuY3Rpb24obil7cmV0dXJuIFJlZ0V4cCgiXig/OiIrbi5zb3VyY2UrIikiLEcobikpfShuKSx1PSIiK247cmV0dXJuIGUoZnVuY3Rpb24obixlKXt2YXIgbz1yLmV4ZWMobi5zbGljZShlKSk7aWYobyl7aWYoMDw9dCYmdDw9by5sZW5ndGgpe3ZhciBpPW9bMF0sYT1vW3RdO3JldHVybiBiKGUraS5sZW5ndGgsYSl9cmV0dXJuIHgoZSwidmFsaWQgbWF0Y2ggZ3JvdXAgKDAgdG8gIitvLmxlbmd0aCsiKSBpbiAiK3UpfXJldHVybiB4KGUsdSl9KX1mdW5jdGlvbiBYKG4pe3JldHVybiBlKGZ1bmN0aW9uKHQscil7cmV0dXJuIGIocixuKX0pfWZ1bmN0aW9uIFkobil7cmV0dXJuIGUoZnVuY3Rpb24odCxyKXtyZXR1cm4geChyLG4pfSl9ZnVuY3Rpb24gWihuKXtpZih5KG4pKXJldHVybiBlKGZ1bmN0aW9uKHQscil7dmFyIGU9bi5fKHQscik7cmV0dXJuIGUuaW5kZXg9cixlLnZhbHVlPSIiLGV9KTtpZigic3RyaW5nIj09dHlwZW9mIG4pcmV0dXJuIFooSyhuKSk7aWYobiBpbnN0YW5jZW9mIFJlZ0V4cClyZXR1cm4gWihRKG4pKTt0aHJvdyBuZXcgRXJyb3IoIm5vdCBhIHN0cmluZywgcmVnZXhwLCBvciBwYXJzZXI6ICIrbil9ZnVuY3Rpb24gJChuKXtyZXR1cm4gXyhuKSxlKGZ1bmN0aW9uKHQscil7dmFyIGU9bi5fKHQsciksdT10LnNsaWNlKHIsZS5pbmRleCk7cmV0dXJuIGUuc3RhdHVzP3gociwnbm90ICInK3UrJyInKTpiKHIsbnVsbCl9KX1mdW5jdGlvbiBubihuKXtyZXR1cm4gayhuKSxlKGZ1bmN0aW9uKHQscil7dmFyIGU9TCh0LHIpO3JldHVybiByPHQubGVuZ3RoJiZuKGUpP2IocisxLGUpOngociwiYSBjaGFyYWN0ZXIvYnl0ZSBtYXRjaGluZyAiK24pfSl9ZnVuY3Rpb24gdG4obix0KXthcmd1bWVudHMubGVuZ3RoPDImJih0PW4sbj12b2lkIDApO3ZhciByPWUoZnVuY3Rpb24obixlKXtyZXR1cm4gci5fPXQoKS5fLHIuXyhuLGUpfSk7cmV0dXJuIG4/ci5kZXNjKG4pOnJ9ZnVuY3Rpb24gcm4oKXtyZXR1cm4gWSgiZmFudGFzeS1sYW5kL2VtcHR5Iil9dS5wYXJzZT1mdW5jdGlvbihuKXtpZigic3RyaW5nIiE9dHlwZW9mIG4mJiF3KG4pKXRocm93IG5ldyBFcnJvcigiLnBhcnNlIG11c3QgYmUgY2FsbGVkIHdpdGggYSBzdHJpbmcgb3IgQnVmZmVyIGFzIGl0cyBhcmd1bWVudCIpO3ZhciB0LHI9dGhpcy5za2lwKGFuKS5fKG4sMCk7cmV0dXJuIHQ9ci5zdGF0dXM/e3N0YXR1czohMCx2YWx1ZTpyLnZhbHVlfTp7c3RhdHVzOiExLGluZGV4OlMobixyLmZ1cnRoZXN0KSxleHBlY3RlZDpyLmV4cGVjdGVkfSxkZWxldGUgaltuXSx0fSx1LnRyeVBhcnNlPWZ1bmN0aW9uKG4pe3ZhciB0PXRoaXMucGFyc2Uobik7aWYodC5zdGF0dXMpcmV0dXJuIHQudmFsdWU7dmFyIHI9TihuLHQpLGU9bmV3IEVycm9yKHIpO3Rocm93IGUudHlwZT0iUGFyc2ltbW9uRXJyb3IiLGUucmVzdWx0PXQsZX0sdS5hc3NlcnQ9ZnVuY3Rpb24obix0KXtyZXR1cm4gdGhpcy5jaGFpbihmdW5jdGlvbihyKXtyZXR1cm4gbihyKT9YKHIpOlkodCl9KX0sdS5vcj1mdW5jdGlvbihuKXtyZXR1cm4gVCh0aGlzLG4pfSx1LnRyaW09ZnVuY3Rpb24obil7cmV0dXJuIHRoaXMud3JhcChuLG4pfSx1LndyYXA9ZnVuY3Rpb24obix0KXtyZXR1cm4gSihuLHRoaXMsdCxmdW5jdGlvbihuLHQpe3JldHVybiB0fSl9LHUudGhydT1mdW5jdGlvbihuKXtyZXR1cm4gbih0aGlzKX0sdS50aGVuPWZ1bmN0aW9uKG4pe3JldHVybiBfKG4pLEModGhpcyxuKS5tYXAoZnVuY3Rpb24obil7cmV0dXJuIG5bMV19KX0sdS5tYW55PWZ1bmN0aW9uKCl7dmFyIG49dGhpcztyZXR1cm4gZShmdW5jdGlvbih0LHIpe2Zvcih2YXIgZT1bXSx1PXZvaWQgMDs7KXtpZighKHU9QihuLl8odCxyKSx1KSkuc3RhdHVzKXJldHVybiBCKGIocixlKSx1KTtpZihyPT09dS5pbmRleCl0aHJvdyBuZXcgRXJyb3IoImluZmluaXRlIGxvb3AgZGV0ZWN0ZWQgaW4gLm1hbnkoKSBwYXJzZXIgLS0tIGNhbGxpbmcgLm1hbnkoKSBvbiBhIHBhcnNlciB3aGljaCBjYW4gYWNjZXB0IHplcm8gY2hhcmFjdGVycyBpcyB1c3VhbGx5IHRoZSBjYXVzZSIpO3I9dS5pbmRleCxlLnB1c2godS52YWx1ZSk7fX0pfSx1LnRpZVdpdGg9ZnVuY3Rpb24obil7cmV0dXJuIFAobiksdGhpcy5tYXAoZnVuY3Rpb24odCl7aWYoZnVuY3Rpb24obil7aWYoIUUobikpdGhyb3cgbmV3IEVycm9yKCJub3QgYW4gYXJyYXk6ICIrbil9KHQpLHQubGVuZ3RoKXtQKHRbMF0pO2Zvcih2YXIgcj10WzBdLGU9MTtlPHQubGVuZ3RoO2UrKylQKHRbZV0pLHIrPW4rdFtlXTtyZXR1cm4gcn1yZXR1cm4gIiJ9KX0sdS50aWU9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy50aWVXaXRoKCIiKX0sdS50aW1lcz1mdW5jdGlvbihuLHQpe3ZhciByPXRoaXM7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGg8MiYmKHQ9biksTyhuKSxPKHQpLGUoZnVuY3Rpb24oZSx1KXtmb3IodmFyIG89W10saT12b2lkIDAsYT12b2lkIDAsZj0wO2Y8bjtmKz0xKXtpZihhPUIoaT1yLl8oZSx1KSxhKSwhaS5zdGF0dXMpcmV0dXJuIGE7dT1pLmluZGV4LG8ucHVzaChpLnZhbHVlKTt9Zm9yKDtmPHQmJihhPUIoaT1yLl8oZSx1KSxhKSxpLnN0YXR1cyk7Zis9MSl1PWkuaW5kZXgsby5wdXNoKGkudmFsdWUpO3JldHVybiBCKGIodSxvKSxhKX0pfSx1LnJlc3VsdD1mdW5jdGlvbihuKXtyZXR1cm4gdGhpcy5tYXAoZnVuY3Rpb24oKXtyZXR1cm4gbn0pfSx1LmF0TW9zdD1mdW5jdGlvbihuKXtyZXR1cm4gdGhpcy50aW1lcygwLG4pfSx1LmF0TGVhc3Q9ZnVuY3Rpb24obil7cmV0dXJuIEoodGhpcy50aW1lcyhuKSx0aGlzLm1hbnkoKSxmdW5jdGlvbihuLHQpe3JldHVybiBuLmNvbmNhdCh0KX0pfSx1Lm1hcD1mdW5jdGlvbihuKXtrKG4pO3ZhciB0PXRoaXM7cmV0dXJuIGUoZnVuY3Rpb24ocixlKXt2YXIgdT10Ll8ocixlKTtyZXR1cm4gdS5zdGF0dXM/QihiKHUuaW5kZXgsbih1LnZhbHVlKSksdSk6dX0pfSx1LmNvbnRyYW1hcD1mdW5jdGlvbihuKXtrKG4pO3ZhciB0PXRoaXM7cmV0dXJuIGUoZnVuY3Rpb24ocixlKXt2YXIgdT10LnBhcnNlKG4oci5zbGljZShlKSkpO3JldHVybiB1LnN0YXR1cz9iKGUrci5sZW5ndGgsdS52YWx1ZSk6dX0pfSx1LnByb21hcD1mdW5jdGlvbihuLHQpe3JldHVybiBrKG4pLGsodCksdGhpcy5jb250cmFtYXAobikubWFwKHQpfSx1LnNraXA9ZnVuY3Rpb24obil7cmV0dXJuIEModGhpcyxuKS5tYXAoZnVuY3Rpb24obil7cmV0dXJuIG5bMF19KX0sdS5tYXJrPWZ1bmN0aW9uKCl7cmV0dXJuIEooZW4sdGhpcyxlbixmdW5jdGlvbihuLHQscil7cmV0dXJuIHtzdGFydDpuLHZhbHVlOnQsZW5kOnJ9fSl9LHUubm9kZT1mdW5jdGlvbihuKXtyZXR1cm4gSihlbix0aGlzLGVuLGZ1bmN0aW9uKHQscixlKXtyZXR1cm4ge25hbWU6bix2YWx1ZTpyLHN0YXJ0OnQsZW5kOmV9fSl9LHUuc2VwQnk9ZnVuY3Rpb24obil7cmV0dXJuIFYodGhpcyxuKX0sdS5zZXBCeTE9ZnVuY3Rpb24obil7cmV0dXJuIEgodGhpcyxuKX0sdS5sb29rYWhlYWQ9ZnVuY3Rpb24obil7cmV0dXJuIHRoaXMuc2tpcChaKG4pKX0sdS5ub3RGb2xsb3dlZEJ5PWZ1bmN0aW9uKG4pe3JldHVybiB0aGlzLnNraXAoJChuKSl9LHUuZGVzYz1mdW5jdGlvbihuKXtFKG4pfHwobj1bbl0pO3ZhciB0PXRoaXM7cmV0dXJuIGUoZnVuY3Rpb24ocixlKXt2YXIgdT10Ll8ocixlKTtyZXR1cm4gdS5zdGF0dXN8fCh1LmV4cGVjdGVkPW4pLHV9KX0sdS5mYWxsYmFjaz1mdW5jdGlvbihuKXtyZXR1cm4gdGhpcy5vcihYKG4pKX0sdS5hcD1mdW5jdGlvbihuKXtyZXR1cm4gSihuLHRoaXMsZnVuY3Rpb24obix0KXtyZXR1cm4gbih0KX0pfSx1LmNoYWluPWZ1bmN0aW9uKG4pe3ZhciB0PXRoaXM7cmV0dXJuIGUoZnVuY3Rpb24ocixlKXt2YXIgdT10Ll8ocixlKTtyZXR1cm4gdS5zdGF0dXM/QihuKHUudmFsdWUpLl8ocix1LmluZGV4KSx1KTp1fSl9LHUuY29uY2F0PXUub3IsdS5lbXB0eT1ybix1Lm9mPVgsdVsiZmFudGFzeS1sYW5kL2FwIl09dS5hcCx1WyJmYW50YXN5LWxhbmQvY2hhaW4iXT11LmNoYWluLHVbImZhbnRhc3ktbGFuZC9jb25jYXQiXT11LmNvbmNhdCx1WyJmYW50YXN5LWxhbmQvZW1wdHkiXT11LmVtcHR5LHVbImZhbnRhc3ktbGFuZC9vZiJdPXUub2YsdVsiZmFudGFzeS1sYW5kL21hcCJdPXUubWFwO3ZhciBlbj1lKGZ1bmN0aW9uKG4sdCl7cmV0dXJuIGIodCxTKG4sdCkpfSksdW49ZShmdW5jdGlvbihuLHQpe3JldHVybiB0Pj1uLmxlbmd0aD94KHQsImFueSBjaGFyYWN0ZXIvYnl0ZSIpOmIodCsxLEwobix0KSl9KSxvbj1lKGZ1bmN0aW9uKG4sdCl7cmV0dXJuIGIobi5sZW5ndGgsbi5zbGljZSh0KSl9KSxhbj1lKGZ1bmN0aW9uKG4sdCl7cmV0dXJuIHQ8bi5sZW5ndGg/eCh0LCJFT0YiKTpiKHQsbnVsbCl9KSxmbj1RKC9bMC05XS8pLmRlc2MoImEgZGlnaXQiKSxjbj1RKC9bMC05XSovKS5kZXNjKCJvcHRpb25hbCBkaWdpdHMiKSxzbj1RKC9bYS16XS9pKS5kZXNjKCJhIGxldHRlciIpLGxuPVEoL1thLXpdKi9pKS5kZXNjKCJvcHRpb25hbCBsZXR0ZXJzIiksaG49USgvXHMqLykuZGVzYygib3B0aW9uYWwgd2hpdGVzcGFjZSIpLHBuPVEoL1xzKy8pLmRlc2MoIndoaXRlc3BhY2UiKSxkbj1LKCJcciIpLHZuPUsoIlxuIiksZ249SygiXHJcbiIpLG1uPVQoZ24sdm4sZG4pLmRlc2MoIm5ld2xpbmUiKSx5bj1UKG1uLGFuKTtlLmFsbD1vbixlLmFsdD1ULGUuYW55PXVuLGUuY3I9ZG4sZS5jcmVhdGVMYW5ndWFnZT1mdW5jdGlvbihuKXt2YXIgdD17fTtmb3IodmFyIHIgaW4gbikoe30pLmhhc093blByb3BlcnR5LmNhbGwobixyKSYmZnVuY3Rpb24ocil7dFtyXT10bihmdW5jdGlvbigpe3JldHVybiBuW3JdKHQpfSk7fShyKTtyZXR1cm4gdH0sZS5jcmxmPWduLGUuY3VzdG9tPWZ1bmN0aW9uKG4pe3JldHVybiBlKG4oYix4KSl9LGUuZGlnaXQ9Zm4sZS5kaWdpdHM9Y24sZS5lbXB0eT1ybixlLmVuZD15bixlLmVvZj1hbixlLmZhaWw9WSxlLmZvcm1hdEVycm9yPU4sZS5pbmRleD1lbixlLmlzUGFyc2VyPXksZS5sYXp5PXRuLGUubGV0dGVyPXNuLGUubGV0dGVycz1sbixlLmxmPXZuLGUubG9va2FoZWFkPVosZS5tYWtlRmFpbHVyZT14LGUubWFrZVN1Y2Nlc3M9YixlLm5ld2xpbmU9bW4sZS5ub25lT2Y9ZnVuY3Rpb24obil7cmV0dXJuIG5uKGZ1bmN0aW9uKHQpe3JldHVybiBuLmluZGV4T2YodCk8MH0pLmRlc2MoIm5vbmUgb2YgJyIrbisiJyIpfSxlLm5vdEZvbGxvd2VkQnk9JCxlLm9mPVgsZS5vbmVPZj1mdW5jdGlvbihuKXtmb3IodmFyIHQ9bi5zcGxpdCgiIikscj0wO3I8dC5sZW5ndGg7cisrKXRbcl09IiciK3Rbcl0rIiciO3JldHVybiBubihmdW5jdGlvbih0KXtyZXR1cm4gbi5pbmRleE9mKHQpPj0wfSkuZGVzYyh0KX0sZS5vcHRXaGl0ZXNwYWNlPWhuLGUuUGFyc2VyPWUsZS5yYW5nZT1mdW5jdGlvbihuLHQpe3JldHVybiBubihmdW5jdGlvbihyKXtyZXR1cm4gbjw9ciYmcjw9dH0pLmRlc2MobisiLSIrdCl9LGUucmVnZXg9USxlLnJlZ2V4cD1RLGUuc2VwQnk9VixlLnNlcEJ5MT1ILGUuc2VxPUMsZS5zZXFNYXA9SixlLnNlcU9iaj1mdW5jdGlvbigpe2Zvcih2YXIgbix0PXt9LHI9MCx1PShuPWFyZ3VtZW50cyxBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChuKSksbz11Lmxlbmd0aCxpPTA7aTxvO2krPTEpe3ZhciBhPXVbaV07aWYoIXkoYSkpe2lmKEUoYSkmJjI9PT1hLmxlbmd0aCYmInN0cmluZyI9PXR5cGVvZiBhWzBdJiZ5KGFbMV0pKXt2YXIgZj1hWzBdO2lmKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LGYpKXRocm93IG5ldyBFcnJvcigic2VxT2JqOiBkdXBsaWNhdGUga2V5ICIrZik7dFtmXT0hMCxyKys7Y29udGludWV9dGhyb3cgbmV3IEVycm9yKCJzZXFPYmogYXJndW1lbnRzIG11c3QgYmUgcGFyc2VycyBvciBbc3RyaW5nLCBwYXJzZXJdIGFycmF5IHBhaXJzLiIpfX1pZigwPT09cil0aHJvdyBuZXcgRXJyb3IoInNlcU9iaiBleHBlY3RzIGF0IGxlYXN0IG9uZSBuYW1lZCBwYXJzZXIsIGZvdW5kIHplcm8iKTtyZXR1cm4gZShmdW5jdGlvbihuLHQpe2Zvcih2YXIgcixlPXt9LGk9MDtpPG87aSs9MSl7dmFyIGEsZjtpZihFKHVbaV0pPyhhPXVbaV1bMF0sZj11W2ldWzFdKTooYT1udWxsLGY9dVtpXSksIShyPUIoZi5fKG4sdCkscikpLnN0YXR1cylyZXR1cm4gcjthJiYoZVthXT1yLnZhbHVlKSx0PXIuaW5kZXg7fXJldHVybiBCKGIodCxlKSxyKX0pfSxlLnN0cmluZz1LLGUuc3VjY2VlZD1YLGUudGFrZVdoaWxlPWZ1bmN0aW9uKG4pe3JldHVybiBrKG4pLGUoZnVuY3Rpb24odCxyKXtmb3IodmFyIGU9cjtlPHQubGVuZ3RoJiZuKEwodCxlKSk7KWUrKztyZXR1cm4gYihlLHQuc2xpY2UocixlKSl9KX0sZS50ZXN0PW5uLGUud2hpdGVzcGFjZT1wbixlWyJmYW50YXN5LWxhbmQvZW1wdHkiXT1ybixlWyJmYW50YXN5LWxhbmQvb2YiXT1YLGUuQmluYXJ5PXtiaXRTZXE6bCxiaXRTZXFPYmo6ZnVuY3Rpb24obil7cygpO3ZhciB0PXt9LHI9MCxlPWEoZnVuY3Rpb24obil7aWYoRShuKSl7dmFyIGU9bjtpZigyIT09ZS5sZW5ndGgpdGhyb3cgbmV3IEVycm9yKCJbIitlLmpvaW4oIiwgIikrIl0gc2hvdWxkIGJlIGxlbmd0aCAyLCBnb3QgbGVuZ3RoICIrZS5sZW5ndGgpO2lmKFAoZVswXSksTyhlWzFdKSxPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxlWzBdKSl0aHJvdyBuZXcgRXJyb3IoImR1cGxpY2F0ZSBrZXkgaW4gYml0U2VxT2JqOiAiK2VbMF0pO3JldHVybiB0W2VbMF1dPSEwLHIrKyxlfXJldHVybiBPKG4pLFtudWxsLG5dfSxuKTtpZihyPDEpdGhyb3cgbmV3IEVycm9yKCJiaXRTZXFPYmogZXhwZWN0cyBhdCBsZWFzdCBvbmUgbmFtZWQgcGFpciwgZ290IFsiK24uam9pbigiLCAiKSsiXSIpO3ZhciB1PWEoZnVuY3Rpb24obil7cmV0dXJuIG5bMF19LGUpO3JldHVybiBsKGEoZnVuY3Rpb24obil7cmV0dXJuIG5bMV19LGUpKS5tYXAoZnVuY3Rpb24obil7cmV0dXJuIGkoZnVuY3Rpb24obix0KXtyZXR1cm4gbnVsbCE9PXRbMF0mJihuW3RbMF1dPXRbMV0pLG59LHt9LGEoZnVuY3Rpb24odCxyKXtyZXR1cm4gW3QsbltyXV19LHUpKX0pfSxieXRlOmZ1bmN0aW9uKG4pe2lmKHMoKSxPKG4pLG4+MjU1KXRocm93IG5ldyBFcnJvcigiVmFsdWUgc3BlY2lmaWVkIHRvIGJ5dGUgY29uc3RydWN0b3IgKCIrbisiPTB4IituLnRvU3RyaW5nKDE2KSsiKSBpcyBsYXJnZXIgaW4gdmFsdWUgdGhhbiBhIHNpbmdsZSBieXRlLiIpO3ZhciB0PShuPjE1PyIweCI6IjB4MCIpK24udG9TdHJpbmcoMTYpO3JldHVybiBlKGZ1bmN0aW9uKHIsZSl7dmFyIHU9TChyLGUpO3JldHVybiB1PT09bj9iKGUrMSx1KTp4KGUsdCl9KX0sYnVmZmVyOmZ1bmN0aW9uKG4pe3JldHVybiBoKCJidWZmZXIiLG4pLm1hcChmdW5jdGlvbihuKXtyZXR1cm4gQnVmZmVyLmZyb20obil9KX0sZW5jb2RlZFN0cmluZzpmdW5jdGlvbihuLHQpe3JldHVybiBoKCJzdHJpbmciLHQpLm1hcChmdW5jdGlvbih0KXtyZXR1cm4gdC50b1N0cmluZyhuKX0pfSx1aW50QkU6ZCx1aW50OEJFOmQoMSksdWludDE2QkU6ZCgyKSx1aW50MzJCRTpkKDQpLHVpbnRMRTp2LHVpbnQ4TEU6digxKSx1aW50MTZMRTp2KDIpLHVpbnQzMkxFOnYoNCksaW50QkU6ZyxpbnQ4QkU6ZygxKSxpbnQxNkJFOmcoMiksaW50MzJCRTpnKDQpLGludExFOm0saW50OExFOm0oMSksaW50MTZMRTptKDIpLGludDMyTEU6bSg0KSxmbG9hdEJFOmgoImZsb2F0QkUiLDQpLm1hcChmdW5jdGlvbihuKXtyZXR1cm4gbi5yZWFkRmxvYXRCRSgwKX0pLGZsb2F0TEU6aCgiZmxvYXRMRSIsNCkubWFwKGZ1bmN0aW9uKG4pe3JldHVybiBuLnJlYWRGbG9hdExFKDApfSksZG91YmxlQkU6aCgiZG91YmxlQkUiLDgpLm1hcChmdW5jdGlvbihuKXtyZXR1cm4gbi5yZWFkRG91YmxlQkUoMCl9KSxkb3VibGVMRTpoKCJkb3VibGVMRSIsOCkubWFwKGZ1bmN0aW9uKG4pe3JldHVybiBuLnJlYWREb3VibGVMRSgwKX0pfSxuLmV4cG9ydHM9ZTt9XSl9KTsgCiAgfSAocGFyc2ltbW9uX3VtZF9taW4sIHBhcnNpbW1vbl91bWRfbWluLmV4cG9ydHMpKTsKCiAgdmFyIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cyA9IHBhcnNpbW1vbl91bWRfbWluLmV4cG9ydHM7CgogIHZhciBlbW9qaVJlZ2V4ID0gKCkgPT4gewogIAkvLyBodHRwczovL210aHMuYmUvZW1vamkKICAJcmV0dXJuIC9bIyowLTldXHVGRTBGP1x1MjBFM3xbXHhBOVx4QUVcdTIwM0NcdTIwNDlcdTIxMjJcdTIxMzlcdTIxOTQtXHUyMTk5XHUyMUE5XHUyMUFBXHUyMzFBXHUyMzFCXHUyMzI4XHUyM0NGXHUyM0VELVx1MjNFRlx1MjNGMVx1MjNGMlx1MjNGOC1cdTIzRkFcdTI0QzJcdTI1QUFcdTI1QUJcdTI1QjZcdTI1QzBcdTI1RkJcdTI1RkNcdTI1RkVcdTI2MDAtXHUyNjA0XHUyNjBFXHUyNjExXHUyNjE0XHUyNjE1XHUyNjE4XHUyNjIwXHUyNjIyXHUyNjIzXHUyNjI2XHUyNjJBXHUyNjJFXHUyNjJGXHUyNjM4LVx1MjYzQVx1MjY0MFx1MjY0Mlx1MjY0OC1cdTI2NTNcdTI2NUZcdTI2NjBcdTI2NjNcdTI2NjVcdTI2NjZcdTI2NjhcdTI2N0JcdTI2N0VcdTI2N0ZcdTI2OTJcdTI2OTQtXHUyNjk3XHUyNjk5XHUyNjlCXHUyNjlDXHUyNkEwXHUyNkE3XHUyNkFBXHUyNkIwXHUyNkIxXHUyNkJEXHUyNkJFXHUyNkM0XHUyNkM4XHUyNkNGXHUyNkQxXHUyNkQzXHUyNkU5XHUyNkYwLVx1MjZGNVx1MjZGN1x1MjZGOFx1MjZGQVx1MjcwMlx1MjcwOFx1MjcwOVx1MjcwRlx1MjcxMlx1MjcxNFx1MjcxNlx1MjcxRFx1MjcyMVx1MjczM1x1MjczNFx1Mjc0NFx1Mjc0N1x1Mjc1N1x1Mjc2M1x1MjdBMVx1MjkzNFx1MjkzNVx1MkIwNS1cdTJCMDdcdTJCMUJcdTJCMUNcdTJCNTVcdTMwMzBcdTMwM0RcdTMyOTdcdTMyOTldXHVGRTBGP3xbXHUyNjFEXHUyNzBDXHUyNzBEXSg/Olx1RkUwRnxcdUQ4M0NbXHVERkZCLVx1REZGRl0pP3xbXHUyNzBBXHUyNzBCXSg/Olx1RDgzQ1tcdURGRkItXHVERkZGXSk/fFtcdTIzRTktXHUyM0VDXHUyM0YwXHUyM0YzXHUyNUZEXHUyNjkzXHUyNkExXHUyNkFCXHUyNkM1XHUyNkNFXHUyNkQ0XHUyNkVBXHUyNkZEXHUyNzA1XHUyNzI4XHUyNzRDXHUyNzRFXHUyNzUzLVx1Mjc1NVx1Mjc5NS1cdTI3OTdcdTI3QjBcdTI3QkZcdTJCNTBdfFx1MjZGOSg/Olx1RkUwRnxcdUQ4M0NbXHVERkZCLVx1REZGRl0pPyg/Olx1MjAwRFtcdTI2NDBcdTI2NDJdXHVGRTBGPyk/fFx1Mjc2NFx1RkUwRj8oPzpcdTIwMEQoPzpcdUQ4M0RcdUREMjV8XHVEODNFXHVERTc5KSk/fFx1RDgzQyg/OltcdURDMDRcdURENzBcdURENzFcdUREN0VcdUREN0ZcdURFMDJcdURFMzdcdURGMjFcdURGMjQtXHVERjJDXHVERjM2XHVERjdEXHVERjk2XHVERjk3XHVERjk5LVx1REY5Qlx1REY5RVx1REY5Rlx1REZDRFx1REZDRVx1REZENC1cdURGREZcdURGRjVcdURGRjddXHVGRTBGP3xbXHVERjg1XHVERkMyXHVERkM3XSg/Olx1RDgzQ1tcdURGRkItXHVERkZGXSk/fFtcdURGQzNcdURGQzRcdURGQ0FdKD86XHVEODNDW1x1REZGQi1cdURGRkZdKT8oPzpcdTIwMERbXHUyNjQwXHUyNjQyXVx1RkUwRj8pP3xbXHVERkNCXHVERkNDXSg/Olx1RkUwRnxcdUQ4M0NbXHVERkZCLVx1REZGRl0pPyg/Olx1MjAwRFtcdTI2NDBcdTI2NDJdXHVGRTBGPyk/fFtcdURDQ0ZcdUREOEVcdUREOTEtXHVERDlBXHVERTAxXHVERTFBXHVERTJGXHVERTMyLVx1REUzNlx1REUzOC1cdURFM0FcdURFNTBcdURFNTFcdURGMDAtXHVERjIwXHVERjJELVx1REYzNVx1REYzNy1cdURGN0NcdURGN0UtXHVERjg0XHVERjg2LVx1REY5M1x1REZBMC1cdURGQzFcdURGQzVcdURGQzZcdURGQzhcdURGQzlcdURGQ0YtXHVERkQzXHVERkUwLVx1REZGMFx1REZGOC1cdURGRkZdfFx1RERFNlx1RDgzQ1tcdURERTgtXHVEREVDXHVEREVFXHVEREYxXHVEREYyXHVEREY0XHVEREY2LVx1RERGQVx1RERGQ1x1RERGRFx1RERGRl18XHVEREU3XHVEODNDW1x1RERFNlx1RERFN1x1RERFOS1cdURERUZcdURERjEtXHVEREY0XHVEREY2LVx1RERGOVx1RERGQlx1RERGQ1x1RERGRVx1RERGRl18XHVEREU4XHVEODNDW1x1RERFNlx1RERFOFx1RERFOVx1RERFQi1cdURERUVcdURERjAtXHVEREY1XHVEREY3XHVEREZBLVx1RERGRl18XHVEREU5XHVEODNDW1x1RERFQVx1RERFQ1x1RERFRlx1RERGMFx1RERGMlx1RERGNFx1RERGRl18XHVEREVBXHVEODNDW1x1RERFNlx1RERFOFx1RERFQVx1RERFQ1x1RERFRFx1RERGNy1cdURERkFdfFx1RERFQlx1RDgzQ1tcdURERUUtXHVEREYwXHVEREYyXHVEREY0XHVEREY3XXxcdURERUNcdUQ4M0NbXHVEREU2XHVEREU3XHVEREU5LVx1RERFRVx1RERGMS1cdURERjNcdURERjUtXHVEREZBXHVEREZDXHVEREZFXXxcdURERURcdUQ4M0NbXHVEREYwXHVEREYyXHVEREYzXHVEREY3XHVEREY5XHVEREZBXXxcdURERUVcdUQ4M0NbXHVEREU4LVx1RERFQVx1RERGMS1cdURERjRcdURERjYtXHVEREY5XXxcdURERUZcdUQ4M0NbXHVEREVBXHVEREYyXHVEREY0XHVEREY1XXxcdURERjBcdUQ4M0NbXHVEREVBXHVEREVDLVx1RERFRVx1RERGMlx1RERGM1x1RERGNVx1RERGN1x1RERGQ1x1RERGRVx1RERGRl18XHVEREYxXHVEODNDW1x1RERFNi1cdURERThcdURERUVcdURERjBcdURERjctXHVEREZCXHVEREZFXXxcdURERjJcdUQ4M0NbXHVEREU2XHVEREU4LVx1RERFRFx1RERGMC1cdURERkZdfFx1RERGM1x1RDgzQ1tcdURERTZcdURERThcdURERUEtXHVEREVDXHVEREVFXHVEREYxXHVEREY0XHVEREY1XHVEREY3XHVEREZBXHVEREZGXXxcdURERjRcdUQ4M0NcdURERjJ8XHVEREY1XHVEODNDW1x1RERFNlx1RERFQS1cdURERURcdURERjAtXHVEREYzXHVEREY3LVx1RERGOVx1RERGQ1x1RERGRV18XHVEREY2XHVEODNDXHVEREU2fFx1RERGN1x1RDgzQ1tcdURERUFcdURERjRcdURERjhcdURERkFcdURERkNdfFx1RERGOFx1RDgzQ1tcdURERTYtXHVEREVBXHVEREVDLVx1RERGNFx1RERGNy1cdURERjlcdURERkJcdURERkQtXHVEREZGXXxcdURERjlcdUQ4M0NbXHVEREU2XHVEREU4XHVEREU5XHVEREVCLVx1RERFRFx1RERFRi1cdURERjRcdURERjdcdURERjlcdURERkJcdURERkNcdURERkZdfFx1RERGQVx1RDgzQ1tcdURERTZcdURERUNcdURERjJcdURERjNcdURERjhcdURERkVcdURERkZdfFx1RERGQlx1RDgzQ1tcdURERTZcdURERThcdURERUFcdURERUNcdURERUVcdURERjNcdURERkFdfFx1RERGQ1x1RDgzQ1tcdURERUJcdURERjhdfFx1RERGRFx1RDgzQ1x1RERGMHxcdURERkVcdUQ4M0NbXHVEREVBXHVEREY5XXxcdURERkZcdUQ4M0NbXHVEREU2XHVEREYyXHVEREZDXXxcdURGRjNcdUZFMEY/KD86XHUyMDBEKD86XHUyNkE3XHVGRTBGP3xcdUQ4M0NcdURGMDgpKT98XHVERkY0KD86XHUyMDBEXHUyNjIwXHVGRTBGP3xcdURCNDBcdURDNjdcdURCNDBcdURDNjJcdURCNDAoPzpcdURDNjVcdURCNDBcdURDNkVcdURCNDBcdURDNjd8XHVEQzczXHVEQjQwXHVEQzYzXHVEQjQwXHVEQzc0fFx1REM3N1x1REI0MFx1REM2Q1x1REI0MFx1REM3MylcdURCNDBcdURDN0YpPyl8XHVEODNEKD86W1x1REMwOFx1REMyNl0oPzpcdTIwMERcdTJCMUIpP3xbXHVEQzNGXHVEQ0ZEXHVERDQ5XHVERDRBXHVERDZGXHVERDcwXHVERDczXHVERDc2LVx1REQ3OVx1REQ4N1x1REQ4QS1cdUREOERcdUREQTVcdUREQThcdUREQjFcdUREQjJcdUREQkNcdUREQzItXHVEREM0XHVEREQxLVx1REREM1x1REREQy1cdUREREVcdURERTFcdURERTNcdURERThcdURERUZcdURERjNcdURERkFcdURFQ0JcdURFQ0QtXHVERUNGXHVERUUwLVx1REVFNVx1REVFOVx1REVGMFx1REVGM11cdUZFMEY/fFtcdURDNDJcdURDNDNcdURDNDYtXHVEQzUwXHVEQzY2XHVEQzY3XHVEQzZCLVx1REM2RFx1REM3Mlx1REM3NC1cdURDNzZcdURDNzhcdURDN0NcdURDODNcdURDODVcdURDOEZcdURDOTFcdURDQUFcdUREN0FcdUREOTVcdUREOTZcdURFNENcdURFNEZcdURFQzBcdURFQ0NdKD86XHVEODNDW1x1REZGQi1cdURGRkZdKT98W1x1REM2RVx1REM3MFx1REM3MVx1REM3M1x1REM3N1x1REM4MVx1REM4Mlx1REM4Nlx1REM4N1x1REU0NS1cdURFNDdcdURFNEJcdURFNERcdURFNEVcdURFQTNcdURFQjQtXHVERUI2XSg/Olx1RDgzQ1tcdURGRkItXHVERkZGXSk/KD86XHUyMDBEW1x1MjY0MFx1MjY0Ml1cdUZFMEY/KT98W1x1REQ3NFx1REQ5MF0oPzpcdUZFMEZ8XHVEODNDW1x1REZGQi1cdURGRkZdKT98W1x1REMwMC1cdURDMDdcdURDMDktXHVEQzE0XHVEQzE2LVx1REMyNVx1REMyNy1cdURDM0FcdURDM0MtXHVEQzNFXHVEQzQwXHVEQzQ0XHVEQzQ1XHVEQzUxLVx1REM2NVx1REM2QVx1REM3OS1cdURDN0JcdURDN0QtXHVEQzgwXHVEQzg0XHVEQzg4LVx1REM4RVx1REM5MFx1REM5Mi1cdURDQTlcdURDQUItXHVEQ0ZDXHVEQ0ZGLVx1REQzRFx1REQ0Qi1cdURENEVcdURENTAtXHVERDY3XHVEREE0XHVEREZCLVx1REUyRFx1REUyRi1cdURFMzRcdURFMzctXHVERTQ0XHVERTQ4LVx1REU0QVx1REU4MC1cdURFQTJcdURFQTQtXHVERUIzXHVERUI3LVx1REVCRlx1REVDMS1cdURFQzVcdURFRDAtXHVERUQyXHVERUQ1LVx1REVEN1x1REVEQy1cdURFREZcdURFRUJcdURFRUNcdURFRjQtXHVERUZDXHVERkUwLVx1REZFQlx1REZGMF18XHVEQzE1KD86XHUyMDBEXHVEODNFXHVEREJBKT98XHVEQzNCKD86XHUyMDBEXHUyNzQ0XHVGRTBGPyk/fFx1REM0MVx1RkUwRj8oPzpcdTIwMERcdUQ4M0RcdURERThcdUZFMEY/KT98XHVEQzY4KD86XHUyMDBEKD86W1x1MjY5NVx1MjY5Nlx1MjcwOF1cdUZFMEY/fFx1Mjc2NFx1RkUwRj9cdTIwMERcdUQ4M0QoPzpcdURDOEJcdTIwMERcdUQ4M0QpP1x1REM2OHxcdUQ4M0NbXHVERjNFXHVERjczXHVERjdDXHVERjkzXHVERkE0XHVERkE4XHVERkVCXHVERkVEXXxcdUQ4M0QoPzpbXHVEQzY4XHVEQzY5XVx1MjAwRFx1RDgzRCg/Olx1REM2Nig/Olx1MjAwRFx1RDgzRFx1REM2Nik/fFx1REM2Nyg/Olx1MjAwRFx1RDgzRFtcdURDNjZcdURDNjddKT8pfFtcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1REM2Nig/Olx1MjAwRFx1RDgzRFx1REM2Nik/fFx1REM2Nyg/Olx1MjAwRFx1RDgzRFtcdURDNjZcdURDNjddKT8pfFx1RDgzRVtcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXSl8XHVEODNDKD86XHVERkZCKD86XHUyMDBEKD86W1x1MjY5NVx1MjY5Nlx1MjcwOF1cdUZFMEY/fFx1Mjc2NFx1RkUwRj9cdTIwMERcdUQ4M0QoPzpcdURDOEJcdTIwMERcdUQ4M0QpP1x1REM2OFx1RDgzQ1tcdURGRkItXHVERkZGXXxcdUQ4M0NbXHVERjNFXHVERjczXHVERjdDXHVERjkzXHVERkE0XHVERkE4XHVERkVCXHVERkVEXXxcdUQ4M0RbXHVEQ0JCXHVEQ0JDXHVERDI3XHVERDJDXHVERTgwXHVERTkyXXxcdUQ4M0UoPzpbXHVEREFGLVx1RERCM1x1RERCQ1x1RERCRF18XHVERDFEXHUyMDBEXHVEODNEXHVEQzY4XHVEODNDW1x1REZGQy1cdURGRkZdKSkpP3xcdURGRkMoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHUyNzY0XHVGRTBGP1x1MjAwRFx1RDgzRCg/Olx1REM4Qlx1MjAwRFx1RDgzRCk/XHVEQzY4XHVEODNDW1x1REZGQi1cdURGRkZdfFx1RDgzQ1tcdURGM0VcdURGNzNcdURGN0NcdURGOTNcdURGQTRcdURGQThcdURGRUJcdURGRURdfFx1RDgzRFtcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1RDgzRSg/OltcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXXxcdUREMURcdTIwMERcdUQ4M0RcdURDNjhcdUQ4M0NbXHVERkZCXHVERkZELVx1REZGRl0pKSk/fFx1REZGRCg/Olx1MjAwRCg/OltcdTI2OTVcdTI2OTZcdTI3MDhdXHVGRTBGP3xcdTI3NjRcdUZFMEY/XHUyMDBEXHVEODNEKD86XHVEQzhCXHUyMDBEXHVEODNEKT9cdURDNjhcdUQ4M0NbXHVERkZCLVx1REZGRl18XHVEODNDW1x1REYzRVx1REY3M1x1REY3Q1x1REY5M1x1REZBNFx1REZBOFx1REZFQlx1REZFRF18XHVEODNEW1x1RENCQlx1RENCQ1x1REQyN1x1REQyQ1x1REU4MFx1REU5Ml18XHVEODNFKD86W1x1RERBRi1cdUREQjNcdUREQkNcdUREQkRdfFx1REQxRFx1MjAwRFx1RDgzRFx1REM2OFx1RDgzQ1tcdURGRkJcdURGRkNcdURGRkVcdURGRkZdKSkpP3xcdURGRkUoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHUyNzY0XHVGRTBGP1x1MjAwRFx1RDgzRCg/Olx1REM4Qlx1MjAwRFx1RDgzRCk/XHVEQzY4XHVEODNDW1x1REZGQi1cdURGRkZdfFx1RDgzQ1tcdURGM0VcdURGNzNcdURGN0NcdURGOTNcdURGQTRcdURGQThcdURGRUJcdURGRURdfFx1RDgzRFtcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1RDgzRSg/OltcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXXxcdUREMURcdTIwMERcdUQ4M0RcdURDNjhcdUQ4M0NbXHVERkZCLVx1REZGRFx1REZGRl0pKSk/fFx1REZGRig/Olx1MjAwRCg/OltcdTI2OTVcdTI2OTZcdTI3MDhdXHVGRTBGP3xcdTI3NjRcdUZFMEY/XHUyMDBEXHVEODNEKD86XHVEQzhCXHUyMDBEXHVEODNEKT9cdURDNjhcdUQ4M0NbXHVERkZCLVx1REZGRl18XHVEODNDW1x1REYzRVx1REY3M1x1REY3Q1x1REY5M1x1REZBNFx1REZBOFx1REZFQlx1REZFRF18XHVEODNEW1x1RENCQlx1RENCQ1x1REQyN1x1REQyQ1x1REU4MFx1REU5Ml18XHVEODNFKD86W1x1RERBRi1cdUREQjNcdUREQkNcdUREQkRdfFx1REQxRFx1MjAwRFx1RDgzRFx1REM2OFx1RDgzQ1tcdURGRkItXHVERkZFXSkpKT8pKT98XHVEQzY5KD86XHUyMDBEKD86W1x1MjY5NVx1MjY5Nlx1MjcwOF1cdUZFMEY/fFx1Mjc2NFx1RkUwRj9cdTIwMERcdUQ4M0QoPzpcdURDOEJcdTIwMERcdUQ4M0QpP1tcdURDNjhcdURDNjldfFx1RDgzQ1tcdURGM0VcdURGNzNcdURGN0NcdURGOTNcdURGQTRcdURGQThcdURGRUJcdURGRURdfFx1RDgzRCg/OltcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1REM2Nig/Olx1MjAwRFx1RDgzRFx1REM2Nik/fFx1REM2Nyg/Olx1MjAwRFx1RDgzRFtcdURDNjZcdURDNjddKT98XHVEQzY5XHUyMDBEXHVEODNEKD86XHVEQzY2KD86XHUyMDBEXHVEODNEXHVEQzY2KT98XHVEQzY3KD86XHUyMDBEXHVEODNEW1x1REM2Nlx1REM2N10pPykpfFx1RDgzRVtcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXSl8XHVEODNDKD86XHVERkZCKD86XHUyMDBEKD86W1x1MjY5NVx1MjY5Nlx1MjcwOF1cdUZFMEY/fFx1Mjc2NFx1RkUwRj9cdTIwMERcdUQ4M0QoPzpbXHVEQzY4XHVEQzY5XXxcdURDOEJcdTIwMERcdUQ4M0RbXHVEQzY4XHVEQzY5XSlcdUQ4M0NbXHVERkZCLVx1REZGRl18XHVEODNDW1x1REYzRVx1REY3M1x1REY3Q1x1REY5M1x1REZBNFx1REZBOFx1REZFQlx1REZFRF18XHVEODNEW1x1RENCQlx1RENCQ1x1REQyN1x1REQyQ1x1REU4MFx1REU5Ml18XHVEODNFKD86W1x1RERBRi1cdUREQjNcdUREQkNcdUREQkRdfFx1REQxRFx1MjAwRFx1RDgzRFtcdURDNjhcdURDNjldXHVEODNDW1x1REZGQy1cdURGRkZdKSkpP3xcdURGRkMoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHUyNzY0XHVGRTBGP1x1MjAwRFx1RDgzRCg/OltcdURDNjhcdURDNjldfFx1REM4Qlx1MjAwRFx1RDgzRFtcdURDNjhcdURDNjldKVx1RDgzQ1tcdURGRkItXHVERkZGXXxcdUQ4M0NbXHVERjNFXHVERjczXHVERjdDXHVERjkzXHVERkE0XHVERkE4XHVERkVCXHVERkVEXXxcdUQ4M0RbXHVEQ0JCXHVEQ0JDXHVERDI3XHVERDJDXHVERTgwXHVERTkyXXxcdUQ4M0UoPzpbXHVEREFGLVx1RERCM1x1RERCQ1x1RERCRF18XHVERDFEXHUyMDBEXHVEODNEW1x1REM2OFx1REM2OV1cdUQ4M0NbXHVERkZCXHVERkZELVx1REZGRl0pKSk/fFx1REZGRCg/Olx1MjAwRCg/OltcdTI2OTVcdTI2OTZcdTI3MDhdXHVGRTBGP3xcdTI3NjRcdUZFMEY/XHUyMDBEXHVEODNEKD86W1x1REM2OFx1REM2OV18XHVEQzhCXHUyMDBEXHVEODNEW1x1REM2OFx1REM2OV0pXHVEODNDW1x1REZGQi1cdURGRkZdfFx1RDgzQ1tcdURGM0VcdURGNzNcdURGN0NcdURGOTNcdURGQTRcdURGQThcdURGRUJcdURGRURdfFx1RDgzRFtcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1RDgzRSg/OltcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXXxcdUREMURcdTIwMERcdUQ4M0RbXHVEQzY4XHVEQzY5XVx1RDgzQ1tcdURGRkJcdURGRkNcdURGRkVcdURGRkZdKSkpP3xcdURGRkUoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHUyNzY0XHVGRTBGP1x1MjAwRFx1RDgzRCg/OltcdURDNjhcdURDNjldfFx1REM4Qlx1MjAwRFx1RDgzRFtcdURDNjhcdURDNjldKVx1RDgzQ1tcdURGRkItXHVERkZGXXxcdUQ4M0NbXHVERjNFXHVERjczXHVERjdDXHVERjkzXHVERkE0XHVERkE4XHVERkVCXHVERkVEXXxcdUQ4M0RbXHVEQ0JCXHVEQ0JDXHVERDI3XHVERDJDXHVERTgwXHVERTkyXXxcdUQ4M0UoPzpbXHVEREFGLVx1RERCM1x1RERCQ1x1RERCRF18XHVERDFEXHUyMDBEXHVEODNEW1x1REM2OFx1REM2OV1cdUQ4M0NbXHVERkZCLVx1REZGRFx1REZGRl0pKSk/fFx1REZGRig/Olx1MjAwRCg/OltcdTI2OTVcdTI2OTZcdTI3MDhdXHVGRTBGP3xcdTI3NjRcdUZFMEY/XHUyMDBEXHVEODNEKD86W1x1REM2OFx1REM2OV18XHVEQzhCXHUyMDBEXHVEODNEW1x1REM2OFx1REM2OV0pXHVEODNDW1x1REZGQi1cdURGRkZdfFx1RDgzQ1tcdURGM0VcdURGNzNcdURGN0NcdURGOTNcdURGQTRcdURGQThcdURGRUJcdURGRURdfFx1RDgzRFtcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1RDgzRSg/OltcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXXxcdUREMURcdTIwMERcdUQ4M0RbXHVEQzY4XHVEQzY5XVx1RDgzQ1tcdURGRkItXHVERkZFXSkpKT8pKT98XHVEQzZGKD86XHUyMDBEW1x1MjY0MFx1MjY0Ml1cdUZFMEY/KT98XHVERDc1KD86XHVGRTBGfFx1RDgzQ1tcdURGRkItXHVERkZGXSk/KD86XHUyMDBEW1x1MjY0MFx1MjY0Ml1cdUZFMEY/KT98XHVERTJFKD86XHUyMDBEXHVEODNEXHVEQ0E4KT98XHVERTM1KD86XHUyMDBEXHVEODNEXHVEQ0FCKT98XHVERTM2KD86XHUyMDBEXHVEODNDXHVERjJCXHVGRTBGPyk/KXxcdUQ4M0UoPzpbXHVERDBDXHVERDBGXHVERDE4LVx1REQxRlx1REQzMC1cdUREMzRcdUREMzZcdURENzdcdUREQjVcdUREQjZcdUREQkJcdURERDJcdURERDNcdURERDVcdURFQzMtXHVERUM1XHVERUYwXHVERUYyLVx1REVGOF0oPzpcdUQ4M0NbXHVERkZCLVx1REZGRl0pP3xbXHVERDI2XHVERDM1XHVERDM3LVx1REQzOVx1REQzRFx1REQzRVx1RERCOFx1RERCOVx1RERDRC1cdUREQ0ZcdURERDRcdURERDYtXHVEREREXSg/Olx1RDgzQ1tcdURGRkItXHVERkZGXSk/KD86XHUyMDBEW1x1MjY0MFx1MjY0Ml1cdUZFMEY/KT98W1x1RERERVx1RERERl0oPzpcdTIwMERbXHUyNjQwXHUyNjQyXVx1RkUwRj8pP3xbXHVERDBEXHVERDBFXHVERDEwLVx1REQxN1x1REQyMC1cdUREMjVcdUREMjctXHVERDJGXHVERDNBXHVERDNGLVx1REQ0NVx1REQ0Ny1cdURENzZcdURENzgtXHVEREI0XHVEREI3XHVEREJBXHVEREJDLVx1RERDQ1x1REREMFx1RERFMC1cdURERkZcdURFNzAtXHVERTdDXHVERTgwLVx1REU4OFx1REU5MC1cdURFQkRcdURFQkYtXHVERUMyXHVERUNFLVx1REVEQlx1REVFMC1cdURFRThdfFx1REQzQyg/Olx1MjAwRFtcdTI2NDBcdTI2NDJdXHVGRTBGP3xcdUQ4M0NbXHVERkZCLVx1REZGRl0pP3xcdURERDEoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHVEODNDW1x1REYzRVx1REY3M1x1REY3Q1x1REY4NFx1REY5M1x1REZBNFx1REZBOFx1REZFQlx1REZFRF18XHVEODNEW1x1RENCQlx1RENCQ1x1REQyN1x1REQyQ1x1REU4MFx1REU5Ml18XHVEODNFKD86W1x1RERBRi1cdUREQjNcdUREQkNcdUREQkRdfFx1REQxRFx1MjAwRFx1RDgzRVx1REREMSkpfFx1RDgzQyg/Olx1REZGQig/Olx1MjAwRCg/OltcdTI2OTVcdTI2OTZcdTI3MDhdXHVGRTBGP3xcdTI3NjRcdUZFMEY/XHUyMDBEKD86XHVEODNEXHVEQzhCXHUyMDBEKT9cdUQ4M0VcdURERDFcdUQ4M0NbXHVERkZDLVx1REZGRl18XHVEODNDW1x1REYzRVx1REY3M1x1REY3Q1x1REY4NFx1REY5M1x1REZBNFx1REZBOFx1REZFQlx1REZFRF18XHVEODNEW1x1RENCQlx1RENCQ1x1REQyN1x1REQyQ1x1REU4MFx1REU5Ml18XHVEODNFKD86W1x1RERBRi1cdUREQjNcdUREQkNcdUREQkRdfFx1REQxRFx1MjAwRFx1RDgzRVx1REREMVx1RDgzQ1tcdURGRkItXHVERkZGXSkpKT98XHVERkZDKD86XHUyMDBEKD86W1x1MjY5NVx1MjY5Nlx1MjcwOF1cdUZFMEY/fFx1Mjc2NFx1RkUwRj9cdTIwMEQoPzpcdUQ4M0RcdURDOEJcdTIwMEQpP1x1RDgzRVx1REREMVx1RDgzQ1tcdURGRkJcdURGRkQtXHVERkZGXXxcdUQ4M0NbXHVERjNFXHVERjczXHVERjdDXHVERjg0XHVERjkzXHVERkE0XHVERkE4XHVERkVCXHVERkVEXXxcdUQ4M0RbXHVEQ0JCXHVEQ0JDXHVERDI3XHVERDJDXHVERTgwXHVERTkyXXxcdUQ4M0UoPzpbXHVEREFGLVx1RERCM1x1RERCQ1x1RERCRF18XHVERDFEXHUyMDBEXHVEODNFXHVEREQxXHVEODNDW1x1REZGQi1cdURGRkZdKSkpP3xcdURGRkQoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHUyNzY0XHVGRTBGP1x1MjAwRCg/Olx1RDgzRFx1REM4Qlx1MjAwRCk/XHVEODNFXHVEREQxXHVEODNDW1x1REZGQlx1REZGQ1x1REZGRVx1REZGRl18XHVEODNDW1x1REYzRVx1REY3M1x1REY3Q1x1REY4NFx1REY5M1x1REZBNFx1REZBOFx1REZFQlx1REZFRF18XHVEODNEW1x1RENCQlx1RENCQ1x1REQyN1x1REQyQ1x1REU4MFx1REU5Ml18XHVEODNFKD86W1x1RERBRi1cdUREQjNcdUREQkNcdUREQkRdfFx1REQxRFx1MjAwRFx1RDgzRVx1REREMVx1RDgzQ1tcdURGRkItXHVERkZGXSkpKT98XHVERkZFKD86XHUyMDBEKD86W1x1MjY5NVx1MjY5Nlx1MjcwOF1cdUZFMEY/fFx1Mjc2NFx1RkUwRj9cdTIwMEQoPzpcdUQ4M0RcdURDOEJcdTIwMEQpP1x1RDgzRVx1REREMVx1RDgzQ1tcdURGRkItXHVERkZEXHVERkZGXXxcdUQ4M0NbXHVERjNFXHVERjczXHVERjdDXHVERjg0XHVERjkzXHVERkE0XHVERkE4XHVERkVCXHVERkVEXXxcdUQ4M0RbXHVEQ0JCXHVEQ0JDXHVERDI3XHVERDJDXHVERTgwXHVERTkyXXxcdUQ4M0UoPzpbXHVEREFGLVx1RERCM1x1RERCQ1x1RERCRF18XHVERDFEXHUyMDBEXHVEODNFXHVEREQxXHVEODNDW1x1REZGQi1cdURGRkZdKSkpP3xcdURGRkYoPzpcdTIwMEQoPzpbXHUyNjk1XHUyNjk2XHUyNzA4XVx1RkUwRj98XHUyNzY0XHVGRTBGP1x1MjAwRCg/Olx1RDgzRFx1REM4Qlx1MjAwRCk/XHVEODNFXHVEREQxXHVEODNDW1x1REZGQi1cdURGRkVdfFx1RDgzQ1tcdURGM0VcdURGNzNcdURGN0NcdURGODRcdURGOTNcdURGQTRcdURGQThcdURGRUJcdURGRURdfFx1RDgzRFtcdURDQkJcdURDQkNcdUREMjdcdUREMkNcdURFODBcdURFOTJdfFx1RDgzRSg/OltcdUREQUYtXHVEREIzXHVEREJDXHVEREJEXXxcdUREMURcdTIwMERcdUQ4M0VcdURERDFcdUQ4M0NbXHVERkZCLVx1REZGRl0pKSk/KSk/fFx1REVGMSg/Olx1RDgzQyg/Olx1REZGQig/Olx1MjAwRFx1RDgzRVx1REVGMlx1RDgzQ1tcdURGRkMtXHVERkZGXSk/fFx1REZGQyg/Olx1MjAwRFx1RDgzRVx1REVGMlx1RDgzQ1tcdURGRkJcdURGRkQtXHVERkZGXSk/fFx1REZGRCg/Olx1MjAwRFx1RDgzRVx1REVGMlx1RDgzQ1tcdURGRkJcdURGRkNcdURGRkVcdURGRkZdKT98XHVERkZFKD86XHUyMDBEXHVEODNFXHVERUYyXHVEODNDW1x1REZGQi1cdURGRkRcdURGRkZdKT98XHVERkZGKD86XHUyMDBEXHVEODNFXHVERUYyXHVEODNDW1x1REZGQi1cdURGRkVdKT8pKT8pL2c7CiAgfTsKCiAgLyoqIE5vcm1hbGl6ZSBhIGR1cmF0aW9uIHRvIGFsbCBvZiB0aGUgcHJvcGVyIHVuaXRzLiAqLwogIGZ1bmN0aW9uIG5vcm1hbGl6ZUR1cmF0aW9uKGR1cikgewogICAgICBpZiAoZHVyID09PSB1bmRlZmluZWQgfHwgZHVyID09PSBudWxsKQogICAgICAgICAgcmV0dXJuIGR1cjsKICAgICAgcmV0dXJuIGR1ci5zaGlmdFRvQWxsKCkubm9ybWFsaXplKCk7CiAgfQogIC8qKiBTdHJpcCB0aGUgdGltZSBjb21wb25lbnRzIG9mIGEgZGF0ZSB0aW1lIG9iamVjdC4gKi8KICBmdW5jdGlvbiBzdHJpcFRpbWUoZHQpIHsKICAgICAgaWYgKGR0ID09PSBudWxsIHx8IGR0ID09PSB1bmRlZmluZWQpCiAgICAgICAgICByZXR1cm4gZHQ7CiAgICAgIHJldHVybiBEYXRlVGltZS5mcm9tT2JqZWN0KHsKICAgICAgICAgIHllYXI6IGR0LnllYXIsCiAgICAgICAgICBtb250aDogZHQubW9udGgsCiAgICAgICAgICBkYXk6IGR0LmRheSwKICAgICAgfSk7CiAgfQogIC8qKiBUcnkgdG8gZXh0cmFjdCBhIFlZWVlNTUREIGRhdGUgZnJvbSBhIHN0cmluZy4gKi8KICBmdW5jdGlvbiBleHRyYWN0RGF0ZShzdHIpIHsKICAgICAgbGV0IGRhdGVNYXRjaCA9IC8oXGR7NH0pLShcZHsyfSktKFxkezJ9KS8uZXhlYyhzdHIpOwogICAgICBpZiAoIWRhdGVNYXRjaCkKICAgICAgICAgIGRhdGVNYXRjaCA9IC8oXGR7NH0pKFxkezJ9KShcZHsyfSkvLmV4ZWMoc3RyKTsKICAgICAgaWYgKGRhdGVNYXRjaCkgewogICAgICAgICAgbGV0IHllYXIgPSBOdW1iZXIucGFyc2VJbnQoZGF0ZU1hdGNoWzFdKTsKICAgICAgICAgIGxldCBtb250aCA9IE51bWJlci5wYXJzZUludChkYXRlTWF0Y2hbMl0pOwogICAgICAgICAgbGV0IGRheSA9IE51bWJlci5wYXJzZUludChkYXRlTWF0Y2hbM10pOwogICAgICAgICAgcmV0dXJuIERhdGVUaW1lLmZyb21PYmplY3QoeyB5ZWFyLCBtb250aCwgZGF5IH0pOwogICAgICB9CiAgICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQogIC8qKiBHZXQgdGhlIGZvbGRlciBjb250YWluaW5nIHRoZSBnaXZlbiBwYXRoIChpLmUuLCBsaWtlIGNvbXB1dGluZyAncGF0aC8uLicpLiAqLwogIGZ1bmN0aW9uIGdldFBhcmVudEZvbGRlcihwYXRoKSB7CiAgICAgIHJldHVybiBwYXRoLnNwbGl0KCIvIikuc2xpY2UoMCwgLTEpLmpvaW4oIi8iKTsKICB9CiAgLyoqIEdldCB0aGUgInRpdGxlIiBmb3IgYSBmaWxlLCBieSBzdHJpcHBpbmcgb3RoZXIgcGFydHMgb2YgdGhlIHBhdGggYXMgd2VsbCBhcyB0aGUgZXh0ZW5zaW9uLiAqLwogIGZ1bmN0aW9uIGdldEZpbGVUaXRsZShwYXRoKSB7CiAgICAgIGlmIChwYXRoLmluY2x1ZGVzKCIvIikpCiAgICAgICAgICBwYXRoID0gcGF0aC5zdWJzdHJpbmcocGF0aC5sYXN0SW5kZXhPZigiLyIpICsgMSk7CiAgICAgIGlmIChwYXRoLmVuZHNXaXRoKCIubWQiKSkKICAgICAgICAgIHBhdGggPSBwYXRoLnN1YnN0cmluZygwLCBwYXRoLmxlbmd0aCAtIDMpOwogICAgICByZXR1cm4gcGF0aDsKICB9CiAgLyoqIEdldCB0aGUgZXh0ZW5zaW9uIG9mIGEgZmlsZSBmcm9tIHRoZSBmaWxlIHBhdGguICovCiAgZnVuY3Rpb24gZ2V0RXh0ZW5zaW9uKHBhdGgpIHsKICAgICAgaWYgKCFwYXRoLmluY2x1ZGVzKCIuIikpCiAgICAgICAgICByZXR1cm4gIiI7CiAgICAgIHJldHVybiBwYXRoLnN1YnN0cmluZyhwYXRoLmxhc3RJbmRleE9mKCIuIikgKyAxKTsKICB9CiAgLyoqIFBhcnNlIGFsbCBzdWJ0YWdzIG91dCBvZiB0aGUgZ2l2ZW4gdGFnLiBJLmUuLCAjaGVsbG8vaS9hbSB3b3VsZCB5aWVsZCBbI2hlbGxvL2kvYW0sICNoZWxsby9pLCAjaGVsbG9dLiAqLwogIGZ1bmN0aW9uIGV4dHJhY3RTdWJ0YWdzKHRhZykgewogICAgICBsZXQgcmVzdWx0ID0gW3RhZ107CiAgICAgIHdoaWxlICh0YWcuaW5jbHVkZXMoIi8iKSkgewogICAgICAgICAgdGFnID0gdGFnLnN1YnN0cmluZygwLCB0YWcubGFzdEluZGV4T2YoIi8iKSk7CiAgICAgICAgICByZXN1bHQucHVzaCh0YWcpOwogICAgICB9CiAgICAgIHJldHVybiByZXN1bHQ7CiAgfQogIC8qKiBBIHBhcnNpbW1vbiBwYXJzZXIgd2hpY2ggY2Fub25pY2FsaXplcyB2YXJpYWJsZSBuYW1lcyB3aGlsZSBwcm9wZXJseSByZXNwZWN0aW5nIGVtb2ppLiAqLwogIGNvbnN0IFZBUl9OQU1FX0NBTk9OSUNBTElaRVIgPSBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuYWx0KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleChuZXcgUmVnRXhwKGVtb2ppUmVnZXgoKSwgIiIpKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4KC9bMC05XHB7TGV0dGVyfV8tXSsvdSkubWFwKHN0ciA9PiBzdHIudG9Mb2NhbGVMb3dlckNhc2UoKSksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy53aGl0ZXNwYWNlLm1hcChfID0+ICItIiksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbnkubWFwKF8gPT4gIiIpKQogICAgICAubWFueSgpCiAgICAgIC5tYXAocmVzdWx0ID0+IHJlc3VsdC5qb2luKCIiKSk7CiAgLyoqIENvbnZlcnQgYW4gYXJiaXRyYXJ5IHZhcmlhYmxlIG5hbWUgaW50byBzb21ldGhpbmcgSlMvcXVlcnkgZnJpZW5kbHkuICovCiAgZnVuY3Rpb24gY2Fub25pY2FsaXplVmFyTmFtZShuYW1lKSB7CiAgICAgIHJldHVybiBWQVJfTkFNRV9DQU5PTklDQUxJWkVSLnRyeVBhcnNlKG5hbWUpOwogIH0KICBjb25zdCBIRUFERVJfQ0FOT05JQ0FMSVpFUiA9IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4KG5ldyBSZWdFeHAoZW1vamlSZWdleCgpLCAiIikpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXgoL1swLTlccHtMZXR0ZXJ9Xy1dKy91KSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLndoaXRlc3BhY2UubWFwKF8gPT4gIiAiKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFueS5tYXAoXyA9PiAiICIpKQogICAgICAubWFueSgpCiAgICAgIC5tYXAocmVzdWx0ID0+IHsKICAgICAgcmV0dXJuIHJlc3VsdC5qb2luKCIiKS5zcGxpdCgvXHMrLykuam9pbigiICIpLnRyaW0oKTsKICB9KTsKICAvKioKICAgKiBOb3JtYWxpemVzIHRoZSB0ZXh0IGluIGEgaGVhZGVyIHRvIGJlIHNvbWV0aGluZyB0aGF0IGlzIGFjdHVhbGx5IGxpbmthYmxlIHRvLiBUaGlzIG1pbWljcwogICAqIGhvdyBPYnNpZGlhbiBkb2VzIGl0J3Mgbm9ybWFsaXphdGlvbiwgY29sbGFwc2luZyByZXBlYXRlZCBzcGFjZXMgYW5kIHN0cmlwcGluZyBvdXQgY29udHJvbCBjaGFyYWN0ZXJzLgogICAqLwogIGZ1bmN0aW9uIG5vcm1hbGl6ZUhlYWRlckZvckxpbmsoaGVhZGVyKSB7CiAgICAgIHJldHVybiBIRUFERVJfQ0FOT05JQ0FMSVpFUi50cnlQYXJzZShoZWFkZXIpOwogIH0KICAvKiogUmVuZGVyIGEgZHVyYXRpb24gaW4gYSBtaW5pbWFsIGZvcm1hdCB0byBzYXZlIHNwYWNlLiAqLwogIGZ1bmN0aW9uIHJlbmRlck1pbmltYWxEdXJhdGlvbihkdXIpIHsKICAgICAgZHVyID0gbm9ybWFsaXplRHVyYXRpb24oZHVyKTsKICAgICAgLy8gdG9IdW1hbiBvdXRwdXRzIHplcm8gcXVhbnRpdGllcyBlLmcuICIwIHNlY29uZHMiCiAgICAgIGR1ciA9IER1cmF0aW9uLmZyb21PYmplY3QoT2JqZWN0LmZyb21FbnRyaWVzKE9iamVjdC5lbnRyaWVzKGR1ci50b09iamVjdCgpKS5maWx0ZXIoKFssIHF1YW50aXR5XSkgPT4gcXVhbnRpdHkgIT0gMCkpKTsKICAgICAgcmV0dXJuIGR1ci50b0h1bWFuKCk7CiAgfQoKICB2YXIgVmFsdWVzOwogIChmdW5jdGlvbiAoVmFsdWVzKSB7CiAgICAgIC8qKiBDb252ZXJ0IGFuIGFyYml0cmFyeSB2YWx1ZSBpbnRvIGEgcmVhc29uYWJsZSwgTWFya2Rvd24tZnJpZW5kbHkgc3RyaW5nIGlmIHBvc3NpYmxlLiAqLwogICAgICBmdW5jdGlvbiB0b1N0cmluZyhmaWVsZCwgc2V0dGluZyA9IERFRkFVTFRfUVVFUllfU0VUVElOR1MsIHJlY3Vyc2l2ZSA9IGZhbHNlKSB7CiAgICAgICAgICBsZXQgd3JhcHBlZCA9IHdyYXBWYWx1ZShmaWVsZCk7CiAgICAgICAgICBpZiAoIXdyYXBwZWQpCiAgICAgICAgICAgICAgcmV0dXJuIHNldHRpbmcucmVuZGVyTnVsbEFzOwogICAgICAgICAgc3dpdGNoICh3cmFwcGVkLnR5cGUpIHsKICAgICAgICAgICAgICBjYXNlICJudWxsIjoKICAgICAgICAgICAgICAgICAgcmV0dXJuIHNldHRpbmcucmVuZGVyTnVsbEFzOwogICAgICAgICAgICAgIGNhc2UgInN0cmluZyI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB3cmFwcGVkLnZhbHVlOwogICAgICAgICAgICAgIGNhc2UgIm51bWJlciI6CiAgICAgICAgICAgICAgY2FzZSAiYm9vbGVhbiI6CiAgICAgICAgICAgICAgICAgIHJldHVybiAiIiArIHdyYXBwZWQudmFsdWU7CiAgICAgICAgICAgICAgY2FzZSAiaHRtbCI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB3cmFwcGVkLnZhbHVlLm91dGVySFRNTDsKICAgICAgICAgICAgICBjYXNlICJ3aWRnZXQiOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZS5tYXJrZG93bigpOwogICAgICAgICAgICAgIGNhc2UgImxpbmsiOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZS5tYXJrZG93bigpOwogICAgICAgICAgICAgIGNhc2UgImZ1bmN0aW9uIjoKICAgICAgICAgICAgICAgICAgcmV0dXJuICI8ZnVuY3Rpb24+IjsKICAgICAgICAgICAgICBjYXNlICJhcnJheSI6CiAgICAgICAgICAgICAgICAgIGxldCByZXN1bHQgPSAiIjsKICAgICAgICAgICAgICAgICAgaWYgKHJlY3Vyc2l2ZSkKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSAiWyI7CiAgICAgICAgICAgICAgICAgIHJlc3VsdCArPSB3cmFwcGVkLnZhbHVlLm1hcChmID0+IHRvU3RyaW5nKGYsIHNldHRpbmcsIHRydWUpKS5qb2luKCIsICIpOwogICAgICAgICAgICAgICAgICBpZiAocmVjdXJzaXZlKQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ICs9ICJdIjsKICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgICAgICBjYXNlICJvYmplY3QiOgogICAgICAgICAgICAgICAgICByZXR1cm4gKCJ7ICIgKwogICAgICAgICAgICAgICAgICAgICAgT2JqZWN0LmVudHJpZXMod3JhcHBlZC52YWx1ZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAubWFwKGUgPT4gZVswXSArICI6ICIgKyB0b1N0cmluZyhlWzFdLCBzZXR0aW5nLCB0cnVlKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAuam9pbigiLCAiKSArCiAgICAgICAgICAgICAgICAgICAgICAiIH0iKTsKICAgICAgICAgICAgICBjYXNlICJkYXRlIjoKICAgICAgICAgICAgICAgICAgaWYgKHdyYXBwZWQudmFsdWUuc2Vjb25kID09IDAgJiYgd3JhcHBlZC52YWx1ZS5ob3VyID09IDAgJiYgd3JhcHBlZC52YWx1ZS5taW51dGUgPT0gMCkgewogICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHdyYXBwZWQudmFsdWUudG9Gb3JtYXQoc2V0dGluZy5kZWZhdWx0RGF0ZUZvcm1hdCk7CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgcmV0dXJuIHdyYXBwZWQudmFsdWUudG9Gb3JtYXQoc2V0dGluZy5kZWZhdWx0RGF0ZVRpbWVGb3JtYXQpOwogICAgICAgICAgICAgIGNhc2UgImR1cmF0aW9uIjoKICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlbmRlck1pbmltYWxEdXJhdGlvbih3cmFwcGVkLnZhbHVlKTsKICAgICAgICAgIH0KICAgICAgfQogICAgICBWYWx1ZXMudG9TdHJpbmcgPSB0b1N0cmluZzsKICAgICAgLyoqIFdyYXAgYSBsaXRlcmFsIHZhbHVlIHNvIHlvdSBjYW4gc3dpdGNoIG9uIGl0IGVhc2lseS4gKi8KICAgICAgZnVuY3Rpb24gd3JhcFZhbHVlKHZhbCkgewogICAgICAgICAgaWYgKGlzTnVsbCh2YWwpKQogICAgICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJudWxsIiwgdmFsdWU6IHZhbCB9OwogICAgICAgICAgZWxzZSBpZiAoaXNOdW1iZXIodmFsKSkKICAgICAgICAgICAgICByZXR1cm4geyB0eXBlOiAibnVtYmVyIiwgdmFsdWU6IHZhbCB9OwogICAgICAgICAgZWxzZSBpZiAoaXNTdHJpbmcodmFsKSkKICAgICAgICAgICAgICByZXR1cm4geyB0eXBlOiAic3RyaW5nIiwgdmFsdWU6IHZhbCB9OwogICAgICAgICAgZWxzZSBpZiAoaXNCb29sZWFuKHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImJvb2xlYW4iLCB2YWx1ZTogdmFsIH07CiAgICAgICAgICBlbHNlIGlmIChpc0R1cmF0aW9uKHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImR1cmF0aW9uIiwgdmFsdWU6IHZhbCB9OwogICAgICAgICAgZWxzZSBpZiAoaXNEYXRlKHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImRhdGUiLCB2YWx1ZTogdmFsIH07CiAgICAgICAgICBlbHNlIGlmIChpc1dpZGdldCh2YWwpKQogICAgICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJ3aWRnZXQiLCB2YWx1ZTogdmFsIH07CiAgICAgICAgICBlbHNlIGlmIChpc0FycmF5KHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImFycmF5IiwgdmFsdWU6IHZhbCB9OwogICAgICAgICAgZWxzZSBpZiAoaXNMaW5rKHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImxpbmsiLCB2YWx1ZTogdmFsIH07CiAgICAgICAgICBlbHNlIGlmIChpc0Z1bmN0aW9uKHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImZ1bmN0aW9uIiwgdmFsdWU6IHZhbCB9OwogICAgICAgICAgZWxzZSBpZiAoaXNIdG1sKHZhbCkpCiAgICAgICAgICAgICAgcmV0dXJuIHsgdHlwZTogImh0bWwiLCB2YWx1ZTogdmFsIH07CiAgICAgICAgICBlbHNlIGlmIChpc09iamVjdCh2YWwpKQogICAgICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJvYmplY3QiLCB2YWx1ZTogdmFsIH07CiAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgICAgfQogICAgICBWYWx1ZXMud3JhcFZhbHVlID0gd3JhcFZhbHVlOwogICAgICAvKiogUmVjdXJzaXZlbHkgbWFwIGNvbXBsZXggb2JqZWN0cyBhdCB0aGUgbGVhdmVzLiAqLwogICAgICBmdW5jdGlvbiBtYXBMZWF2ZXModmFsLCBmdW5jKSB7CiAgICAgICAgICBpZiAoaXNPYmplY3QodmFsKSkgewogICAgICAgICAgICAgIGxldCByZXN1bHQgPSB7fTsKICAgICAgICAgICAgICBmb3IgKGxldCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXModmFsKSkKICAgICAgICAgICAgICAgICAgcmVzdWx0W2tleV0gPSBtYXBMZWF2ZXModmFsdWUsIGZ1bmMpOwogICAgICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIGlmIChpc0FycmF5KHZhbCkpIHsKICAgICAgICAgICAgICBsZXQgcmVzdWx0ID0gW107CiAgICAgICAgICAgICAgZm9yIChsZXQgdmFsdWUgb2YgdmFsKQogICAgICAgICAgICAgICAgICByZXN1bHQucHVzaChtYXBMZWF2ZXModmFsdWUsIGZ1bmMpKTsKICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgICAgfQogICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgcmV0dXJuIGZ1bmModmFsKTsKICAgICAgICAgIH0KICAgICAgfQogICAgICBWYWx1ZXMubWFwTGVhdmVzID0gbWFwTGVhdmVzOwogICAgICAvKiogQ29tcGFyZSB0d28gYXJiaXRyYXJ5IEphdmFTY3JpcHQgdmFsdWVzLiBQcm9kdWNlcyBhIHRvdGFsIG9yZGVyaW5nIG92ZXIgQU5ZIHBvc3NpYmxlIGRhdGF2aWV3IHZhbHVlLiAqLwogICAgICBmdW5jdGlvbiBjb21wYXJlVmFsdWUodmFsMSwgdmFsMiwgbGlua05vcm1hbGl6ZXIpIHsKICAgICAgICAgIC8vIEhhbmRsZSB1bmRlZmluZWQvbnVsbHMgZmlyc3QuCiAgICAgICAgICBpZiAodmFsMSA9PT0gdW5kZWZpbmVkKQogICAgICAgICAgICAgIHZhbDEgPSBudWxsOwogICAgICAgICAgaWYgKHZhbDIgPT09IHVuZGVmaW5lZCkKICAgICAgICAgICAgICB2YWwyID0gbnVsbDsKICAgICAgICAgIGlmICh2YWwxID09PSBudWxsICYmIHZhbDIgPT09IG51bGwpCiAgICAgICAgICAgICAgcmV0dXJuIDA7CiAgICAgICAgICBlbHNlIGlmICh2YWwxID09PSBudWxsKQogICAgICAgICAgICAgIHJldHVybiAtMTsKICAgICAgICAgIGVsc2UgaWYgKHZhbDIgPT09IG51bGwpCiAgICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgICAvLyBBIG5vbi1udWxsIHZhbHVlIG5vdyB3aGljaCB3ZSBjYW4gd3JhcCAmIGNvbXBhcmUgb24uCiAgICAgICAgICBsZXQgd3JhcDEgPSB3cmFwVmFsdWUodmFsMSk7CiAgICAgICAgICBsZXQgd3JhcDIgPSB3cmFwVmFsdWUodmFsMik7CiAgICAgICAgICBpZiAod3JhcDEgPT09IHVuZGVmaW5lZCAmJiB3cmFwMiA9PT0gdW5kZWZpbmVkKQogICAgICAgICAgICAgIHJldHVybiAwOwogICAgICAgICAgZWxzZSBpZiAod3JhcDEgPT09IHVuZGVmaW5lZCkKICAgICAgICAgICAgICByZXR1cm4gLTE7CiAgICAgICAgICBlbHNlIGlmICh3cmFwMiA9PT0gdW5kZWZpbmVkKQogICAgICAgICAgICAgIHJldHVybiAxOwogICAgICAgICAgLy8gU2hvcnQtY2lyY3VpdCBvbiBkaWZmZXJlbnQgdHlwZXMgb3Igb24gcmVmZXJlbmNlIGVxdWFsaXR5LgogICAgICAgICAgaWYgKHdyYXAxLnR5cGUgIT0gd3JhcDIudHlwZSkKICAgICAgICAgICAgICByZXR1cm4gd3JhcDEudHlwZS5sb2NhbGVDb21wYXJlKHdyYXAyLnR5cGUpOwogICAgICAgICAgaWYgKHdyYXAxLnZhbHVlID09PSB3cmFwMi52YWx1ZSkKICAgICAgICAgICAgICByZXR1cm4gMDsKICAgICAgICAgIHN3aXRjaCAod3JhcDEudHlwZSkgewogICAgICAgICAgICAgIGNhc2UgInN0cmluZyI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB3cmFwMS52YWx1ZS5sb2NhbGVDb21wYXJlKHdyYXAyLnZhbHVlKTsKICAgICAgICAgICAgICBjYXNlICJudW1iZXIiOgogICAgICAgICAgICAgICAgICBpZiAod3JhcDEudmFsdWUgPCB3cmFwMi52YWx1ZSkKICAgICAgICAgICAgICAgICAgICAgIHJldHVybiAtMTsKICAgICAgICAgICAgICAgICAgZWxzZSBpZiAod3JhcDEudmFsdWUgPT0gd3JhcDIudmFsdWUpCiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gMDsKICAgICAgICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgICAgICAgY2FzZSAibnVsbCI6CiAgICAgICAgICAgICAgICAgIHJldHVybiAwOwogICAgICAgICAgICAgIGNhc2UgImJvb2xlYW4iOgogICAgICAgICAgICAgICAgICBpZiAod3JhcDEudmFsdWUgPT0gd3JhcDIudmFsdWUpCiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gMDsKICAgICAgICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHdyYXAxLnZhbHVlID8gMSA6IC0xOwogICAgICAgICAgICAgIGNhc2UgImxpbmsiOgogICAgICAgICAgICAgICAgICBsZXQgbGluazEgPSB3cmFwMS52YWx1ZTsKICAgICAgICAgICAgICAgICAgbGV0IGxpbmsyID0gd3JhcDIudmFsdWU7CiAgICAgICAgICAgICAgICAgIGxldCBub3JtYWxpemUgPSBsaW5rTm9ybWFsaXplciA/PyAoKHgpID0+IHgpOwogICAgICAgICAgICAgICAgICAvLyBXZSBjYW4ndCBjb21wYXJlIGJ5IGZpbGUgbmFtZSBvciBkaXNwbGF5LCBzaW5jZSB0aGF0IHdvdWxkIGJyZWFrIGxpbmsgZXF1YWxpdHkuIENvbXBhcmUgYnkgcGF0aC4KICAgICAgICAgICAgICAgICAgbGV0IHBhdGhDb21wYXJlID0gbm9ybWFsaXplKGxpbmsxLnBhdGgpLmxvY2FsZUNvbXBhcmUobm9ybWFsaXplKGxpbmsyLnBhdGgpKTsKICAgICAgICAgICAgICAgICAgaWYgKHBhdGhDb21wYXJlICE9IDApCiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gcGF0aENvbXBhcmU7CiAgICAgICAgICAgICAgICAgIC8vIFRoZW4gY29tcGFyZSBieSB0eXBlLgogICAgICAgICAgICAgICAgICBsZXQgdHlwZUNvbXBhcmUgPSBsaW5rMS50eXBlLmxvY2FsZUNvbXBhcmUobGluazIudHlwZSk7CiAgICAgICAgICAgICAgICAgIGlmICh0eXBlQ29tcGFyZSAhPSAwKQogICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHR5cGVDb21wYXJlOwogICAgICAgICAgICAgICAgICAvLyBUaGVuIGNvbXBhcmUgYnkgc3VicGF0aCBleGlzdGVuY2UuCiAgICAgICAgICAgICAgICAgIGlmIChsaW5rMS5zdWJwYXRoICYmICFsaW5rMi5zdWJwYXRoKQogICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgICAgICAgICAgIGlmICghbGluazEuc3VicGF0aCAmJiBsaW5rMi5zdWJwYXRoKQogICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIC0xOwogICAgICAgICAgICAgICAgICBpZiAoIWxpbmsxLnN1YnBhdGggJiYgIWxpbmsyLnN1YnBhdGgpCiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gMDsKICAgICAgICAgICAgICAgICAgLy8gU2luY2UgYm90aCBoYXZlIGEgc3VicGF0aCwgY29tcGFyZSBieSBzdWJwYXRoLgogICAgICAgICAgICAgICAgICByZXR1cm4gKGxpbmsxLnN1YnBhdGggPz8gIiIpLmxvY2FsZUNvbXBhcmUobGluazIuc3VicGF0aCA/PyAiIik7CiAgICAgICAgICAgICAgY2FzZSAiZGF0ZSI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB3cmFwMS52YWx1ZSA8IHdyYXAyLnZhbHVlCiAgICAgICAgICAgICAgICAgICAgICA/IC0xCiAgICAgICAgICAgICAgICAgICAgICA6IHdyYXAxLnZhbHVlLmVxdWFscyh3cmFwMi52YWx1ZSkKICAgICAgICAgICAgICAgICAgICAgICAgICA/IDAKICAgICAgICAgICAgICAgICAgICAgICAgICA6IDE7CiAgICAgICAgICAgICAgY2FzZSAiZHVyYXRpb24iOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcDEudmFsdWUgPCB3cmFwMi52YWx1ZQogICAgICAgICAgICAgICAgICAgICAgPyAtMQogICAgICAgICAgICAgICAgICAgICAgOiB3cmFwMS52YWx1ZS5lcXVhbHMod3JhcDIudmFsdWUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgPyAwCiAgICAgICAgICAgICAgICAgICAgICAgICAgOiAxOwogICAgICAgICAgICAgIGNhc2UgImFycmF5IjoKICAgICAgICAgICAgICAgICAgbGV0IGYxID0gd3JhcDEudmFsdWU7CiAgICAgICAgICAgICAgICAgIGxldCBmMiA9IHdyYXAyLnZhbHVlOwogICAgICAgICAgICAgICAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgTWF0aC5taW4oZjEubGVuZ3RoLCBmMi5sZW5ndGgpOyBpbmRleCsrKSB7CiAgICAgICAgICAgICAgICAgICAgICBsZXQgY29tcCA9IGNvbXBhcmVWYWx1ZShmMVtpbmRleF0sIGYyW2luZGV4XSk7CiAgICAgICAgICAgICAgICAgICAgICBpZiAoY29tcCAhPSAwKQogICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjb21wOwogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIHJldHVybiBmMS5sZW5ndGggLSBmMi5sZW5ndGg7CiAgICAgICAgICAgICAgY2FzZSAib2JqZWN0IjoKICAgICAgICAgICAgICAgICAgbGV0IG8xID0gd3JhcDEudmFsdWU7CiAgICAgICAgICAgICAgICAgIGxldCBvMiA9IHdyYXAyLnZhbHVlOwogICAgICAgICAgICAgICAgICBsZXQgazEgPSBBcnJheS5mcm9tKE9iamVjdC5rZXlzKG8xKSk7CiAgICAgICAgICAgICAgICAgIGxldCBrMiA9IEFycmF5LmZyb20oT2JqZWN0LmtleXMobzIpKTsKICAgICAgICAgICAgICAgICAgazEuc29ydCgpOwogICAgICAgICAgICAgICAgICBrMi5zb3J0KCk7CiAgICAgICAgICAgICAgICAgIGxldCBrZXlDb21wYXJlID0gY29tcGFyZVZhbHVlKGsxLCBrMik7CiAgICAgICAgICAgICAgICAgIGlmIChrZXlDb21wYXJlICE9IDApCiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ga2V5Q29tcGFyZTsKICAgICAgICAgICAgICAgICAgZm9yIChsZXQga2V5IG9mIGsxKSB7CiAgICAgICAgICAgICAgICAgICAgICBsZXQgY29tcCA9IGNvbXBhcmVWYWx1ZShvMVtrZXldLCBvMltrZXldKTsKICAgICAgICAgICAgICAgICAgICAgIGlmIChjb21wICE9IDApCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGNvbXA7CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgcmV0dXJuIDA7CiAgICAgICAgICAgICAgY2FzZSAid2lkZ2V0IjoKICAgICAgICAgICAgICBjYXNlICJodG1sIjoKICAgICAgICAgICAgICBjYXNlICJmdW5jdGlvbiI6CiAgICAgICAgICAgICAgICAgIHJldHVybiAwOwogICAgICAgICAgfQogICAgICB9CiAgICAgIFZhbHVlcy5jb21wYXJlVmFsdWUgPSBjb21wYXJlVmFsdWU7CiAgICAgIC8qKiBGaW5kIHRoZSBjb3JyZXNwb25kaW5nIERhdGF2aWV3IHR5cGUgZm9yIGFuIGFyYml0cmFyeSB2YWx1ZS4gKi8KICAgICAgZnVuY3Rpb24gdHlwZU9mKHZhbCkgewogICAgICAgICAgcmV0dXJuIHdyYXBWYWx1ZSh2YWwpPy50eXBlOwogICAgICB9CiAgICAgIFZhbHVlcy50eXBlT2YgPSB0eXBlT2Y7CiAgICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhlIGdpdmVuIHZhbHVlIGlzICJ0cnV0aHkiIChpLmUuLCBpcyBub24tbnVsbCBhbmQgaGFzIGRhdGEgaW4gaXQpLiAqLwogICAgICBmdW5jdGlvbiBpc1RydXRoeShmaWVsZCkgewogICAgICAgICAgbGV0IHdyYXBwZWQgPSB3cmFwVmFsdWUoZmllbGQpOwogICAgICAgICAgaWYgKCF3cmFwcGVkKQogICAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICAgIHN3aXRjaCAod3JhcHBlZC50eXBlKSB7CiAgICAgICAgICAgICAgY2FzZSAibnVtYmVyIjoKICAgICAgICAgICAgICAgICAgcmV0dXJuIHdyYXBwZWQudmFsdWUgIT0gMDsKICAgICAgICAgICAgICBjYXNlICJzdHJpbmciOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZS5sZW5ndGggPiAwOwogICAgICAgICAgICAgIGNhc2UgImJvb2xlYW4iOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZTsKICAgICAgICAgICAgICBjYXNlICJsaW5rIjoKICAgICAgICAgICAgICAgICAgcmV0dXJuICEhd3JhcHBlZC52YWx1ZS5wYXRoOwogICAgICAgICAgICAgIGNhc2UgImRhdGUiOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZS50b01pbGxpcygpICE9IDA7CiAgICAgICAgICAgICAgY2FzZSAiZHVyYXRpb24iOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZS5hcygic2Vjb25kcyIpICE9IDA7CiAgICAgICAgICAgICAgY2FzZSAib2JqZWN0IjoKICAgICAgICAgICAgICAgICAgcmV0dXJuIE9iamVjdC5rZXlzKHdyYXBwZWQudmFsdWUpLmxlbmd0aCA+IDA7CiAgICAgICAgICAgICAgY2FzZSAiYXJyYXkiOgogICAgICAgICAgICAgICAgICByZXR1cm4gd3JhcHBlZC52YWx1ZS5sZW5ndGggPiAwOwogICAgICAgICAgICAgIGNhc2UgIm51bGwiOgogICAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICAgICAgY2FzZSAiaHRtbCI6CiAgICAgICAgICAgICAgY2FzZSAid2lkZ2V0IjoKICAgICAgICAgICAgICBjYXNlICJmdW5jdGlvbiI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgICAgfQogICAgICB9CiAgICAgIFZhbHVlcy5pc1RydXRoeSA9IGlzVHJ1dGh5OwogICAgICAvKiogRGVlcCBjb3B5IGEgZmllbGQuICovCiAgICAgIGZ1bmN0aW9uIGRlZXBDb3B5KGZpZWxkKSB7CiAgICAgICAgICBpZiAoZmllbGQgPT09IG51bGwgfHwgZmllbGQgPT09IHVuZGVmaW5lZCkKICAgICAgICAgICAgICByZXR1cm4gZmllbGQ7CiAgICAgICAgICBpZiAoVmFsdWVzLmlzQXJyYXkoZmllbGQpKSB7CiAgICAgICAgICAgICAgcmV0dXJuIFtdLmNvbmNhdChmaWVsZC5tYXAodiA9PiBkZWVwQ29weSh2KSkpOwogICAgICAgICAgfQogICAgICAgICAgZWxzZSBpZiAoVmFsdWVzLmlzT2JqZWN0KGZpZWxkKSkgewogICAgICAgICAgICAgIGxldCByZXN1bHQgPSB7fTsKICAgICAgICAgICAgICBmb3IgKGxldCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoZmllbGQpKQogICAgICAgICAgICAgICAgICByZXN1bHRba2V5XSA9IGRlZXBDb3B5KHZhbHVlKTsKICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgICAgfQogICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgcmV0dXJuIGZpZWxkOwogICAgICAgICAgfQogICAgICB9CiAgICAgIFZhbHVlcy5kZWVwQ29weSA9IGRlZXBDb3B5OwogICAgICBmdW5jdGlvbiBpc1N0cmluZyh2YWwpIHsKICAgICAgICAgIHJldHVybiB0eXBlb2YgdmFsID09ICJzdHJpbmciOwogICAgICB9CiAgICAgIFZhbHVlcy5pc1N0cmluZyA9IGlzU3RyaW5nOwogICAgICBmdW5jdGlvbiBpc051bWJlcih2YWwpIHsKICAgICAgICAgIHJldHVybiB0eXBlb2YgdmFsID09ICJudW1iZXIiOwogICAgICB9CiAgICAgIFZhbHVlcy5pc051bWJlciA9IGlzTnVtYmVyOwogICAgICBmdW5jdGlvbiBpc0RhdGUodmFsKSB7CiAgICAgICAgICByZXR1cm4gdmFsIGluc3RhbmNlb2YgRGF0ZVRpbWU7CiAgICAgIH0KICAgICAgVmFsdWVzLmlzRGF0ZSA9IGlzRGF0ZTsKICAgICAgZnVuY3Rpb24gaXNEdXJhdGlvbih2YWwpIHsKICAgICAgICAgIHJldHVybiB2YWwgaW5zdGFuY2VvZiBEdXJhdGlvbjsKICAgICAgfQogICAgICBWYWx1ZXMuaXNEdXJhdGlvbiA9IGlzRHVyYXRpb247CiAgICAgIGZ1bmN0aW9uIGlzTnVsbCh2YWwpIHsKICAgICAgICAgIHJldHVybiB2YWwgPT09IG51bGwgfHwgdmFsID09PSB1bmRlZmluZWQ7CiAgICAgIH0KICAgICAgVmFsdWVzLmlzTnVsbCA9IGlzTnVsbDsKICAgICAgZnVuY3Rpb24gaXNBcnJheSh2YWwpIHsKICAgICAgICAgIHJldHVybiBBcnJheS5pc0FycmF5KHZhbCk7CiAgICAgIH0KICAgICAgVmFsdWVzLmlzQXJyYXkgPSBpc0FycmF5OwogICAgICBmdW5jdGlvbiBpc0Jvb2xlYW4odmFsKSB7CiAgICAgICAgICByZXR1cm4gdHlwZW9mIHZhbCA9PT0gImJvb2xlYW4iOwogICAgICB9CiAgICAgIFZhbHVlcy5pc0Jvb2xlYW4gPSBpc0Jvb2xlYW47CiAgICAgIGZ1bmN0aW9uIGlzTGluayh2YWwpIHsKICAgICAgICAgIHJldHVybiB2YWwgaW5zdGFuY2VvZiBMaW5rOwogICAgICB9CiAgICAgIFZhbHVlcy5pc0xpbmsgPSBpc0xpbms7CiAgICAgIGZ1bmN0aW9uIGlzV2lkZ2V0KHZhbCkgewogICAgICAgICAgcmV0dXJuIHZhbCBpbnN0YW5jZW9mIFdpZGdldDsKICAgICAgfQogICAgICBWYWx1ZXMuaXNXaWRnZXQgPSBpc1dpZGdldDsKICAgICAgZnVuY3Rpb24gaXNIdG1sKHZhbCkgewogICAgICAgICAgaWYgKHR5cGVvZiBIVE1MRWxlbWVudCAhPT0gInVuZGVmaW5lZCIpIHsKICAgICAgICAgICAgICByZXR1cm4gdmFsIGluc3RhbmNlb2YgSFRNTEVsZW1lbnQ7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICB9CiAgICAgIH0KICAgICAgVmFsdWVzLmlzSHRtbCA9IGlzSHRtbDsKICAgICAgLyoqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gdmFsdWUgaXMgYW4gb2JqZWN0IChhbmQgbm90IGFueSBvdGhlciBkYXRhdmlldy1yZWNvZ25pemVkIG9iamVjdC1saWtlIHR5cGUpLiAqLwogICAgICBmdW5jdGlvbiBpc09iamVjdCh2YWwpIHsKICAgICAgICAgIHJldHVybiAodHlwZW9mIHZhbCA9PSAib2JqZWN0IiAmJgogICAgICAgICAgICAgICFpc0h0bWwodmFsKSAmJgogICAgICAgICAgICAgICFpc1dpZGdldCh2YWwpICYmCiAgICAgICAgICAgICAgIWlzQXJyYXkodmFsKSAmJgogICAgICAgICAgICAgICFpc0R1cmF0aW9uKHZhbCkgJiYKICAgICAgICAgICAgICAhaXNEYXRlKHZhbCkgJiYKICAgICAgICAgICAgICAhaXNMaW5rKHZhbCkgJiYKICAgICAgICAgICAgICB2YWwgIT09IHVuZGVmaW5lZCAmJgogICAgICAgICAgICAgICFpc051bGwodmFsKSk7CiAgICAgIH0KICAgICAgVmFsdWVzLmlzT2JqZWN0ID0gaXNPYmplY3Q7CiAgICAgIGZ1bmN0aW9uIGlzRnVuY3Rpb24odmFsKSB7CiAgICAgICAgICByZXR1cm4gdHlwZW9mIHZhbCA9PSAiZnVuY3Rpb24iOwogICAgICB9CiAgICAgIFZhbHVlcy5pc0Z1bmN0aW9uID0gaXNGdW5jdGlvbjsKICB9KShWYWx1ZXMgfHwgKFZhbHVlcyA9IHt9KSk7CiAgLy8vLy8vLy8vLy8vLy8vCiAgLy8gR3JvdXBpbmdzIC8vCiAgLy8vLy8vLy8vLy8vLy8vCiAgdmFyIEdyb3VwaW5nczsKICAoZnVuY3Rpb24gKEdyb3VwaW5ncykgewogICAgICAvKiogRGV0ZXJtaW5lcyBpZiB0aGUgZ2l2ZW4gZ3JvdXAgZW50cnkgaXMgYSBzdGFuZGFsb25lIHZhbHVlLCBvciBhIGdyb3VwaW5nIG9mIHN1Yi1lbnRyaWVzLiAqLwogICAgICBmdW5jdGlvbiBpc0VsZW1lbnRHcm91cChlbnRyeSkgewogICAgICAgICAgcmV0dXJuIFZhbHVlcy5pc09iamVjdChlbnRyeSkgJiYgT2JqZWN0LmtleXMoZW50cnkpLmxlbmd0aCA9PSAyICYmICJrZXkiIGluIGVudHJ5ICYmICJyb3dzIiBpbiBlbnRyeTsKICAgICAgfQogICAgICBHcm91cGluZ3MuaXNFbGVtZW50R3JvdXAgPSBpc0VsZW1lbnRHcm91cDsKICAgICAgLyoqIERldGVybWluZXMgaWYgdGhlIGdpdmVuIGFycmF5IGlzIGEgZ3JvdXBpbmcgYXJyYXkuICovCiAgICAgIGZ1bmN0aW9uIGlzR3JvdXBpbmcoZW50cnkpIHsKICAgICAgICAgIGZvciAobGV0IGVsZW1lbnQgb2YgZW50cnkpCiAgICAgICAgICAgICAgaWYgKCFpc0VsZW1lbnRHcm91cChlbGVtZW50KSkKICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgIH0KICAgICAgR3JvdXBpbmdzLmlzR3JvdXBpbmcgPSBpc0dyb3VwaW5nOwogICAgICAvKiogQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiBlbGVtZW50cyBpbiBhIHJlY3Vyc2l2ZSBncm91cGluZy4gKi8KICAgICAgZnVuY3Rpb24gY291bnQoZWxlbWVudHMpIHsKICAgICAgICAgIGlmIChpc0dyb3VwaW5nKGVsZW1lbnRzKSkgewogICAgICAgICAgICAgIGxldCByZXN1bHQgPSAwOwogICAgICAgICAgICAgIGZvciAobGV0IHN1Ymdyb3VwIG9mIGVsZW1lbnRzKQogICAgICAgICAgICAgICAgICByZXN1bHQgKz0gY291bnQoc3ViZ3JvdXAucm93cyk7CiAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgIHJldHVybiBlbGVtZW50cy5sZW5ndGg7CiAgICAgICAgICB9CiAgICAgIH0KICAgICAgR3JvdXBpbmdzLmNvdW50ID0gY291bnQ7CiAgfSkoR3JvdXBpbmdzIHx8IChHcm91cGluZ3MgPSB7fSkpOwogIC8vLy8vLy8vLy8KICAvLyBMSU5LIC8vCiAgLy8vLy8vLy8vLwogIC8qKiBUaGUgT2JzaWRpYW4gJ2xpbmsnLCB1c2VkIGZvciB1bmlxdWVseSBkZXNjcmliaW5nIGEgZmlsZSwgaGVhZGVyLCBvciBibG9jay4gKi8KICBjbGFzcyBMaW5rIHsKICAgICAgLyoqIFRoZSBmaWxlIHBhdGggdGhpcyBsaW5rIHBvaW50cyB0by4gKi8KICAgICAgcGF0aDsKICAgICAgLyoqIFRoZSBkaXNwbGF5IG5hbWUgYXNzb2NpYXRlZCB3aXRoIHRoZSBsaW5rLiAqLwogICAgICBkaXNwbGF5OwogICAgICAvKiogVGhlIGJsb2NrIElEIG9yIGhlYWRlciB0aGlzIGxpbmsgcG9pbnRzIHRvIHdpdGhpbiBhIGZpbGUsIGlmIHJlbGV2YW50LiAqLwogICAgICBzdWJwYXRoOwogICAgICAvKiogSXMgdGhpcyBsaW5rIGFuIGVtYmVkZGVkIGxpbmsgKCEpPyAqLwogICAgICBlbWJlZDsKICAgICAgLyoqIFRoZSB0eXBlIG9mIHRoaXMgbGluaywgd2hpY2ggZGV0ZXJtaW5lcyB3aGF0ICdzdWJwYXRoJyByZWZlcnMgdG8sIGlmIGFueXRoaW5nLiAqLwogICAgICB0eXBlOwogICAgICAvKiogQ3JlYXRlIGEgbGluayB0byBhIHNwZWNpZmljIGZpbGUuICovCiAgICAgIHN0YXRpYyBmaWxlKHBhdGgsIGVtYmVkID0gZmFsc2UsIGRpc3BsYXkpIHsKICAgICAgICAgIHJldHVybiBuZXcgTGluayh7CiAgICAgICAgICAgICAgcGF0aCwKICAgICAgICAgICAgICBlbWJlZCwKICAgICAgICAgICAgICBkaXNwbGF5LAogICAgICAgICAgICAgIHN1YnBhdGg6IHVuZGVmaW5lZCwKICAgICAgICAgICAgICB0eXBlOiAiZmlsZSIsCiAgICAgICAgICB9KTsKICAgICAgfQogICAgICBzdGF0aWMgaW5mZXIobGlua3BhdGgsIGVtYmVkID0gZmFsc2UsIGRpc3BsYXkpIHsKICAgICAgICAgIGlmIChsaW5rcGF0aC5pbmNsdWRlcygiI14iKSkgewogICAgICAgICAgICAgIGxldCBzcGxpdCA9IGxpbmtwYXRoLnNwbGl0KCIjXiIpOwogICAgICAgICAgICAgIHJldHVybiBMaW5rLmJsb2NrKHNwbGl0WzBdLCBzcGxpdFsxXSwgZW1iZWQsIGRpc3BsYXkpOwogICAgICAgICAgfQogICAgICAgICAgZWxzZSBpZiAobGlua3BhdGguaW5jbHVkZXMoIiMiKSkgewogICAgICAgICAgICAgIGxldCBzcGxpdCA9IGxpbmtwYXRoLnNwbGl0KCIjIik7CiAgICAgICAgICAgICAgcmV0dXJuIExpbmsuaGVhZGVyKHNwbGl0WzBdLCBzcGxpdFsxXSwgZW1iZWQsIGRpc3BsYXkpOwogICAgICAgICAgfQogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIHJldHVybiBMaW5rLmZpbGUobGlua3BhdGgsIGVtYmVkLCBkaXNwbGF5KTsKICAgICAgfQogICAgICAvKiogQ3JlYXRlIGEgbGluayB0byBhIHNwZWNpZmljIGZpbGUgYW5kIGhlYWRlciBpbiB0aGF0IGZpbGUuICovCiAgICAgIHN0YXRpYyBoZWFkZXIocGF0aCwgaGVhZGVyLCBlbWJlZCwgZGlzcGxheSkgewogICAgICAgICAgLy8gSGVhZGVycyBuZWVkIHRvIGJlIG5vcm1hbGl6ZWQgdG8gYWxwaGEtbnVtZXJpYyAmIHdpdGggZXh0cmEgc3BhY2luZyByZW1vdmVkLgogICAgICAgICAgcmV0dXJuIG5ldyBMaW5rKHsKICAgICAgICAgICAgICBwYXRoLAogICAgICAgICAgICAgIGVtYmVkLAogICAgICAgICAgICAgIGRpc3BsYXksCiAgICAgICAgICAgICAgc3VicGF0aDogbm9ybWFsaXplSGVhZGVyRm9yTGluayhoZWFkZXIpLAogICAgICAgICAgICAgIHR5cGU6ICJoZWFkZXIiLAogICAgICAgICAgfSk7CiAgICAgIH0KICAgICAgLyoqIENyZWF0ZSBhIGxpbmsgdG8gYSBzcGVjaWZpYyBmaWxlIGFuZCBibG9jayBpbiB0aGF0IGZpbGUuICovCiAgICAgIHN0YXRpYyBibG9jayhwYXRoLCBibG9ja0lkLCBlbWJlZCwgZGlzcGxheSkgewogICAgICAgICAgcmV0dXJuIG5ldyBMaW5rKHsKICAgICAgICAgICAgICBwYXRoLAogICAgICAgICAgICAgIGVtYmVkLAogICAgICAgICAgICAgIGRpc3BsYXksCiAgICAgICAgICAgICAgc3VicGF0aDogYmxvY2tJZCwKICAgICAgICAgICAgICB0eXBlOiAiYmxvY2siLAogICAgICAgICAgfSk7CiAgICAgIH0KICAgICAgc3RhdGljIGZyb21PYmplY3Qob2JqZWN0KSB7CiAgICAgICAgICByZXR1cm4gbmV3IExpbmsob2JqZWN0KTsKICAgICAgfQogICAgICBjb25zdHJ1Y3RvcihmaWVsZHMpIHsKICAgICAgICAgIE9iamVjdC5hc3NpZ24odGhpcywgZmllbGRzKTsKICAgICAgfQogICAgICAvKiogQ2hlY2tzIGZvciBsaW5rIGVxdWFsaXR5IChpLmUuLCB0aGF0IHRoZSBsaW5rcyBhcmUgcG9pbnRpbmcgdG8gdGhlIHNhbWUgZXhhY3QgbG9jYXRpb24pLiAqLwogICAgICBlcXVhbHMob3RoZXIpIHsKICAgICAgICAgIGlmIChvdGhlciA9PSB1bmRlZmluZWQgfHwgb3RoZXIgPT0gbnVsbCkKICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgICByZXR1cm4gdGhpcy5wYXRoID09IG90aGVyLnBhdGggJiYgdGhpcy50eXBlID09IG90aGVyLnR5cGUgJiYgdGhpcy5zdWJwYXRoID09IG90aGVyLnN1YnBhdGg7CiAgICAgIH0KICAgICAgLyoqIENvbnZlcnQgdGhpcyBsaW5rIHRvIGl0J3MgbWFya2Rvd24gcmVwcmVzZW50YXRpb24uICovCiAgICAgIHRvU3RyaW5nKCkgewogICAgICAgICAgcmV0dXJuIHRoaXMubWFya2Rvd24oKTsKICAgICAgfQogICAgICAvKiogQ29udmVydCB0aGlzIGxpbmsgdG8gYSByYXcgb2JqZWN0IHdoaWNoIGlzIHNlcmlhbGl6YXRpb24tZnJpZW5kbHkuICovCiAgICAgIHRvT2JqZWN0KCkgewogICAgICAgICAgcmV0dXJuIHsgcGF0aDogdGhpcy5wYXRoLCB0eXBlOiB0aGlzLnR5cGUsIHN1YnBhdGg6IHRoaXMuc3VicGF0aCwgZGlzcGxheTogdGhpcy5kaXNwbGF5LCBlbWJlZDogdGhpcy5lbWJlZCB9OwogICAgICB9CiAgICAgIC8qKiBVcGRhdGUgdGhpcyBsaW5rIHdpdGggYSBuZXcgcGF0aC4gKi8KICAgICAgLy9AdHMtaWdub3JlOyBlcnJvciBhcHBlYXJlZCBhZnRlciB1cGRhdGluZyBPYnNpZGlhbiB0byAwLjE1LjQ7IGl0IGFsc28gdXBkYXRlZCBvdGhlciBwYWNrYWdlcyBidXQgZGlkbid0IHNheSB3aGljaAogICAgICB3aXRoUGF0aChwYXRoKSB7CiAgICAgICAgICByZXR1cm4gbmV3IExpbmsoT2JqZWN0LmFzc2lnbih7fSwgdGhpcywgeyBwYXRoIH0pKTsKICAgICAgfQogICAgICAvKiogUmV0dXJuIGEgbmV3IGxpbmsgd2hpY2ggcG9pbnRzIHRvIHRoZSBzYW1lIGxvY2F0aW9uIGJ1dCB3aXRoIGEgbmV3IGRpc3BsYXkgdmFsdWUuICovCiAgICAgIHdpdGhEaXNwbGF5KGRpc3BsYXkpIHsKICAgICAgICAgIHJldHVybiBuZXcgTGluayhPYmplY3QuYXNzaWduKHt9LCB0aGlzLCB7IGRpc3BsYXkgfSkpOwogICAgICB9CiAgICAgIC8qKiBDb252ZXJ0IGEgZmlsZSBsaW5rIGludG8gYSBsaW5rIHRvIGEgc3BlY2lmaWMgaGVhZGVyLiAqLwogICAgICB3aXRoSGVhZGVyKGhlYWRlcikgewogICAgICAgICAgcmV0dXJuIExpbmsuaGVhZGVyKHRoaXMucGF0aCwgaGVhZGVyLCB0aGlzLmVtYmVkLCB0aGlzLmRpc3BsYXkpOwogICAgICB9CiAgICAgIC8qKiBDb252ZXJ0IGFueSBsaW5rIGludG8gYSBsaW5rIHRvIGl0cyBmaWxlLiAqLwogICAgICB0b0ZpbGUoKSB7CiAgICAgICAgICByZXR1cm4gTGluay5maWxlKHRoaXMucGF0aCwgdGhpcy5lbWJlZCwgdGhpcy5kaXNwbGF5KTsKICAgICAgfQogICAgICAvKiogQ29udmVydCB0aGlzIGxpbmsgaW50byBhbiBlbWJlZGRlZCBsaW5rLiAqLwogICAgICB0b0VtYmVkKCkgewogICAgICAgICAgaWYgKHRoaXMuZW1iZWQpIHsKICAgICAgICAgICAgICByZXR1cm4gdGhpczsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgIGxldCBsaW5rID0gbmV3IExpbmsodGhpcyk7CiAgICAgICAgICAgICAgbGluay5lbWJlZCA9IHRydWU7CiAgICAgICAgICAgICAgcmV0dXJuIGxpbms7CiAgICAgICAgICB9CiAgICAgIH0KICAgICAgLyoqIENvbnZlcnQgdGhpcyBsaW5rIGludG8gYSBub24tZW1iZWRkZWQgbGluay4gKi8KICAgICAgZnJvbUVtYmVkKCkgewogICAgICAgICAgaWYgKCF0aGlzLmVtYmVkKSB7CiAgICAgICAgICAgICAgcmV0dXJuIHRoaXM7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICBsZXQgbGluayA9IG5ldyBMaW5rKHRoaXMpOwogICAgICAgICAgICAgIGxpbmsuZW1iZWQgPSBmYWxzZTsKICAgICAgICAgICAgICByZXR1cm4gbGluazsKICAgICAgICAgIH0KICAgICAgfQogICAgICAvKiogQ29udmVydCB0aGlzIGxpbmsgdG8gbWFya2Rvd24gc28gaXQgY2FuIGJlIHJlbmRlcmVkLiAqLwogICAgICBtYXJrZG93bigpIHsKICAgICAgICAgIGxldCByZXN1bHQgPSAodGhpcy5lbWJlZCA/ICIhIiA6ICIiKSArICJbWyIgKyB0aGlzLm9ic2lkaWFuTGluaygpOwogICAgICAgICAgaWYgKHRoaXMuZGlzcGxheSkgewogICAgICAgICAgICAgIHJlc3VsdCArPSAifCIgKyB0aGlzLmRpc3BsYXk7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIHsKICAgICAgICAgICAgICByZXN1bHQgKz0gInwiICsgZ2V0RmlsZVRpdGxlKHRoaXMucGF0aCk7CiAgICAgICAgICAgICAgaWYgKHRoaXMudHlwZSA9PSAiaGVhZGVyIiB8fCB0aGlzLnR5cGUgPT0gImJsb2NrIikKICAgICAgICAgICAgICAgICAgcmVzdWx0ICs9ICIgPiAiICsgdGhpcy5zdWJwYXRoOwogICAgICAgICAgfQogICAgICAgICAgcmVzdWx0ICs9ICJdXSI7CiAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICB9CiAgICAgIC8qKiBDb252ZXJ0IHRoZSBpbm5lciBwYXJ0IG9mIHRoZSBsaW5rIHRvIHNvbWV0aGluZyB0aGF0IE9ic2lkaWFuIGNhbiBvcGVuIC8gdW5kZXJzdGFuZC4gKi8KICAgICAgb2JzaWRpYW5MaW5rKCkgewogICAgICAgICAgY29uc3QgZXNjYXBlZCA9IHRoaXMucGF0aC5yZXBsYWNlQWxsKCJ8IiwgIlxcfCIpOwogICAgICAgICAgaWYgKHRoaXMudHlwZSA9PSAiaGVhZGVyIikKICAgICAgICAgICAgICByZXR1cm4gZXNjYXBlZCArICIjIiArIHRoaXMuc3VicGF0aD8ucmVwbGFjZUFsbCgifCIsICJcXHwiKTsKICAgICAgICAgIGlmICh0aGlzLnR5cGUgPT0gImJsb2NrIikKICAgICAgICAgICAgICByZXR1cm4gZXNjYXBlZCArICIjXiIgKyB0aGlzLnN1YnBhdGg/LnJlcGxhY2VBbGwoInwiLCAiXFx8Iik7CiAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgcmV0dXJuIGVzY2FwZWQ7CiAgICAgIH0KICAgICAgLyoqIFRoZSBzdHJpcHBlZCBuYW1lIG9mIHRoZSBmaWxlIHRoaXMgbGluayBwb2ludHMgdG8uICovCiAgICAgIGZpbGVOYW1lKCkgewogICAgICAgICAgcmV0dXJuIGdldEZpbGVUaXRsZSh0aGlzLnBhdGgpLnJlcGxhY2UoIi5tZCIsICIiKTsKICAgICAgfQogIH0KICAvLy8vLy8vLy8vLy8vLy8vLwogIC8vIFdJREdFVCBCQVNFIC8vCiAgLy8vLy8vLy8vLy8vLy8vLy8KICAvKioKICAgKiBBIHRyaXZpYWwgYmFzZSBjbGFzcyB3aGljaCBqdXN0IGRlZmluZXMgdGhlICckd2lkZ2V0JyBpZGVudGlmaWVyIHR5cGUuIFN1YnR5cGVzIG9mCiAgICogd2lkZ2V0IGFyZSByZXNwb25zaWJsZSBmb3IgYWRkaW5nIHdoYXRldmVyIG1ldGFkYXRhIGlzIHJlbGV2YW50LiBJZiB5b3Ugd2FudCB5b3VyIHdpZGdldAogICAqIHRvIGhhdmUgcmVuZGVyaW5nIGZ1bmN0aW9uYWxpdHkgKHdoaWNoIHlvdSBwcm9iYWJseSBkbyksIHlvdSBzaG91bGQgZXh0ZW5kIGBSZW5kZXJXaWRnZXRgLgogICAqLwogIGNsYXNzIFdpZGdldCB7CiAgICAgICR3aWRnZXQ7CiAgICAgIGNvbnN0cnVjdG9yKCR3aWRnZXQpIHsKICAgICAgICAgIHRoaXMuJHdpZGdldCA9ICR3aWRnZXQ7CiAgICAgIH0KICB9CiAgLyoqIEEgdHJpdmlhbCB3aWRnZXQgd2hpY2ggcmVuZGVycyBhIChrZXksIHZhbHVlKSBwYWlyLCBhbmQgYWxsb3dzIGFjY2Vzc2luZyB0aGUga2V5IGFuZCB2YWx1ZS4gKi8KICBjbGFzcyBMaXN0UGFpcldpZGdldCBleHRlbmRzIFdpZGdldCB7CiAgICAgIGtleTsKICAgICAgdmFsdWU7CiAgICAgIGNvbnN0cnVjdG9yKGtleSwgdmFsdWUpIHsKICAgICAgICAgIHN1cGVyKCJkYXRhdmlldzpsaXN0LXBhaXIiKTsKICAgICAgICAgIHRoaXMua2V5ID0ga2V5OwogICAgICAgICAgdGhpcy52YWx1ZSA9IHZhbHVlOwogICAgICB9CiAgICAgIG1hcmtkb3duKCkgewogICAgICAgICAgcmV0dXJuIGAke1ZhbHVlcy50b1N0cmluZyh0aGlzLmtleSl9OiAke1ZhbHVlcy50b1N0cmluZyh0aGlzLnZhbHVlKX1gOwogICAgICB9CiAgfQogIC8qKiBBIHNpbXBsZSB3aWRnZXQgd2hpY2ggcmVuZGVycyBhbiBleHRlcm5hbCBsaW5rLiAqLwogIGNsYXNzIEV4dGVybmFsTGlua1dpZGdldCBleHRlbmRzIFdpZGdldCB7CiAgICAgIHVybDsKICAgICAgZGlzcGxheTsKICAgICAgY29uc3RydWN0b3IodXJsLCBkaXNwbGF5KSB7CiAgICAgICAgICBzdXBlcigiZGF0YXZpZXc6ZXh0ZXJuYWwtbGluayIpOwogICAgICAgICAgdGhpcy51cmwgPSB1cmw7CiAgICAgICAgICB0aGlzLmRpc3BsYXkgPSBkaXNwbGF5OwogICAgICB9CiAgICAgIG1hcmtkb3duKCkgewogICAgICAgICAgcmV0dXJuIGBbJHt0aGlzLmRpc3BsYXkgPz8gdGhpcy51cmx9XSgke3RoaXMudXJsfSlgOwogICAgICB9CiAgfQogIHZhciBXaWRnZXRzOwogIChmdW5jdGlvbiAoV2lkZ2V0cykgewogICAgICAvKiogQ3JlYXRlIGEgbGlzdCBwYWlyIHdpZGdldCBtYXRjaGluZyB0aGUgZ2l2ZW4ga2V5IGFuZCB2YWx1ZS4gKi8KICAgICAgZnVuY3Rpb24gbGlzdFBhaXIoa2V5LCB2YWx1ZSkgewogICAgICAgICAgcmV0dXJuIG5ldyBMaXN0UGFpcldpZGdldChrZXksIHZhbHVlKTsKICAgICAgfQogICAgICBXaWRnZXRzLmxpc3RQYWlyID0gbGlzdFBhaXI7CiAgICAgIC8qKiBDcmVhdGUgYW4gZXh0ZXJuYWwgbGluayB3aWRnZXQgd2hpY2ggcmVuZGVycyBhbiBleHRlcm5hbCBPYnNpZGlhbiBsaW5rLiAqLwogICAgICBmdW5jdGlvbiBleHRlcm5hbExpbmsodXJsLCBkaXNwbGF5KSB7CiAgICAgICAgICByZXR1cm4gbmV3IEV4dGVybmFsTGlua1dpZGdldCh1cmwsIGRpc3BsYXkpOwogICAgICB9CiAgICAgIFdpZGdldHMuZXh0ZXJuYWxMaW5rID0gZXh0ZXJuYWxMaW5rOwogICAgICAvKiogQ2hlY2tzIGlmIHRoZSBnaXZlbiB3aWRnZXQgaXMgYSBsaXN0IHBhaXIgd2lkZ2V0LiAqLwogICAgICBmdW5jdGlvbiBpc0xpc3RQYWlyKHdpZGdldCkgewogICAgICAgICAgcmV0dXJuIHdpZGdldC4kd2lkZ2V0ID09PSAiZGF0YXZpZXc6bGlzdC1wYWlyIjsKICAgICAgfQogICAgICBXaWRnZXRzLmlzTGlzdFBhaXIgPSBpc0xpc3RQYWlyOwogICAgICBmdW5jdGlvbiBpc0V4dGVybmFsTGluayh3aWRnZXQpIHsKICAgICAgICAgIHJldHVybiB3aWRnZXQuJHdpZGdldCA9PT0gImRhdGF2aWV3OmV4dGVybmFsLWxpbmsiOwogICAgICB9CiAgICAgIFdpZGdldHMuaXNFeHRlcm5hbExpbmsgPSBpc0V4dGVybmFsTGluazsKICAgICAgLyoqIERldGVybWluZXMgaWYgdGhlIGdpdmVuIHdpZGdldCBpcyBhbnkga2luZCBvZiBidWlsdC1pbiB3aWRnZXQgd2l0aCBzcGVjaWFsIHJlbmRlcmluZyBoYW5kbGluZy4gKi8KICAgICAgZnVuY3Rpb24gaXNCdWlsdGluKHdpZGdldCkgewogICAgICAgICAgcmV0dXJuIGlzTGlzdFBhaXIod2lkZ2V0KSB8fCBpc0V4dGVybmFsTGluayh3aWRnZXQpOwogICAgICB9CiAgICAgIFdpZGdldHMuaXNCdWlsdGluID0gaXNCdWlsdGluOwogIH0pKFdpZGdldHMgfHwgKFdpZGdldHMgPSB7fSkpOwoKICAvKiogVXRpbGl0eSBtZXRob2RzIGZvciBjcmVhdGluZyAmIGNvbXBhcmluZyBmaWVsZHMuICovCiAgdmFyIEZpZWxkczsKICAoZnVuY3Rpb24gKEZpZWxkcykgewogICAgICBmdW5jdGlvbiB2YXJpYWJsZShuYW1lKSB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAidmFyaWFibGUiLCBuYW1lIH07CiAgICAgIH0KICAgICAgRmllbGRzLnZhcmlhYmxlID0gdmFyaWFibGU7CiAgICAgIGZ1bmN0aW9uIGxpdGVyYWwodmFsdWUpIHsKICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJsaXRlcmFsIiwgdmFsdWUgfTsKICAgICAgfQogICAgICBGaWVsZHMubGl0ZXJhbCA9IGxpdGVyYWw7CiAgICAgIGZ1bmN0aW9uIGJpbmFyeU9wKGxlZnQsIG9wLCByaWdodCkgewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogImJpbmFyeW9wIiwgbGVmdCwgb3AsIHJpZ2h0IH07CiAgICAgIH0KICAgICAgRmllbGRzLmJpbmFyeU9wID0gYmluYXJ5T3A7CiAgICAgIGZ1bmN0aW9uIGluZGV4KG9iaiwgaW5kZXgpIHsKICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJpbmRleCIsIG9iamVjdDogb2JqLCBpbmRleCB9OwogICAgICB9CiAgICAgIEZpZWxkcy5pbmRleCA9IGluZGV4OwogICAgICAvKiogQ29udmVydHMgYSBzdHJpbmcgaW4gZG90LW5vdGF0aW9uLWZvcm1hdCBpbnRvIGEgdmFyaWFibGUgd2hpY2ggaW5kZXhlcy4gKi8KICAgICAgZnVuY3Rpb24gaW5kZXhWYXJpYWJsZShuYW1lKSB7CiAgICAgICAgICBsZXQgcGFydHMgPSBuYW1lLnNwbGl0KCIuIik7CiAgICAgICAgICBsZXQgcmVzdWx0ID0gRmllbGRzLnZhcmlhYmxlKHBhcnRzWzBdKTsKICAgICAgICAgIGZvciAobGV0IGluZGV4ID0gMTsgaW5kZXggPCBwYXJ0cy5sZW5ndGg7IGluZGV4KyspIHsKICAgICAgICAgICAgICByZXN1bHQgPSBGaWVsZHMuaW5kZXgocmVzdWx0LCBGaWVsZHMubGl0ZXJhbChwYXJ0c1tpbmRleF0pKTsKICAgICAgICAgIH0KICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgIH0KICAgICAgRmllbGRzLmluZGV4VmFyaWFibGUgPSBpbmRleFZhcmlhYmxlOwogICAgICBmdW5jdGlvbiBsYW1iZGEoYXJncywgdmFsdWUpIHsKICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJsYW1iZGEiLCBhcmd1bWVudHM6IGFyZ3MsIHZhbHVlIH07CiAgICAgIH0KICAgICAgRmllbGRzLmxhbWJkYSA9IGxhbWJkYTsKICAgICAgZnVuY3Rpb24gZnVuYyhmdW5jLCBhcmdzKSB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAiZnVuY3Rpb24iLCBmdW5jLCBhcmd1bWVudHM6IGFyZ3MgfTsKICAgICAgfQogICAgICBGaWVsZHMuZnVuYyA9IGZ1bmM7CiAgICAgIGZ1bmN0aW9uIGxpc3QodmFsdWVzKSB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAibGlzdCIsIHZhbHVlcyB9OwogICAgICB9CiAgICAgIEZpZWxkcy5saXN0ID0gbGlzdDsKICAgICAgZnVuY3Rpb24gb2JqZWN0KHZhbHVlcykgewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogIm9iamVjdCIsIHZhbHVlcyB9OwogICAgICB9CiAgICAgIEZpZWxkcy5vYmplY3QgPSBvYmplY3Q7CiAgICAgIGZ1bmN0aW9uIG5lZ2F0ZShjaGlsZCkgewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogIm5lZ2F0ZWQiLCBjaGlsZCB9OwogICAgICB9CiAgICAgIEZpZWxkcy5uZWdhdGUgPSBuZWdhdGU7CiAgICAgIGZ1bmN0aW9uIGlzQ29tcGFyZU9wKG9wKSB7CiAgICAgICAgICByZXR1cm4gb3AgPT0gIjw9IiB8fCBvcCA9PSAiPCIgfHwgb3AgPT0gIj4iIHx8IG9wID09ICI+PSIgfHwgb3AgPT0gIiE9IiB8fCBvcCA9PSAiPSI7CiAgICAgIH0KICAgICAgRmllbGRzLmlzQ29tcGFyZU9wID0gaXNDb21wYXJlT3A7CiAgICAgIEZpZWxkcy5OVUxMID0gRmllbGRzLmxpdGVyYWwobnVsbCk7CiAgfSkoRmllbGRzIHx8IChGaWVsZHMgPSB7fSkpOwoKICAvKiogQVNUIGltcGxlbWVudGF0aW9uIGZvciBxdWVyaWVzIG92ZXIgZGF0YSBzb3VyY2VzLiAqLwogIC8qKiBVdGlsaXR5IGZ1bmN0aW9ucyBmb3IgY3JlYXRpbmcgYW5kIG1hbmlwdWxhdGluZyBzb3VyY2VzLiAqLwogIHZhciBTb3VyY2VzOwogIChmdW5jdGlvbiAoU291cmNlcykgewogICAgICAvKiogQ3JlYXRlIGEgc291cmNlIHdoaWNoIHNlYXJjaGVzIGZyb20gYSB0YWcuICovCiAgICAgIGZ1bmN0aW9uIHRhZyh0YWcpIHsKICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJ0YWciLCB0YWcgfTsKICAgICAgfQogICAgICBTb3VyY2VzLnRhZyA9IHRhZzsKICAgICAgLyoqIENyZWF0ZSBhIHNvdXJjZSB3aGljaCBmZXRjaGVzIGZyb20gYSBDU1YgZmlsZS4gKi8KICAgICAgZnVuY3Rpb24gY3N2KHBhdGgpIHsKICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJjc3YiLCBwYXRoIH07CiAgICAgIH0KICAgICAgU291cmNlcy5jc3YgPSBjc3Y7CiAgICAgIC8qKiBDcmVhdGUgYSBzb3VyY2Ugd2hpY2ggc2VhcmNoZXMgZm9yIGZpbGVzIHVuZGVyIGEgZm9sZGVyIHByZWZpeC4gKi8KICAgICAgZnVuY3Rpb24gZm9sZGVyKHByZWZpeCkgewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogImZvbGRlciIsIGZvbGRlcjogcHJlZml4IH07CiAgICAgIH0KICAgICAgU291cmNlcy5mb2xkZXIgPSBmb2xkZXI7CiAgICAgIC8qKiBDcmVhdGUgYSBzb3VyY2Ugd2hpY2ggc2VhcmNoZXMgZm9yIGZpbGVzIHdoaWNoIGxpbmsgdG8vZnJvbSBhIGdpdmVuIGZpbGUuICovCiAgICAgIGZ1bmN0aW9uIGxpbmsoZmlsZSwgaW5jb21pbmcpIHsKICAgICAgICAgIHJldHVybiB7IHR5cGU6ICJsaW5rIiwgZmlsZSwgZGlyZWN0aW9uOiBpbmNvbWluZyA/ICJpbmNvbWluZyIgOiAib3V0Z29pbmciIH07CiAgICAgIH0KICAgICAgU291cmNlcy5saW5rID0gbGluazsKICAgICAgLyoqIENyZWF0ZSBhIHNvdXJjZSB3aGljaCBqb2lucyB0d28gc291cmNlcyBieSBhIGxvZ2ljYWwgb3BlcmF0b3IgKGFuZC9vcikuICovCiAgICAgIGZ1bmN0aW9uIGJpbmFyeU9wKGxlZnQsIG9wLCByaWdodCkgewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogImJpbmFyeW9wIiwgbGVmdCwgb3AsIHJpZ2h0IH07CiAgICAgIH0KICAgICAgU291cmNlcy5iaW5hcnlPcCA9IGJpbmFyeU9wOwogICAgICAvKiogQ3JlYXRlIGEgc291cmNlIHdoaWNoIHRha2VzIHRoZSBpbnRlcnNlY3Rpb24gb2YgdHdvIHNvdXJjZXMuICovCiAgICAgIGZ1bmN0aW9uIGFuZChsZWZ0LCByaWdodCkgewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogImJpbmFyeW9wIiwgbGVmdCwgb3A6ICImIiwgcmlnaHQgfTsKICAgICAgfQogICAgICBTb3VyY2VzLmFuZCA9IGFuZDsKICAgICAgLyoqIENyZWF0ZSBhIHNvdXJjZSB3aGljaCB0YWtlcyB0aGUgdW5pb24gb2YgdHdvIHNvdXJjZXMuICovCiAgICAgIGZ1bmN0aW9uIG9yKGxlZnQsIHJpZ2h0KSB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAiYmluYXJ5b3AiLCBsZWZ0LCBvcDogInwiLCByaWdodCB9OwogICAgICB9CiAgICAgIFNvdXJjZXMub3IgPSBvcjsKICAgICAgLyoqIENyZWF0ZSBhIHNvdXJjZSB3aGljaCBuZWdhdGVzIHRoZSB1bmRlcmx5aW5nIHNvdXJjZS4gKi8KICAgICAgZnVuY3Rpb24gbmVnYXRlKGNoaWxkKSB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAibmVnYXRlIiwgY2hpbGQgfTsKICAgICAgfQogICAgICBTb3VyY2VzLm5lZ2F0ZSA9IG5lZ2F0ZTsKICAgICAgZnVuY3Rpb24gZW1wdHkoKSB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAiZW1wdHkiIH07CiAgICAgIH0KICAgICAgU291cmNlcy5lbXB0eSA9IGVtcHR5OwogIH0pKFNvdXJjZXMgfHwgKFNvdXJjZXMgPSB7fSkpOwoKICAvKiogRW1vamkgcmVnZXggd2l0aG91dCBhbnkgYWRkaXRpb25hbCBmbGFncy4gKi8KICBjb25zdCBFTU9KSV9SRUdFWCA9IG5ldyBSZWdFeHAoZW1vamlSZWdleCgpLCAiIik7CiAgLyoqIFByb3ZpZGVzIGEgbG9va3VwIHRhYmxlIGZvciB1bml0IGR1cmF0aW9ucyBvZiB0aGUgZ2l2ZW4gdHlwZS4gKi8KICBjb25zdCBEVVJBVElPTl9UWVBFUyA9IHsKICAgICAgeWVhcjogRHVyYXRpb24uZnJvbU9iamVjdCh7IHllYXJzOiAxIH0pLAogICAgICB5ZWFyczogRHVyYXRpb24uZnJvbU9iamVjdCh7IHllYXJzOiAxIH0pLAogICAgICB5cjogRHVyYXRpb24uZnJvbU9iamVjdCh7IHllYXJzOiAxIH0pLAogICAgICB5cnM6IER1cmF0aW9uLmZyb21PYmplY3QoeyB5ZWFyczogMSB9KSwKICAgICAgbW9udGg6IER1cmF0aW9uLmZyb21PYmplY3QoeyBtb250aHM6IDEgfSksCiAgICAgIG1vbnRoczogRHVyYXRpb24uZnJvbU9iamVjdCh7IG1vbnRoczogMSB9KSwKICAgICAgbW86IER1cmF0aW9uLmZyb21PYmplY3QoeyBtb250aHM6IDEgfSksCiAgICAgIG1vczogRHVyYXRpb24uZnJvbU9iamVjdCh7IG1vbnRoczogMSB9KSwKICAgICAgd2VlazogRHVyYXRpb24uZnJvbU9iamVjdCh7IHdlZWtzOiAxIH0pLAogICAgICB3ZWVrczogRHVyYXRpb24uZnJvbU9iamVjdCh7IHdlZWtzOiAxIH0pLAogICAgICB3azogRHVyYXRpb24uZnJvbU9iamVjdCh7IHdlZWtzOiAxIH0pLAogICAgICB3a3M6IER1cmF0aW9uLmZyb21PYmplY3QoeyB3ZWVrczogMSB9KSwKICAgICAgdzogRHVyYXRpb24uZnJvbU9iamVjdCh7IHdlZWtzOiAxIH0pLAogICAgICBkYXk6IER1cmF0aW9uLmZyb21PYmplY3QoeyBkYXlzOiAxIH0pLAogICAgICBkYXlzOiBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgZGF5czogMSB9KSwKICAgICAgZDogRHVyYXRpb24uZnJvbU9iamVjdCh7IGRheXM6IDEgfSksCiAgICAgIGhvdXI6IER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMSB9KSwKICAgICAgaG91cnM6IER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMSB9KSwKICAgICAgaHI6IER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMSB9KSwKICAgICAgaHJzOiBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgaG91cnM6IDEgfSksCiAgICAgIGg6IER1cmF0aW9uLmZyb21PYmplY3QoeyBob3VyczogMSB9KSwKICAgICAgbWludXRlOiBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgbWludXRlczogMSB9KSwKICAgICAgbWludXRlczogRHVyYXRpb24uZnJvbU9iamVjdCh7IG1pbnV0ZXM6IDEgfSksCiAgICAgIG1pbjogRHVyYXRpb24uZnJvbU9iamVjdCh7IG1pbnV0ZXM6IDEgfSksCiAgICAgIG1pbnM6IER1cmF0aW9uLmZyb21PYmplY3QoeyBtaW51dGVzOiAxIH0pLAogICAgICBtOiBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgbWludXRlczogMSB9KSwKICAgICAgc2Vjb25kOiBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgc2Vjb25kczogMSB9KSwKICAgICAgc2Vjb25kczogRHVyYXRpb24uZnJvbU9iamVjdCh7IHNlY29uZHM6IDEgfSksCiAgICAgIHNlYzogRHVyYXRpb24uZnJvbU9iamVjdCh7IHNlY29uZHM6IDEgfSksCiAgICAgIHNlY3M6IER1cmF0aW9uLmZyb21PYmplY3QoeyBzZWNvbmRzOiAxIH0pLAogICAgICBzOiBEdXJhdGlvbi5mcm9tT2JqZWN0KHsgc2Vjb25kczogMSB9KSwKICB9OwogIC8qKiBTaG9ydGhhbmQgZm9yIGNvbW1vbiBkYXRlcyAocmVsYXRpdmUgdG8gcmlnaHQgbm93KS4gKi8KICBjb25zdCBEQVRFX1NIT1JUSEFORFMgPSB7CiAgICAgIG5vdzogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKSwKICAgICAgdG9kYXk6ICgpID0+IERhdGVUaW1lLmxvY2FsKCkuc3RhcnRPZigiZGF5IiksCiAgICAgIHllc3RlcmRheTogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKQogICAgICAgICAgLnN0YXJ0T2YoImRheSIpCiAgICAgICAgICAubWludXMoRHVyYXRpb24uZnJvbU9iamVjdCh7IGRheXM6IDEgfSkpLAogICAgICB0b21vcnJvdzogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKQogICAgICAgICAgLnN0YXJ0T2YoImRheSIpCiAgICAgICAgICAucGx1cyhEdXJhdGlvbi5mcm9tT2JqZWN0KHsgZGF5czogMSB9KSksCiAgICAgIHNvdzogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5zdGFydE9mKCJ3ZWVrIiksCiAgICAgICJzdGFydC1vZi13ZWVrIjogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5zdGFydE9mKCJ3ZWVrIiksCiAgICAgIGVvdzogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5lbmRPZigid2VlayIpLAogICAgICAiZW5kLW9mLXdlZWsiOiAoKSA9PiBEYXRlVGltZS5sb2NhbCgpLmVuZE9mKCJ3ZWVrIiksCiAgICAgIHNveTogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5zdGFydE9mKCJ5ZWFyIiksCiAgICAgICJzdGFydC1vZi15ZWFyIjogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5zdGFydE9mKCJ5ZWFyIiksCiAgICAgIGVveTogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5lbmRPZigieWVhciIpLAogICAgICAiZW5kLW9mLXllYXIiOiAoKSA9PiBEYXRlVGltZS5sb2NhbCgpLmVuZE9mKCJ5ZWFyIiksCiAgICAgIHNvbTogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5zdGFydE9mKCJtb250aCIpLAogICAgICAic3RhcnQtb2YtbW9udGgiOiAoKSA9PiBEYXRlVGltZS5sb2NhbCgpLnN0YXJ0T2YoIm1vbnRoIiksCiAgICAgIGVvbTogKCkgPT4gRGF0ZVRpbWUubG9jYWwoKS5lbmRPZigibW9udGgiKSwKICAgICAgImVuZC1vZi1tb250aCI6ICgpID0+IERhdGVUaW1lLmxvY2FsKCkuZW5kT2YoIm1vbnRoIiksCiAgfTsKICAvKioKICAgKiBLZXl3b3JkcyB3aGljaCBjYW5ub3QgYmUgdXNlZCBhcyB2YXJpYWJsZXMgZGlyZWN0bHkuIFVzZSBgcm93Ljx0aGluZz5gIGlmIGl0IGlzIGEgdmFyaWFibGUgeW91IGhhdmUgZGVmaW5lZCBhbmQgd2FudAogICAqIHRvIGFjY2Vzcy4KICAgKi8KICBjb25zdCBLRVlXT1JEUyA9IFsiRlJPTSIsICJXSEVSRSIsICJMSU1JVCIsICJHUk9VUCIsICJGTEFUVEVOIl07CiAgLy8vLy8vLy8vLy8vLy8vCiAgLy8gVXRpbGl0aWVzIC8vCiAgLy8vLy8vLy8vLy8vLy8vCiAgLyoqIFNwbGl0IG9uIHVuZXNjYXBlZCBwaXBlcyBpbiBhbiBpbm5lciBsaW5rLiAqLwogIGZ1bmN0aW9uIHNwbGl0T25VbmVzY2FwZWRQaXBlKGxpbmspIHsKICAgICAgbGV0IHBpcGUgPSAtMTsKICAgICAgd2hpbGUgKChwaXBlID0gbGluay5pbmRleE9mKCJ8IiwgcGlwZSArIDEpKSA+PSAwKSB7CiAgICAgICAgICBpZiAocGlwZSA+IDAgJiYgbGlua1twaXBlIC0gMV0gPT0gIlxcIikKICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgIHJldHVybiBbbGluay5zdWJzdHJpbmcoMCwgcGlwZSkucmVwbGFjZSgvXFxcfC9nLCAifCIpLCBsaW5rLnN1YnN0cmluZyhwaXBlICsgMSldOwogICAgICB9CiAgICAgIHJldHVybiBbbGluay5yZXBsYWNlKC9cXFx8L2csICJ8IiksIHVuZGVmaW5lZF07CiAgfQogIC8qKiBBdHRlbXB0IHRvIHBhcnNlIHRoZSBpbnNpZGUgb2YgYSBsaW5rIHRvIHB1bGwgb3V0IGRpc3BsYXkgbmFtZSwgc3VicGF0aCwgZXRjLiAqLwogIGZ1bmN0aW9uIHBhcnNlSW5uZXJMaW5rKHJhd2xpbmspIHsKICAgICAgbGV0IFtsaW5rLCBkaXNwbGF5XSA9IHNwbGl0T25VbmVzY2FwZWRQaXBlKHJhd2xpbmspOwogICAgICByZXR1cm4gTGluay5pbmZlcihsaW5rLCBmYWxzZSwgZGlzcGxheSk7CiAgfQogIC8qKiBDcmVhdGUgYSBsZWZ0LWFzc29jaWF0aXZlIGJpbmFyeSBwYXJzZXIgd2hpY2ggcGFyc2VzIHRoZSBnaXZlbiBzdWItZWxlbWVudCBhbmQgc2VwYXJhdG9yLiBIYW5kbGVzIHdoaXRlc3BhY2UuICovCiAgZnVuY3Rpb24gY3JlYXRlQmluYXJ5UGFyc2VyKGNoaWxkLCBzZXAsIGNvbWJpbmUpIHsKICAgICAgcmV0dXJuIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAoY2hpbGQsIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXEocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UsIHNlcCwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UsIGNoaWxkKS5tYW55KCksIChmaXJzdCwgcmVzdCkgPT4gewogICAgICAgICAgaWYgKHJlc3QubGVuZ3RoID09IDApCiAgICAgICAgICAgICAgcmV0dXJuIGZpcnN0OwogICAgICAgICAgbGV0IG5vZGUgPSBjb21iaW5lKGZpcnN0LCByZXN0WzBdWzFdLCByZXN0WzBdWzNdKTsKICAgICAgICAgIGZvciAobGV0IGluZGV4ID0gMTsgaW5kZXggPCByZXN0Lmxlbmd0aDsgaW5kZXgrKykgewogICAgICAgICAgICAgIG5vZGUgPSBjb21iaW5lKG5vZGUsIHJlc3RbaW5kZXhdWzFdLCByZXN0W2luZGV4XVszXSk7CiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4gbm9kZTsKICAgICAgfSk7CiAgfQogIGZ1bmN0aW9uIGNoYWluT3B0KGJhc2UsIC4uLmZ1bmNzKSB7CiAgICAgIHJldHVybiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuY3VzdG9tKChzdWNjZXNzLCBmYWlsdXJlKSA9PiB7CiAgICAgICAgICByZXR1cm4gKGlucHV0LCBpKSA9PiB7CiAgICAgICAgICAgICAgbGV0IHJlc3VsdCA9IGJhc2UuXyhpbnB1dCwgaSk7CiAgICAgICAgICAgICAgaWYgKCFyZXN1bHQuc3RhdHVzKQogICAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgICAgICAgIGZvciAobGV0IGZ1bmMgb2YgZnVuY3MpIHsKICAgICAgICAgICAgICAgICAgbGV0IG5leHQgPSBmdW5jKHJlc3VsdC52YWx1ZSkuXyhpbnB1dCwgcmVzdWx0LmluZGV4KTsKICAgICAgICAgICAgICAgICAgaWYgKCFuZXh0LnN0YXR1cykKICAgICAgICAgICAgICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IG5leHQ7CiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgICAgICB9OwogICAgICB9KTsKICB9CiAgY29uc3QgRVhQUkVTU0lPTiA9IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5jcmVhdGVMYW5ndWFnZSh7CiAgICAgIC8vIEEgZmxvYXRpbmcgcG9pbnQgbnVtYmVyOyB0aGUgZGVjaW1hbCBwb2ludCBpcyBvcHRpb25hbC4KICAgICAgbnVtYmVyOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoLy0/WzAtOV0rKFwuWzAtOV0rKT8vKQogICAgICAgICAgLm1hcChzdHIgPT4gTnVtYmVyLnBhcnNlRmxvYXQoc3RyKSkKICAgICAgICAgIC5kZXNjKCJudW1iZXIiKSwKICAgICAgLy8gQSBxdW90ZS1zdXJyb3VuZGVkIHN0cmluZyB3aGljaCBzdXBwb3J0cyBlc2NhcGUgY2hhcmFjdGVycyAoJ1wnKS4KICAgICAgc3RyaW5nOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoJyInKQogICAgICAgICAgLnRoZW4ocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdChxLmVzY2FwZUNoYXJhY3RlciwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm5vbmVPZignIlxcJykpCiAgICAgICAgICAuYXRMZWFzdCgwKQogICAgICAgICAgLm1hcChjaGFycyA9PiBjaGFycy5qb2luKCIiKSkpCiAgICAgICAgICAuc2tpcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCciJykpCiAgICAgICAgICAuZGVzYygic3RyaW5nIiksCiAgICAgIGVzY2FwZUNoYXJhY3RlcjogXyA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCJcXCIpCiAgICAgICAgICAudGhlbihwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuYW55KQogICAgICAgICAgLm1hcChlc2NhcGVkID0+IHsKICAgICAgICAgIC8vIElmIHdlIGFyZSBlc2NhcGluZyBhIGJhY2tzbGFzaCBvciBhIHF1b3RlLCBwYXNzIGluIG9uIGluIGVzY2FwZWQgZm9ybQogICAgICAgICAgaWYgKGVzY2FwZWQgPT09ICciJykKICAgICAgICAgICAgICByZXR1cm4gJyInOwogICAgICAgICAgaWYgKGVzY2FwZWQgPT09ICJcXCIpCiAgICAgICAgICAgICAgcmV0dXJuICJcXCI7CiAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgcmV0dXJuICJcXCIgKyBlc2NhcGVkOwogICAgICB9KSwKICAgICAgLy8gQSBib29sZWFuIHRydWUvZmFsc2UgdmFsdWUuCiAgICAgIGJvb2w6IF8gPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvdHJ1ZXxmYWxzZXxUcnVlfEZhbHNlLykKICAgICAgICAgIC5tYXAoc3RyID0+IHN0ci50b0xvd2VyQ2FzZSgpID09ICJ0cnVlIikKICAgICAgICAgIC5kZXNjKCJib29sZWFuICgndHJ1ZScgb3IgJ2ZhbHNlJykiKSwKICAgICAgLy8gQSB0YWcgb2YgdGhlIGZvcm0gJyNzdHVmZi9oZWxsby10aGVyZScuCiAgICAgIHRhZzogXyA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiMiKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC9bXlx1MjAwMC1cdTIwNkZcdTJFMDAtXHUyRTdGJyEiIyQlJigpKissLjo7PD0+P0BeYHt8fX5cW1xdXFxcc10vKS5kZXNjKCJ0ZXh0IikpLm1hbnkoKSwgKHN0YXJ0LCByZXN0KSA9PiBzdGFydCArIHJlc3Quam9pbigiIikpLmRlc2MoInRhZyAoJyNoZWxsby9zdHVmZicpIiksCiAgICAgIC8vIEEgdmFyaWFibGUgaWRlbnRpZmllciwgd2hpY2ggaXMgYWxwaGFudW1lcmljIGFuZCBtdXN0IHN0YXJ0IHdpdGggYSBsZXR0ZXIgb3IuLi4gZW1vamkuCiAgICAgIGlkZW50aWZpZXI6IF8gPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnNlcU1hcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuYWx0KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoL1xwe0xldHRlcn0vdSksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoRU1PSklfUkVHRVgpLmRlc2MoInRleHQiKSksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvWzAtOVxwe0xldHRlcn1fLV0vdSksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoRU1PSklfUkVHRVgpLmRlc2MoInRleHQiKSkubWFueSgpLCAoZmlyc3QsIHJlc3QpID0+IGZpcnN0ICsgcmVzdC5qb2luKCIiKSkuZGVzYygidmFyaWFibGUgaWRlbnRpZmllciIpLAogICAgICAvLyBBbiBPYnNpZGlhbiBsaW5rIG9mIHRoZSBmb3JtIFtbPGxpbms+XV0uCiAgICAgIGxpbms6IF8gPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvXFtcWyhbXlxbXF1dKj8pXF1cXS91LCAxKQogICAgICAgICAgLm1hcChsaW5rSW5uZXIgPT4gcGFyc2VJbm5lckxpbmsobGlua0lubmVyKSkKICAgICAgICAgIC5kZXNjKCJmaWxlIGxpbmsiKSwKICAgICAgLy8gQW4gZW1iZWRkYWJsZSBsaW5rIHdoaWNoIGNhbiBzdGFydCB3aXRoICchJy4gVGhpcyBvdmVybGFwcyB3aXRoIHRoZSBub3JtYWwgbmVnYXRpb24gb3BlcmF0b3IsIHNvIGl0IGlzIG9ubHkKICAgICAgLy8gcHJvdmlkZWQgZm9yIG1ldGFkYXRhIHBhcnNpbmcuCiAgICAgIGVtYmVkTGluazogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiEiKS5hdE1vc3QoMSksIHEubGluaywgKHAsIGwpID0+IHsKICAgICAgICAgIGlmIChwLmxlbmd0aCA+IDApCiAgICAgICAgICAgICAgbC5lbWJlZCA9IHRydWU7CiAgICAgICAgICByZXR1cm4gbDsKICAgICAgfSkuZGVzYygiZmlsZSBsaW5rIiksCiAgICAgIC8vIEJpbmFyeSBwbHVzIG9yIG1pbnVzIG9wZXJhdG9yLgogICAgICBiaW5hcnlQbHVzTWludXM6IF8gPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvXCt8LS8pCiAgICAgICAgICAubWFwKHN0ciA9PiBzdHIpCiAgICAgICAgICAuZGVzYygiJysnIG9yICctJyIpLAogICAgICAvLyBCaW5hcnkgdGltZXMgb3IgZGl2aWRlIG9wZXJhdG9yLgogICAgICBiaW5hcnlNdWxEaXY6IF8gPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvXCp8XC98JS8pCiAgICAgICAgICAubWFwKHN0ciA9PiBzdHIpCiAgICAgICAgICAuZGVzYygiJyonIG9yICcvJyBvciAnJSciKSwKICAgICAgLy8gQmluYXJ5IGNvbXBhcmlzb24gb3BlcmF0b3IuCiAgICAgIGJpbmFyeUNvbXBhcmVPcDogXyA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC8+PXw8PXwhPXw+fDx8PS8pCiAgICAgICAgICAubWFwKHN0ciA9PiBzdHIpCiAgICAgICAgICAuZGVzYygiJz49JyBvciAnPD0nIG9yICchPScgb3IgJz0nIG9yICc+JyBvciAnPCciKSwKICAgICAgLy8gQmluYXJ5IGJvb2xlYW4gY29tYmluYXRpb24gb3BlcmF0b3IuCiAgICAgIGJpbmFyeUJvb2xlYW5PcDogXyA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC9hbmR8b3J8JnxcfC9pKQogICAgICAgICAgLm1hcChzdHIgPT4gewogICAgICAgICAgaWYgKHN0ci50b0xvd2VyQ2FzZSgpID09ICJhbmQiKQogICAgICAgICAgICAgIHJldHVybiAiJiI7CiAgICAgICAgICBlbHNlIGlmIChzdHIudG9Mb3dlckNhc2UoKSA9PSAib3IiKQogICAgICAgICAgICAgIHJldHVybiAifCI7CiAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgcmV0dXJuIHN0cjsKICAgICAgfSkKICAgICAgICAgIC5kZXNjKCInYW5kJyBvciAnb3InIiksCiAgICAgIC8vIEEgZGF0ZSB3aGljaCBjYW4gYmUgWVlZWS1NTVstRERUSEg6bW06c3NdLgogICAgICByb290RGF0ZTogXyA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoL1xkezR9LyksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIi0iKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvXGR7Mn0vKSwgKHllYXIsIF8sIG1vbnRoKSA9PiB7CiAgICAgICAgICByZXR1cm4gRGF0ZVRpbWUuZnJvbU9iamVjdCh7IHllYXI6IE51bWJlci5wYXJzZUludCh5ZWFyKSwgbW9udGg6IE51bWJlci5wYXJzZUludChtb250aCkgfSk7CiAgICAgIH0pLmRlc2MoImRhdGUgaW4gZm9ybWF0IFlZWVktTU1bLUREVEhILU1NLVNTLk1TXSIpLAogICAgICBkYXRlU2hvcnRoYW5kOiBfID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQoLi4uT2JqZWN0LmtleXMoREFURV9TSE9SVEhBTkRTKQogICAgICAgICAgLnNvcnQoKGEsIGIpID0+IGIubGVuZ3RoIC0gYS5sZW5ndGgpCiAgICAgICAgICAubWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcpKSwKICAgICAgZGF0ZTogcSA9PiBjaGFpbk9wdChxLnJvb3REYXRlLCAoeW0pID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiLSIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC9cZHsyfS8pLCAoXywgZGF5KSA9PiB5bS5zZXQoeyBkYXk6IE51bWJlci5wYXJzZUludChkYXkpIH0pKSwgKHltZCkgPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnNlcU1hcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCJUIiksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoL1xkezJ9LyksIChfLCBob3VyKSA9PiB5bWQuc2V0KHsgaG91cjogTnVtYmVyLnBhcnNlSW50KGhvdXIpIH0pKSwgKHltZGgpID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiOiIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC9cZHsyfS8pLCAoXywgbWludXRlKSA9PiB5bWRoLnNldCh7IG1pbnV0ZTogTnVtYmVyLnBhcnNlSW50KG1pbnV0ZSkgfSkpLCAoeW1kaG0pID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiOiIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC9cZHsyfS8pLCAoXywgc2Vjb25kKSA9PiB5bWRobS5zZXQoeyBzZWNvbmQ6IE51bWJlci5wYXJzZUludChzZWNvbmQpIH0pKSwgKHltZGhtcykgPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIi4iKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvXGR7M30vKSwgKF8sIG1pbGxpc2Vjb25kKSA9PiB5bWRobXMuc2V0KHsgbWlsbGlzZWNvbmQ6IE51bWJlci5wYXJzZUludChtaWxsaXNlY29uZCkgfSkpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3VjY2VlZCh5bWRobXMpIC8vIHBhc3MKICAgICAgKSwgKGR0KSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuYWx0KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKyIpLm9yKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIi0iKSksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5yZWdleHAoL1xkezEsMn0oOlxkezJ9KT8vKSwgKHBtLCBocikgPT4gZHQuc2V0Wm9uZSgiVVRDIiArIHBtICsgaHIsIHsga2VlcExvY2FsVGltZTogdHJ1ZSB9KSksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiWiIpLCAoKSA9PiBkdC5zZXRab25lKCJ1dGMiLCB7IGtlZXBMb2NhbFRpbWU6IHRydWUgfSkpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIlsiKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvWzAtOUEtWmEteistXC9dKy91KSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiXSIpLCAoX2EsIHpvbmUsIF9iKSA9PiBkdC5zZXRab25lKHpvbmUsIHsga2VlcExvY2FsVGltZTogdHJ1ZSB9KSkpKQogICAgICAgICAgLmFzc2VydCgoZHQpID0+IGR0LmlzVmFsaWQsICJ2YWxpZCBkYXRlIikKICAgICAgICAgIC5kZXNjKCJkYXRlIGluIGZvcm1hdCBZWVlZLU1NWy1ERFRISC1NTS1TUy5NU10iKSwKICAgICAgLy8gQSBkYXRlLCBwbHVzIHZhcmlvdXMgc2hvcnRoYW5kIHRpbWVzIG9mIGRheSBpdCBjb3VsZCBiZS4KICAgICAgZGF0ZVBsdXM6IHEgPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdChxLmRhdGVTaG9ydGhhbmQubWFwKGQgPT4gREFURV9TSE9SVEhBTkRTW2RdKCkpLCBxLmRhdGUpLmRlc2MoImRhdGUgaW4gZm9ybWF0IFlZWVktTU1bLUREVEhILU1NLVNTLk1TXSBvciBpbiBzaG9ydGhhbmQiKSwKICAgICAgLy8gQSBkdXJhdGlvbiBvZiB0aW1lLgogICAgICBkdXJhdGlvblR5cGU6IF8gPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdCguLi5PYmplY3Qua2V5cyhEVVJBVElPTl9UWVBFUykKICAgICAgICAgIC5zb3J0KChhLCBiKSA9PiBiLmxlbmd0aCAtIGEubGVuZ3RoKQogICAgICAgICAgLm1hcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKSksCiAgICAgIGR1cmF0aW9uOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocS5udW1iZXIsIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlLCBxLmR1cmF0aW9uVHlwZSwgKGNvdW50LCBfLCB0KSA9PiBEVVJBVElPTl9UWVBFU1t0XS5tYXBVbml0cyh4ID0+IHggKiBjb3VudCkpCiAgICAgICAgICAuc2VwQnkxKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiwiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKS5vcihwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSkpCiAgICAgICAgICAubWFwKGR1cmF0aW9ucyA9PiBkdXJhdGlvbnMucmVkdWNlKChwLCBjKSA9PiBwLnBsdXMoYykpKQogICAgICAgICAgLmRlc2MoImR1cmF0aW9uIGxpa2UgNGhyMm1pbiIpLAogICAgICAvLyBBIHJhdyBudWxsIHZhbHVlLgogICAgICByYXdOdWxsOiBfID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIm51bGwiKSwKICAgICAgLy8gU291cmNlIHBhcnNpbmcuCiAgICAgIHRhZ1NvdXJjZTogcSA9PiBxLnRhZy5tYXAodGFnID0+IFNvdXJjZXMudGFnKHRhZykpLAogICAgICBjc3ZTb3VyY2U6IHEgPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnNlcU1hcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCJjc3YoIikuc2tpcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSksIHEuc3RyaW5nLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCIpIiksIChfMSwgcGF0aCwgXzIpID0+IFNvdXJjZXMuY3N2KHBhdGgpKSwKICAgICAgbGlua0luY29taW5nU291cmNlOiBxID0+IHEubGluay5tYXAobGluayA9PiBTb3VyY2VzLmxpbmsobGluay5wYXRoLCB0cnVlKSksCiAgICAgIGxpbmtPdXRnb2luZ1NvdXJjZTogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIm91dGdvaW5nKCIpLnNraXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UpLCBxLmxpbmssIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIikiKSwgKF8xLCBsaW5rLCBfMikgPT4gU291cmNlcy5saW5rKGxpbmsucGF0aCwgZmFsc2UpKSwKICAgICAgZm9sZGVyU291cmNlOiBxID0+IHEuc3RyaW5nLm1hcChzdHIgPT4gU291cmNlcy5mb2xkZXIoc3RyKSksCiAgICAgIHBhcmVuc1NvdXJjZTogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIigiKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UsIHEuc291cmNlLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKSIpLCAoXzEsIF8yLCBmaWVsZCwgXzMsIF80KSA9PiBmaWVsZCksCiAgICAgIG5lZ2F0ZVNvdXJjZTogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiLSIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCIhIikpLCBxLmF0b21Tb3VyY2UsIChfLCBzb3VyY2UpID0+IFNvdXJjZXMubmVnYXRlKHNvdXJjZSkpLAogICAgICBhdG9tU291cmNlOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQocS5wYXJlbnNTb3VyY2UsIHEubmVnYXRlU291cmNlLCBxLmxpbmtPdXRnb2luZ1NvdXJjZSwgcS5saW5rSW5jb21pbmdTb3VyY2UsIHEuZm9sZGVyU291cmNlLCBxLnRhZ1NvdXJjZSwgcS5jc3ZTb3VyY2UpLAogICAgICBiaW5hcnlPcFNvdXJjZTogcSA9PiBjcmVhdGVCaW5hcnlQYXJzZXIocS5hdG9tU291cmNlLCBxLmJpbmFyeUJvb2xlYW5PcC5tYXAocyA9PiBzKSwgU291cmNlcy5iaW5hcnlPcCksCiAgICAgIHNvdXJjZTogcSA9PiBxLmJpbmFyeU9wU291cmNlLAogICAgICAvLyBGaWVsZCBwYXJzaW5nLgogICAgICB2YXJpYWJsZUZpZWxkOiBxID0+IHEuaWRlbnRpZmllcgogICAgICAgICAgLmNoYWluKHIgPT4gewogICAgICAgICAgaWYgKEtFWVdPUkRTLmluY2x1ZGVzKHIudG9VcHBlckNhc2UoKSkpIHsKICAgICAgICAgICAgICByZXR1cm4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmZhaWwoIlZhcmlhYmxlIGZpZWxkcyBjYW5ub3QgYmUgYSBrZXl3b3JkICgiICsgS0VZV09SRFMuam9pbigiIG9yICIpICsgIikiKTsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgIHJldHVybiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3VjY2VlZChGaWVsZHMudmFyaWFibGUocikpOwogICAgICAgICAgfQogICAgICB9KQogICAgICAgICAgLmRlc2MoInZhcmlhYmxlIiksCiAgICAgIG51bWJlckZpZWxkOiBxID0+IHEubnVtYmVyLm1hcCh2YWwgPT4gRmllbGRzLmxpdGVyYWwodmFsKSkuZGVzYygibnVtYmVyIiksCiAgICAgIHN0cmluZ0ZpZWxkOiBxID0+IHEuc3RyaW5nLm1hcCh2YWwgPT4gRmllbGRzLmxpdGVyYWwodmFsKSkuZGVzYygic3RyaW5nIiksCiAgICAgIGJvb2xGaWVsZDogcSA9PiBxLmJvb2wubWFwKHZhbCA9PiBGaWVsZHMubGl0ZXJhbCh2YWwpKS5kZXNjKCJib29sZWFuIiksCiAgICAgIGRhdGVGaWVsZDogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoImRhdGUoIiksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlLCBxLmRhdGVQbHVzLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKSIpLCAocHJlZml4LCBfMSwgZGF0ZSwgXzIsIHBvc3RmaXgpID0+IEZpZWxkcy5saXRlcmFsKGRhdGUpKS5kZXNjKCJkYXRlIiksCiAgICAgIGR1cmF0aW9uRmllbGQ6IHEgPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnNlcU1hcChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCJkdXIoIiksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlLCBxLmR1cmF0aW9uLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKSIpLCAocHJlZml4LCBfMSwgZHVyLCBfMiwgcG9zdGZpeCkgPT4gRmllbGRzLmxpdGVyYWwoZHVyKSkuZGVzYygiZHVyYXRpb24iKSwKICAgICAgbnVsbEZpZWxkOiBxID0+IHEucmF3TnVsbC5tYXAoXyA9PiBGaWVsZHMuTlVMTCksCiAgICAgIGxpbmtGaWVsZDogcSA9PiBxLmxpbmsubWFwKGYgPT4gRmllbGRzLmxpdGVyYWwoZikpLAogICAgICBsaXN0RmllbGQ6IHEgPT4gcS5maWVsZAogICAgICAgICAgLnNlcEJ5KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiwiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSkKICAgICAgICAgIC53cmFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIlsiKS5za2lwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UudGhlbihwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCJdIikpKQogICAgICAgICAgLm1hcChsID0+IEZpZWxkcy5saXN0KGwpKQogICAgICAgICAgLmRlc2MoImxpc3QgKCdbMSwgMiwgM10nKSIpLAogICAgICBvYmplY3RGaWVsZDogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHEuaWRlbnRpZmllci5vcihxLnN0cmluZyksIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIjoiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSwgcS5maWVsZCwgKG5hbWUsIF9zZXAsIHZhbHVlKSA9PiB7CiAgICAgICAgICByZXR1cm4geyBuYW1lLCB2YWx1ZSB9OwogICAgICB9KQogICAgICAgICAgLnNlcEJ5KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiwiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSkKICAgICAgICAgIC53cmFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoInsiKS5za2lwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UudGhlbihwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCJ9IikpKQogICAgICAgICAgLm1hcCh2YWxzID0+IHsKICAgICAgICAgIGxldCByZXMgPSB7fTsKICAgICAgICAgIGZvciAobGV0IGVudHJ5IG9mIHZhbHMpCiAgICAgICAgICAgICAgcmVzW2VudHJ5Lm5hbWVdID0gZW50cnkudmFsdWU7CiAgICAgICAgICByZXR1cm4gRmllbGRzLm9iamVjdChyZXMpOwogICAgICB9KQogICAgICAgICAgLmRlc2MoIm9iamVjdCAoJ3sgYTogMSwgYjogMiB9JykiKSwKICAgICAgYXRvbUlubGluZUZpZWxkOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQocS5kYXRlLCBxLmR1cmF0aW9uLm1hcChkID0+IG5vcm1hbGl6ZUR1cmF0aW9uKGQpKSwgcS5zdHJpbmcsIHEudGFnLCBxLmVtYmVkTGluaywgcS5ib29sLCBxLm51bWJlciwgcS5yYXdOdWxsKSwKICAgICAgaW5saW5lRmllbGRMaXN0OiBxID0+IHEuYXRvbUlubGluZUZpZWxkLnNlcEJ5KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiwiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKS5sb29rYWhlYWQocS5hdG9tSW5saW5lRmllbGQpKSwKICAgICAgaW5saW5lRmllbGQ6IHEgPT4gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHEuYXRvbUlubGluZUZpZWxkLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCIsIikudHJpbShwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSksIHEuaW5saW5lRmllbGRMaXN0LCAoZiwgX3MsIGwpID0+IFtmXS5jb25jYXQobCkpLCBxLmF0b21JbmxpbmVGaWVsZCksCiAgICAgIGF0b21GaWVsZDogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuYWx0KAogICAgICAvLyBQbGFjZSBlbWJlZCBsaW5rcyBhYm92ZSBuZWdhdGVkIGZpZWxkcyBhcyB0aGV5IGFyZSB0aGUgc3BlY2lhbCBwYXJzZXIgY2FzZSAnIVtbdGhpbmddXScgYW5kIGFyZSBnZW5lcmFsbHkgdW5hbWJpZ3VvdXMuCiAgICAgIHEuZW1iZWRMaW5rLm1hcChsID0+IEZpZWxkcy5saXRlcmFsKGwpKSwgcS5uZWdhdGVkRmllbGQsIHEubGlua0ZpZWxkLCBxLmxpc3RGaWVsZCwgcS5vYmplY3RGaWVsZCwgcS5sYW1iZGFGaWVsZCwgcS5wYXJlbnNGaWVsZCwgcS5ib29sRmllbGQsIHEubnVtYmVyRmllbGQsIHEuc3RyaW5nRmllbGQsIHEuZGF0ZUZpZWxkLCBxLmR1cmF0aW9uRmllbGQsIHEubnVsbEZpZWxkLCBxLnZhcmlhYmxlRmllbGQpLAogICAgICBpbmRleEZpZWxkOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocS5hdG9tRmllbGQsIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5hbHQocS5kb3RQb3N0Zml4LCBxLmluZGV4UG9zdGZpeCwgcS5mdW5jdGlvblBvc3RmaXgpLm1hbnkoKSwgKG9iaiwgcG9zdGZpeGVzKSA9PiB7CiAgICAgICAgICBsZXQgcmVzdWx0ID0gb2JqOwogICAgICAgICAgZm9yIChsZXQgcG9zdCBvZiBwb3N0Zml4ZXMpIHsKICAgICAgICAgICAgICBzd2l0Y2ggKHBvc3QudHlwZSkgewogICAgICAgICAgICAgICAgICBjYXNlICJkb3QiOgogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gRmllbGRzLmluZGV4KHJlc3VsdCwgRmllbGRzLmxpdGVyYWwocG9zdC5maWVsZCkpOwogICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgIGNhc2UgImluZGV4IjoKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IEZpZWxkcy5pbmRleChyZXN1bHQsIHBvc3QuZmllbGQpOwogICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgICAgIGNhc2UgImZ1bmN0aW9uIjoKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IEZpZWxkcy5mdW5jKHJlc3VsdCwgcG9zdC5maWVsZHMpOwogICAgICAgICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgfSksCiAgICAgIG5lZ2F0ZWRGaWVsZDogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiEiKSwgcS5pbmRleEZpZWxkLCAoXywgZmllbGQpID0+IEZpZWxkcy5uZWdhdGUoZmllbGQpKS5kZXNjKCJuZWdhdGVkIGZpZWxkIiksCiAgICAgIHBhcmVuc0ZpZWxkOiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKCIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcS5maWVsZCwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UsIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIikiKSwgKF8xLCBfMiwgZmllbGQsIF8zLCBfNCkgPT4gZmllbGQpLAogICAgICBsYW1iZGFGaWVsZDogcSA9PiBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc2VxTWFwKHEuaWRlbnRpZmllcgogICAgICAgICAgLnNlcEJ5KHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIiwiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSkKICAgICAgICAgIC53cmFwKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIigiKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKSIpLnRyaW0ocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UpKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiPT4iKS50cmltKHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5vcHRXaGl0ZXNwYWNlKSwgcS5maWVsZCwgKGlkZW50LCBfaWdub3JlLCB2YWx1ZSkgPT4gewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogImxhbWJkYSIsIGFyZ3VtZW50czogaWRlbnQsIHZhbHVlIH07CiAgICAgIH0pLAogICAgICBkb3RQb3N0Zml4OiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiLiIpLCBxLmlkZW50aWZpZXIsIChfLCBmaWVsZCkgPT4gewogICAgICAgICAgcmV0dXJuIHsgdHlwZTogImRvdCIsIGZpZWxkOiBmaWVsZCB9OwogICAgICB9KSwKICAgICAgaW5kZXhQb3N0Zml4OiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiWyIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcS5maWVsZCwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLm9wdFdoaXRlc3BhY2UsIHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zdHJpbmcoIl0iKSwgKF8sIF8yLCBmaWVsZCwgXzMsIF80KSA9PiB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAiaW5kZXgiLCBmaWVsZCB9OwogICAgICB9KSwKICAgICAgZnVuY3Rpb25Qb3N0Zml4OiBxID0+IHBhcnNpbW1vbl91bWRfbWluRXhwb3J0cy5zZXFNYXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKCIpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcS5maWVsZC5zZXBCeShwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMuc3RyaW5nKCIsIikudHJpbShwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSkpLCBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMub3B0V2hpdGVzcGFjZSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnN0cmluZygiKSIpLCAoXywgXzEsIGZpZWxkcywgXzIsIF8zKSA9PiB7CiAgICAgICAgICByZXR1cm4geyB0eXBlOiAiZnVuY3Rpb24iLCBmaWVsZHMgfTsKICAgICAgfSksCiAgICAgIC8vIFRoZSBwcmVjZWRlbmNlIGhpZXJhcmNoeSBvZiBvcGVyYXRvcnMgLSBtdWx0aXBseS9kaXZpZGUsIGFkZC9zdWJ0cmFjdCwgY29tcGFyZSwgYW5kIHRoZW4gYm9vbGVhbiBvcGVyYXRpb25zLgogICAgICBiaW5hcnlNdWxEaXZGaWVsZDogcSA9PiBjcmVhdGVCaW5hcnlQYXJzZXIocS5pbmRleEZpZWxkLCBxLmJpbmFyeU11bERpdiwgRmllbGRzLmJpbmFyeU9wKSwKICAgICAgYmluYXJ5UGx1c01pbnVzRmllbGQ6IHEgPT4gY3JlYXRlQmluYXJ5UGFyc2VyKHEuYmluYXJ5TXVsRGl2RmllbGQsIHEuYmluYXJ5UGx1c01pbnVzLCBGaWVsZHMuYmluYXJ5T3ApLAogICAgICBiaW5hcnlDb21wYXJlRmllbGQ6IHEgPT4gY3JlYXRlQmluYXJ5UGFyc2VyKHEuYmluYXJ5UGx1c01pbnVzRmllbGQsIHEuYmluYXJ5Q29tcGFyZU9wLCBGaWVsZHMuYmluYXJ5T3ApLAogICAgICBiaW5hcnlCb29sZWFuRmllbGQ6IHEgPT4gY3JlYXRlQmluYXJ5UGFyc2VyKHEuYmluYXJ5Q29tcGFyZUZpZWxkLCBxLmJpbmFyeUJvb2xlYW5PcCwgRmllbGRzLmJpbmFyeU9wKSwKICAgICAgYmluYXJ5T3BGaWVsZDogcSA9PiBxLmJpbmFyeUJvb2xlYW5GaWVsZCwKICAgICAgZmllbGQ6IHEgPT4gcS5iaW5hcnlPcEZpZWxkLAogIH0pOwoKICAvKiogUGFyc2UgaW5saW5lIGZpZWxkcyBhbmQgb3RoZXIgZW1iZWRkZWQgbWV0YWRhdGEgaW4gYSBsaW5lLiAqLwogIC8qKiBUaGUgd3JhcHBlciBjaGFyYWN0ZXJzIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGVmaW5lIGFuIGlubGluZSBmaWVsZC4gKi8KICBjb25zdCBJTkxJTkVfRklFTERfV1JBUFBFUlMgPSBPYmplY3QuZnJlZXplKHsKICAgICAgIlsiOiAiXSIsCiAgICAgICIoIjogIikiLAogIH0pOwogIC8qKgogICAqIEZpbmQgYSBtYXRjaGluZyBjbG9zaW5nIGJyYWNrZXQgdGhhdCBvY2N1cnMgYXQgb3IgYWZ0ZXIgYHN0YXJ0YCwgcmVzcGVjdGluZyBuZXN0aW5nIGFuZCBlc2NhcGVzLiBJZiBmb3VuZCwKICAgKiByZXR1cm5zIHRoZSB2YWx1ZSBjb250YWluZWQgd2l0aGluIGFuZCB0aGUgc3RyaW5nIGluZGV4IGFmdGVyIHRoZSBlbmQgb2YgdGhlIHZhbHVlLgogICAqLwogIGZ1bmN0aW9uIGZpbmRDbG9zaW5nKGxpbmUsIHN0YXJ0LCBvcGVuLCBjbG9zZSkgewogICAgICBsZXQgbmVzdGluZyA9IDA7CiAgICAgIGxldCBlc2NhcGVkID0gZmFsc2U7CiAgICAgIGZvciAobGV0IGluZGV4ID0gc3RhcnQ7IGluZGV4IDwgbGluZS5sZW5ndGg7IGluZGV4KyspIHsKICAgICAgICAgIGxldCBjaGFyID0gbGluZS5jaGFyQXQoaW5kZXgpOwogICAgICAgICAgLy8gQWxsb3dzIGZvciBkb3VibGUgZXNjYXBlcyBsaWtlICdcXCcgdG8gYmUgcmVuZGVyZWQgbm9ybWFsbHkuCiAgICAgICAgICBpZiAoY2hhciA9PSAiXFwiKSB7CiAgICAgICAgICAgICAgZXNjYXBlZCA9ICFlc2NhcGVkOwogICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgfQogICAgICAgICAgLy8gSWYgZXNjYXBlZCwgaWdub3JlIHRoZSBuZXh0IGNoYXJhY3RlciBmb3IgY29tcHV0aW5nIG5lc3RpbmcsIHJlZ2FyZGxlc3Mgb2Ygd2hhdCBpdCBpcy4KICAgICAgICAgIGlmIChlc2NhcGVkKSB7CiAgICAgICAgICAgICAgZXNjYXBlZCA9IGZhbHNlOwogICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgfQogICAgICAgICAgaWYgKGNoYXIgPT0gb3BlbikKICAgICAgICAgICAgICBuZXN0aW5nKys7CiAgICAgICAgICBlbHNlIGlmIChjaGFyID09IGNsb3NlKQogICAgICAgICAgICAgIG5lc3RpbmctLTsKICAgICAgICAgIC8vIE9ubHkgb2NjdXJzIGlmIHdlIGFyZSBvbiBhIGNsb3NlIGNoYXJhY3RlciBhbmQgdGhlcmUgaXMgbm8gbW9yZSBuZXN0aW5nLgogICAgICAgICAgaWYgKG5lc3RpbmcgPCAwKQogICAgICAgICAgICAgIHJldHVybiB7IHZhbHVlOiBsaW5lLnN1YnN0cmluZyhzdGFydCwgaW5kZXgpLnRyaW0oKSwgZW5kSW5kZXg6IGluZGV4ICsgMSB9OwogICAgICAgICAgZXNjYXBlZCA9IGZhbHNlOwogICAgICB9CiAgICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQogIC8qKiBGaW5kIHRoZSAnOjonIHNlcGFyYXRvciBpbiBhbiBpbmxpbmUgZmllbGQuICovCiAgZnVuY3Rpb24gZmluZFNlcGFyYXRvcihsaW5lLCBzdGFydCkgewogICAgICBsZXQgc2VwID0gbGluZS5pbmRleE9mKCI6OiIsIHN0YXJ0KTsKICAgICAgaWYgKHNlcCA8IDApCiAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkOwogICAgICByZXR1cm4geyBrZXk6IGxpbmUuc3Vic3RyaW5nKHN0YXJ0LCBzZXApLnRyaW0oKSwgdmFsdWVJbmRleDogc2VwICsgMiB9OwogIH0KICAvKiogVHJ5IHRvIGNvbXBsZXRlbHkgcGFyc2UgYW4gaW5saW5lIGZpZWxkIHN0YXJ0aW5nIGF0IHRoZSBnaXZlbiBwb3NpdGlvbi4gQXNzdW1lcyBgc3RhcnRgIGlzIG9uIGEgd3JhcHBpbmcgY2hhcmFjdGVyLiAqLwogIGZ1bmN0aW9uIGZpbmRTcGVjaWZpY0lubGluZUZpZWxkKGxpbmUsIHN0YXJ0KSB7CiAgICAgIGxldCBvcGVuID0gbGluZS5jaGFyQXQoc3RhcnQpOwogICAgICBsZXQga2V5ID0gZmluZFNlcGFyYXRvcihsaW5lLCBzdGFydCArIDEpOwogICAgICBpZiAoa2V5ID09PSB1bmRlZmluZWQpCiAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkOwogICAgICAvLyBGYWlsIHRoZSBtYXRjaCBpZiB3ZSBmaW5kIGFueSBzZXBhcmF0b3IgY2hhcmFjdGVycyAobm90IGFsbG93ZWQgaW4ga2V5cykuCiAgICAgIGZvciAobGV0IHNlcCBvZiBPYmplY3Qua2V5cyhJTkxJTkVfRklFTERfV1JBUFBFUlMpLmNvbmNhdChPYmplY3QudmFsdWVzKElOTElORV9GSUVMRF9XUkFQUEVSUykpKSB7CiAgICAgICAgICBpZiAoa2V5LmtleS5pbmNsdWRlcyhzZXApKQogICAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7CiAgICAgIH0KICAgICAgbGV0IHZhbHVlID0gZmluZENsb3NpbmcobGluZSwga2V5LnZhbHVlSW5kZXgsIG9wZW4sIElOTElORV9GSUVMRF9XUkFQUEVSU1tvcGVuXSk7CiAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkKQogICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgICAgcmV0dXJuIHsKICAgICAgICAgIGtleToga2V5LmtleSwKICAgICAgICAgIHZhbHVlOiB2YWx1ZS52YWx1ZSwKICAgICAgICAgIHN0YXJ0OiBzdGFydCwKICAgICAgICAgIHN0YXJ0VmFsdWU6IGtleS52YWx1ZUluZGV4LAogICAgICAgICAgZW5kOiB2YWx1ZS5lbmRJbmRleCwKICAgICAgICAgIHdyYXBwaW5nOiBvcGVuLAogICAgICB9OwogIH0KICAvKiogUGFyc2UgYSB0ZXh0dWFsIGlubGluZSBmaWVsZCB2YWx1ZSBpbnRvIHNvbWV0aGluZyB3ZSBjYW4gd29yayB3aXRoLiAqLwogIGZ1bmN0aW9uIHBhcnNlSW5saW5lVmFsdWUodmFsdWUpIHsKICAgICAgLy8gRW1wdHkgaW5saW5lIHZhbHVlcyAoaS5lLiwgbm8gdGV4dCkgc2hvdWxkIG1hcCB0byBudWxsIHRvIG1hdGNoIGxvbmctdGVybSBEYXRhdmlldyBzZW1hbnRpY3MuCiAgICAgIC8vIE51bGwgaXMgYWxzbyBhIG1vcmUgdW5pdmVyc2FsIHR5cGUgdG8gZGVhbCB3aXRoIHRoYW4gc3RyaW5ncywgc2luY2UgYWxsIGZ1bmN0aW9ucyBhY2NlcHQgbnVsbHMuCiAgICAgIGlmICh2YWx1ZS50cmltKCkgPT0gIiIpCiAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgLy8gVGhlIHN0cmlwcGVkIGxpdGVyYWwgZmllbGQgcGFyc2VyIHVuZGVyc3RhbmRzIGFsbCBvZiB0aGUgbm9uLWFycmF5L25vbi1vYmplY3QgZmllbGRzIGFuZCBjYW4gcGFyc2UgdGhlbSBmb3IgdXMuCiAgICAgIC8vIElubGluZSBmaWVsZCBvYmplY3RzIGFyZSBub3QgY3VycmVudGx5IHN1cHBvcnRlZDsgaW5saW5lIGFycmF5IG9iamVjdHMgaGF2ZSB0byBiZSBoYW5kbGVkIGJ5IHRoZSBwYXJzZXIKICAgICAgLy8gc2VwYXJhdGVseS4KICAgICAgbGV0IGlubGluZSA9IEVYUFJFU1NJT04uaW5saW5lRmllbGQucGFyc2UodmFsdWUpOwogICAgICBpZiAoaW5saW5lLnN0YXR1cykKICAgICAgICAgIHJldHVybiBpbmxpbmUudmFsdWU7CiAgICAgIGVsc2UKICAgICAgICAgIHJldHVybiB2YWx1ZTsKICB9CiAgLyoqIEV4dHJhY3RzIGlubGluZSBmaWVsZHMgb2YgdGhlIGZvcm0gJ1trZXk6OiB2YWx1ZV0nIGZyb20gYSBsaW5lIG9mIHRleHQuIFRoaXMgaXMgZG9uZSBpbiBhIHJlbGF0aXZlbHkKICAgKiAicm9idXN0IiB3YXkgdG8gYXZvaWQgZmFpbGluZyBkdWUgdG8gYmFkIG5lc3Rpbmcgb3Igb3RoZXIgaW50ZXJmZXJpbmcgTWFya2Rvd24gc3ltYm9sczoKICAgKgogICAqIC0gTG9vayBmb3IgYW55IHdyYXBwZXJzICgnWycgYW5kICcoJykgaW4gdGhlIGxpbmUsIHRyeWluZyB0byBwYXJzZSB3aGF0ZXZlciBjb21lcyBhZnRlciBpdCBhcyBhbiBpbmxpbmUga2V5OjouCiAgICogLSBJZiBzdWNjZXNzZnVsLCBzY2FuIHVudGlsIHlvdSBmaW5kIGEgbWF0Y2hpbmcgZW5kIGJyYWNrZXQsIGFuZCBwYXJzZSB3aGF0ZXZlciByZW1haW5zIGFzIGFuIGlubGluZSB2YWx1ZS4KICAgKi8KICBmdW5jdGlvbiBleHRyYWN0SW5saW5lRmllbGRzKGxpbmUsIGluY2x1ZGVUYXNrRmllbGRzID0gZmFsc2UpIHsKICAgICAgbGV0IGZpZWxkcyA9IFtdOwogICAgICBmb3IgKGxldCB3cmFwcGVyIG9mIE9iamVjdC5rZXlzKElOTElORV9GSUVMRF9XUkFQUEVSUykpIHsKICAgICAgICAgIGxldCBmb3VuZEluZGV4ID0gbGluZS5pbmRleE9mKHdyYXBwZXIpOwogICAgICAgICAgd2hpbGUgKGZvdW5kSW5kZXggPj0gMCkgewogICAgICAgICAgICAgIGxldCBwYXJzZWRGaWVsZCA9IGZpbmRTcGVjaWZpY0lubGluZUZpZWxkKGxpbmUsIGZvdW5kSW5kZXgpOwogICAgICAgICAgICAgIGlmICghcGFyc2VkRmllbGQpIHsKICAgICAgICAgICAgICAgICAgZm91bmRJbmRleCA9IGxpbmUuaW5kZXhPZih3cmFwcGVyLCBmb3VuZEluZGV4ICsgMSk7CiAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgIH0KICAgICAgICAgICAgICBmaWVsZHMucHVzaChwYXJzZWRGaWVsZCk7CiAgICAgICAgICAgICAgZm91bmRJbmRleCA9IGxpbmUuaW5kZXhPZih3cmFwcGVyLCBwYXJzZWRGaWVsZC5lbmQpOwogICAgICAgICAgfQogICAgICB9CiAgICAgIGlmIChpbmNsdWRlVGFza0ZpZWxkcykKICAgICAgICAgIGZpZWxkcyA9IGZpZWxkcy5jb25jYXQoZXh0cmFjdFNwZWNpYWxUYXNrRmllbGRzKGxpbmUpKTsKICAgICAgZmllbGRzLnNvcnQoKGEsIGIpID0+IGEuc3RhcnQgLSBiLnN0YXJ0KTsKICAgICAgbGV0IGZpbHRlcmVkRmllbGRzID0gW107CiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZmllbGRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgICBpZiAoaSA9PSAwIHx8IGZpbHRlcmVkRmllbGRzW2ZpbHRlcmVkRmllbGRzLmxlbmd0aCAtIDFdLmVuZCA8IGZpZWxkc1tpXS5zdGFydCkgewogICAgICAgICAgICAgIGZpbHRlcmVkRmllbGRzLnB1c2goZmllbGRzW2ldKTsKICAgICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gZmlsdGVyZWRGaWVsZHM7CiAgfQogIC8qKiBWYWxpZGF0ZXMgdGhhdCBhIHJhdyBmaWVsZCBuYW1lIGhhcyBhIHZhbGlkIGZvcm0uICovCiAgY29uc3QgRlVMTF9MSU5FX0tFWV9QQVJUID0gcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLmFsdChwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKG5ldyBSZWdFeHAoZW1vamlSZWdleCgpLCAidSIpKSwgcGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvWzAtOVxwe0xldHRlcn1cd1xzXy8tXSsvdSkpCiAgICAgIC5tYW55KCkKICAgICAgLm1hcChwYXJ0cyA9PiBwYXJ0cy5qb2luKCIiKSk7CiAgY29uc3QgRlVMTF9MSU5FX0tFWV9QQVJTRVIgPSBwYXJzaW1tb25fdW1kX21pbkV4cG9ydHMucmVnZXhwKC9bXjAtOVx3XHB7TGV0dGVyfV0qL3UpCiAgICAgIC50aGVuKEZVTExfTElORV9LRVlfUEFSVCkKICAgICAgLnNraXAocGFyc2ltbW9uX3VtZF9taW5FeHBvcnRzLnJlZ2V4cCgvW19cKn5gXSovdSkpOwogIC8qKiBBdHRlbXB0IHRvIGV4dHJhY3QgYSBmdWxsLWxpbmUgZmllbGQgKEtleTo6IFZhbHVlIGNvbnN1bWluZyB0aGUgZW50aXJlIGNvbnRlbnQgbGluZSkuICovCiAgZnVuY3Rpb24gZXh0cmFjdEZ1bGxMaW5lRmllbGQodGV4dCkgewogICAgICBsZXQgc2VwID0gZmluZFNlcGFyYXRvcih0ZXh0LCAwKTsKICAgICAgaWYgKCFzZXApCiAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkOwogICAgICAvLyBXZSBuZWVkIHRvIHBvc3QtcHJvY2VzcyB0aGUga2V5IHRvIGRyb3AgdW5uZWNlc3Nhcnkgb3BlbmluZyBhbm5vdGF0aW9ucyBhcyB3ZWxsIGFzCiAgICAgIC8vIGRyb3Agc3Vycm91bmRpbmcgTWFya2Rvd24uCiAgICAgIGxldCByZWFsS2V5ID0gRlVMTF9MSU5FX0tFWV9QQVJTRVIucGFyc2Uoc2VwLmtleSk7CiAgICAgIGlmICghcmVhbEtleS5zdGF0dXMpCiAgICAgICAgICByZXR1cm4gdW5kZWZpbmVkOwogICAgICByZXR1cm4gewogICAgICAgICAga2V5OiByZWFsS2V5LnZhbHVlLAogICAgICAgICAgdmFsdWU6IHRleHQuc3Vic3RyaW5nKHNlcC52YWx1ZUluZGV4KS50cmltKCksCiAgICAgICAgICBzdGFydDogMCwKICAgICAgICAgIHN0YXJ0VmFsdWU6IHNlcC52YWx1ZUluZGV4LAogICAgICAgICAgZW5kOiB0ZXh0Lmxlbmd0aCwKICAgICAgfTsKICB9CiAgY29uc3QgQ1JFQVRFRF9EQVRFX1JFR0VYID0gL1x1ezI3OTV9XHMqKFxkezR9LVxkezJ9LVxkezJ9KS91OwogIGNvbnN0IERVRV9EQVRFX1JFR0VYID0gLyg/Olx1ezFGNEM1fXxcdXsxRjRDNn18XHV7MUY1RDN9XHV7RkUwRn0/KVxzKihcZHs0fS1cZHsyfS1cZHsyfSkvdTsKICBjb25zdCBET05FX0RBVEVfUkVHRVggPSAvXHV7MjcwNX1ccyooXGR7NH0tXGR7Mn0tXGR7Mn0pL3U7CiAgY29uc3QgU0NIRURVTEVEX0RBVEVfUkVHRVggPSAvW1x1ezIzRjN9XHV7MjMxQn1dXHMqKFxkezR9LVxkezJ9LVxkezJ9KS91OwogIGNvbnN0IFNUQVJUX0RBVEVfUkVHRVggPSAvXHV7MUY2RUJ9XHMqKFxkezR9LVxkezJ9LVxkezJ9KS91OwogIGNvbnN0IEVNT0pJX1JFR0VYRVMgPSBbCiAgICAgIHsgcmVnZXg6IENSRUFURURfREFURV9SRUdFWCwga2V5OiAiY3JlYXRlZCIgfSwKICAgICAgeyByZWdleDogU1RBUlRfREFURV9SRUdFWCwga2V5OiAic3RhcnQiIH0sCiAgICAgIHsgcmVnZXg6IFNDSEVEVUxFRF9EQVRFX1JFR0VYLCBrZXk6ICJzY2hlZHVsZWQiIH0sCiAgICAgIHsgcmVnZXg6IERVRV9EQVRFX1JFR0VYLCBrZXk6ICJkdWUiIH0sCiAgICAgIHsgcmVnZXg6IERPTkVfREFURV9SRUdFWCwga2V5OiAiY29tcGxldGlvbiIgfSwKICBdOwogIC8qKiBQYXJzZSBzcGVjaWFsIGNvbXBsZXRlZC9kdWUvZG9uZSB0YXNrIGZpZWxkcyB3aGljaCBhcmUgbWFya2VkIHZpYSBlbW9qaS4gKi8KICBmdW5jdGlvbiBleHRyYWN0U3BlY2lhbFRhc2tGaWVsZHMobGluZSkgewogICAgICBsZXQgcmVzdWx0cyA9IFtdOwogICAgICBmb3IgKGxldCB7IHJlZ2V4LCBrZXkgfSBvZiBFTU9KSV9SRUdFWEVTKSB7CiAgICAgICAgICBjb25zdCBtYXRjaCA9IHJlZ2V4LmV4ZWMobGluZSk7CiAgICAgICAgICBpZiAoIW1hdGNoKQogICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgcmVzdWx0cy5wdXNoKHsKICAgICAgICAgICAgICBrZXksCiAgICAgICAgICAgICAgdmFsdWU6IG1hdGNoWzFdLAogICAgICAgICAgICAgIHN0YXJ0OiBtYXRjaC5pbmRleCwKICAgICAgICAgICAgICBzdGFydFZhbHVlOiBtYXRjaC5pbmRleCArIDEsCiAgICAgICAgICAgICAgZW5kOiBtYXRjaC5pbmRleCArIG1hdGNoWzBdLmxlbmd0aCwKICAgICAgICAgICAgICB3cmFwcGluZzogImVtb2ppLXNob3J0aGFuZCIsCiAgICAgICAgICB9KTsKICAgICAgfQogICAgICByZXR1cm4gcmVzdWx0czsKICB9CgogIC8qKiBBbGwgZXh0cmFjdGVkIG1hcmtkb3duIGZpbGUgbWV0YWRhdGEgb2J0YWluZWQgZnJvbSBhIGZpbGUuICovCiAgY2xhc3MgUGFnZU1ldGFkYXRhIHsKICAgICAgLyoqIFRoZSBwYXRoIHRoaXMgZmlsZSBleGlzdHMgYXQuICovCiAgICAgIHBhdGg7CiAgICAgIC8qKiBPYnNpZGlhbi1wcm92aWRlZCBkYXRlIHRoaXMgcGFnZSB3YXMgY3JlYXRlZC4gKi8KICAgICAgY3RpbWU7CiAgICAgIC8qKiBPYnNpZGlhbi1wcm92aWRlZCBkYXRlIHRoaXMgcGFnZSB3YXMgbW9kaWZpZWQuICovCiAgICAgIG10aW1lOwogICAgICAvKiogT2JzaWRpYW4tcHJvdmlkZWQgc2l6ZSBvZiB0aGlzIHBhZ2UgaW4gYnl0ZXMuICovCiAgICAgIHNpemU7CiAgICAgIC8qKiBUaGUgZGF5IGFzc29jaWF0ZWQgd2l0aCB0aGlzIHBhZ2UsIGlmIHJlbGV2YW50LiAqLwogICAgICBkYXk7CiAgICAgIC8qKiBUaGUgZmlyc3QgSDEvSDIgaGVhZGVyIGluIHRoZSBmaWxlLiBNYXkgbm90IGV4aXN0LiAqLwogICAgICB0aXRsZTsKICAgICAgLyoqIEFsbCBvZiB0aGUgZmllbGRzIGNvbnRhaW5lZCBpbiB0aGlzIG1hcmtkb3duIGZpbGUgLSBib3RoIGZyb250bWF0dGVyIEFORCBpbi1maWxlIGxpbmtzLiAqLwogICAgICBmaWVsZHM7CiAgICAgIC8qKiBBbGwgb2YgdGhlIGV4YWN0IHRhZ3MgKHByZWZpeGVkIHdpdGggJyMnKSBpbiB0aGlzIGZpbGUgb3ZlcmFsbC4gKi8KICAgICAgdGFnczsKICAgICAgLyoqIEFsbCBvZiB0aGUgYWxpYXNlcyBkZWZpbmVkIGZvciB0aGlzIGZpbGUuICovCiAgICAgIGFsaWFzZXM7CiAgICAgIC8qKiBBbGwgT1VUR09JTkcgbGlua3MgKGluY2x1ZGluZyBlbWJlZHMsIGhlYWRlciArIGJsb2NrIGxpbmtzKSBpbiB0aGlzIGZpbGUuICovCiAgICAgIGxpbmtzOwogICAgICAvKiogQWxsIGxpc3QgaXRlbXMgY29udGFpbmVkIHdpdGhpbiB0aGlzIHBhZ2UuIEZpbHRlciBmb3IgdGFza3MgdG8gZ2V0IGp1c3QgdGFza3MuICovCiAgICAgIGxpc3RzOwogICAgICAvKiogVGhlIHJhdyBmcm9udG1hdHRlciBmb3IgdGhpcyBkb2N1bWVudC4gKi8KICAgICAgZnJvbnRtYXR0ZXI7CiAgICAgIGNvbnN0cnVjdG9yKHBhdGgsIGluaXQpIHsKICAgICAgICAgIHRoaXMucGF0aCA9IHBhdGg7CiAgICAgICAgICB0aGlzLmZpZWxkcyA9IG5ldyBNYXAoKTsKICAgICAgICAgIHRoaXMuZnJvbnRtYXR0ZXIgPSB7fTsKICAgICAgICAgIHRoaXMudGFncyA9IG5ldyBTZXQoKTsKICAgICAgICAgIHRoaXMuYWxpYXNlcyA9IG5ldyBTZXQoKTsKICAgICAgICAgIHRoaXMubGlua3MgPSBbXTsKICAgICAgICAgIE9iamVjdC5hc3NpZ24odGhpcywgaW5pdCk7CiAgICAgICAgICB0aGlzLmxpc3RzID0gKHRoaXMubGlzdHMgfHwgW10pLm1hcChsID0+IG5ldyBMaXN0SXRlbShsKSk7CiAgICAgIH0KICAgICAgLyoqIENhbm9uaWNhbGl6ZSByYXcgbGlua3MgYW5kIG90aGVyIGRhdGEgaW4gcGFydGlhbCBkYXRhIHdpdGggbm9ybWFsaXplcnMsIHJldHVybmluZyBhIGNvbXBsZXRlZCBvYmplY3QuICovCiAgICAgIHN0YXRpYyBjYW5vbmljYWxpemUoZGF0YSwgbGlua05vcm1hbGl6ZXIpIHsKICAgICAgICAgIC8vIE11dGF0ZSB0aGUgZGF0YSBmb3Igbm93LCB3aGljaCBpcyBwcm9iYWJseSBhIGJhZCBpZGVhIGJ1dC4uLiBhbGwgd2VsbC4KICAgICAgICAgIGlmIChkYXRhLmZyb250bWF0dGVyKSB7CiAgICAgICAgICAgICAgZGF0YS5mcm9udG1hdHRlciA9IFZhbHVlcy5tYXBMZWF2ZXMoZGF0YS5mcm9udG1hdHRlciwgdCA9PiBWYWx1ZXMuaXNMaW5rKHQpID8gbGlua05vcm1hbGl6ZXIodCkgOiB0KTsKICAgICAgICAgIH0KICAgICAgICAgIGlmIChkYXRhLmZpZWxkcykgewogICAgICAgICAgICAgIGZvciAobGV0IFtrZXksIHZhbHVlXSBvZiBkYXRhLmZpZWxkcy5lbnRyaWVzKCkpIHsKICAgICAgICAgICAgICAgICAgZGF0YS5maWVsZHMuc2V0KGtleSwgVmFsdWVzLm1hcExlYXZlcyh2YWx1ZSwgdCA9PiAoVmFsdWVzLmlzTGluayh0KSA/IGxpbmtOb3JtYWxpemVyKHQpIDogdCkpKTsKICAgICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgICBpZiAoZGF0YS5saXN0cykgewogICAgICAgICAgICAgIGZvciAobGV0IGl0ZW0gb2YgZGF0YS5saXN0cykgewogICAgICAgICAgICAgICAgICBmb3IgKGxldCBba2V5LCB2YWx1ZV0gb2YgaXRlbS5maWVsZHMuZW50cmllcygpKSB7CiAgICAgICAgICAgICAgICAgICAgICBpdGVtLmZpZWxkcy5zZXQoa2V5LCB2YWx1ZS5tYXAoeCA9PiBWYWx1ZXMubWFwTGVhdmVzKHgsIHQgPT4gKFZhbHVlcy5pc0xpbmsodCkgPyBsaW5rTm9ybWFsaXplcih0KSA6IHQpKSkpOwogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICAgICAgaWYgKGRhdGEubGlua3MpIHsKICAgICAgICAgICAgICBkYXRhLmxpbmtzID0gZGF0YS5saW5rcy5tYXAobCA9PiBsaW5rTm9ybWFsaXplcihsKSk7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBUaGlzIGlzIHByZXR0eSB1Z2x5LCBidXQgaXQncyBub3QgcG9zc2libGUgdG8gbm9ybWFsaXplIG9uIHRoZSB3b3JrZXIgdGhyZWFkIHRoYXQgZG9lcyBwYXJzaW5nLgogICAgICAgICAgLy8gVGhlIGJlc3Qgd2F5IHRvIGltcHJvdmUgdGhpcyBpcyB0byBpbnN0ZWFkIGp1c3QgY2Fub25pY2FsaXplIHRoZSBlbnRpcmUgZGF0YSBvYmplY3Q7IEkgY2FuIHRyeSB0bwogICAgICAgICAgLy8gb3B0aW1pemUgYFZhbHVlcy5tYXBMZWF2ZXNgIHRvIG9ubHkgbXV0YXRlIGlmIGl0IGFjdHVhbGx5IGNoYW5nZXMgdGhpbmdzLgogICAgICAgICAgcmV0dXJuIG5ldyBQYWdlTWV0YWRhdGEoZGF0YS5wYXRoLCBkYXRhKTsKICAgICAgfQogICAgICAvKiogVGhlIG5hbWUgKGJhc2VkIG9uIHBhdGgpIG9mIHRoaXMgZmlsZS4gKi8KICAgICAgbmFtZSgpIHsKICAgICAgICAgIHJldHVybiBnZXRGaWxlVGl0bGUodGhpcy5wYXRoKTsKICAgICAgfQogICAgICAvKiogVGhlIGNvbnRhaW5pbmcgZm9sZGVyIChiYXNlZCBvbiBwYXRoKSBvZiB0aGlzIGZpbGUuICovCiAgICAgIGZvbGRlcigpIHsKICAgICAgICAgIHJldHVybiBnZXRQYXJlbnRGb2xkZXIodGhpcy5wYXRoKTsKICAgICAgfQogICAgICAvKiogVGhlIGV4dGVuc2lvbiBvZiB0aGlzIGZpbGUgKGxpa2VseSAnbWQnKS4gKi8KICAgICAgZXh0ZW5zaW9uKCkgewogICAgICAgICAgcmV0dXJuIGdldEV4dGVuc2lvbih0aGlzLnBhdGgpOwogICAgICB9CiAgICAgIC8qKiBSZXR1cm4gYSBzZXQgb2YgdGFncyBBTkQgYWxsIG9mIHRoZWlyIHBhcmVudCB0YWdzIChzbyAjaGVsbG8veWVzIHdvdWxkIGJlY29tZSAjaGVsbG8sICNoZWxsby95ZXMpLiAqLwogICAgICBmdWxsVGFncygpIHsKICAgICAgICAgIGxldCByZXN1bHQgPSBuZXcgU2V0KCk7CiAgICAgICAgICBmb3IgKGxldCB0YWcgb2YgdGhpcy50YWdzKSB7CiAgICAgICAgICAgICAgZm9yIChsZXQgc3VidGFnIG9mIGV4dHJhY3RTdWJ0YWdzKHRhZykpCiAgICAgICAgICAgICAgICAgIHJlc3VsdC5hZGQoc3VidGFnKTsKICAgICAgICAgIH0KICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgIH0KICAgICAgLyoqIENvbnZlcnQgYWxsIGxpbmtzIGluIHRoaXMgZmlsZSB0byBmaWxlIGxpbmtzLiAqLwogICAgICBmaWxlTGlua3MoKSB7CiAgICAgICAgICAvLyBXZSB3YW50IHRvIG1ha2UgdGhlbSBkaXN0aW5jdCwgYnV0IHdoZXJlIGxpbmtzIGFyZSBub3QgcmF3IGxpbmtzIHdlCiAgICAgICAgICAvLyBub3cga2VlcCB0aGUgYWRkaXRpb25hbCBtZXRhZGF0YS4KICAgICAgICAgIGxldCBkaXN0aW5jdExpbmtzID0gbmV3IFNldCh0aGlzLmxpbmtzKTsKICAgICAgICAgIHJldHVybiBBcnJheS5mcm9tKGRpc3RpbmN0TGlua3MpOwogICAgICB9CiAgICAgIC8qKiBNYXAgdGhpcyBtZXRhZGF0YSB0byBhIGZ1bGwgb2JqZWN0OyB1c2VzIHRoZSBpbmRleCBmb3IgYWRkaXRpb25hbCBkYXRhIGxvb2t1cHMuICAqLwogICAgICBzZXJpYWxpemUoaW5kZXgsIGNhY2hlKSB7CiAgICAgICAgICAvLyBDb252ZXJ0IGxpc3QgaXRlbXMgdmlhIHRoZSBjYW5vbmljYWxpemF0aW9uIGNhY2hlLgogICAgICAgICAgbGV0IHJlYWxDYWNoZSA9IGNhY2hlID8/IG5ldyBMaXN0U2VyaWFsaXphdGlvbkNhY2hlKHRoaXMubGlzdHMpOwogICAgICAgICAgbGV0IHJlc3VsdCA9IHsKICAgICAgICAgICAgICBmaWxlOiB7CiAgICAgICAgICAgICAgICAgIHBhdGg6IHRoaXMucGF0aCwKICAgICAgICAgICAgICAgICAgZm9sZGVyOiB0aGlzLmZvbGRlcigpLAogICAgICAgICAgICAgICAgICBuYW1lOiB0aGlzLm5hbWUoKSwKICAgICAgICAgICAgICAgICAgbGluazogTGluay5maWxlKHRoaXMucGF0aCksCiAgICAgICAgICAgICAgICAgIG91dGxpbmtzOiB0aGlzLmZpbGVMaW5rcygpLAogICAgICAgICAgICAgICAgICBpbmxpbmtzOiBBcnJheS5mcm9tKGluZGV4LmxpbmtzLmdldEludmVyc2UodGhpcy5wYXRoKSkubWFwKGwgPT4gTGluay5maWxlKGwpKSwKICAgICAgICAgICAgICAgICAgZXRhZ3M6IEFycmF5LmZyb20odGhpcy50YWdzKSwKICAgICAgICAgICAgICAgICAgdGFnczogQXJyYXkuZnJvbSh0aGlzLmZ1bGxUYWdzKCkpLAogICAgICAgICAgICAgICAgICBhbGlhc2VzOiBBcnJheS5mcm9tKHRoaXMuYWxpYXNlcyksCiAgICAgICAgICAgICAgICAgIGxpc3RzOiB0aGlzLmxpc3RzLm1hcChsID0+IHJlYWxDYWNoZS5nZXQobC5saW5lKSksCiAgICAgICAgICAgICAgICAgIHRhc2tzOiB0aGlzLmxpc3RzLmZpbHRlcihsID0+ICEhbC50YXNrKS5tYXAobCA9PiByZWFsQ2FjaGUuZ2V0KGwubGluZSkpLAogICAgICAgICAgICAgICAgICBjdGltZTogdGhpcy5jdGltZSwKICAgICAgICAgICAgICAgICAgY2RheTogc3RyaXBUaW1lKHRoaXMuY3RpbWUpLAogICAgICAgICAgICAgICAgICBtdGltZTogdGhpcy5tdGltZSwKICAgICAgICAgICAgICAgICAgbWRheTogc3RyaXBUaW1lKHRoaXMubXRpbWUpLAogICAgICAgICAgICAgICAgICBzaXplOiB0aGlzLnNpemUsCiAgICAgICAgICAgICAgICAgIHN0YXJyZWQ6IGluZGV4LnN0YXJyZWQuc3RhcnJlZCh0aGlzLnBhdGgpLAogICAgICAgICAgICAgICAgICBmcm9udG1hdHRlcjogVmFsdWVzLmRlZXBDb3B5KHRoaXMuZnJvbnRtYXR0ZXIpLAogICAgICAgICAgICAgICAgICBleHQ6IHRoaXMuZXh0ZW5zaW9uKCksCiAgICAgICAgICAgICAgfSwKICAgICAgICAgIH07CiAgICAgICAgICAvLyBBZGQgdGhlIGN1cnJlbnQgZGF5IGlmIHByZXNlbnQuCiAgICAgICAgICBpZiAodGhpcy5kYXkpCiAgICAgICAgICAgICAgcmVzdWx0LmZpbGUuZGF5ID0gdGhpcy5kYXk7CiAgICAgICAgICAvLyBUaGVuIGFwcGVuZCB0aGUgY29tcHV0ZWQgZmllbGRzLgogICAgICAgICAgZm9yIChsZXQgW2tleSwgdmFsdWVdIG9mIHRoaXMuZmllbGRzLmVudHJpZXMoKSkgewogICAgICAgICAgICAgIGlmIChrZXkgaW4gcmVzdWx0KQogICAgICAgICAgICAgICAgICBjb250aW51ZTsgLy8gRG9uJ3QgYWxsb3cgZmllbGRzIHRvIG92ZXJyaWRlIGV4aXN0aW5nIGtleXMuCiAgICAgICAgICAgICAgcmVzdWx0W2tleV0gPSB2YWx1ZTsKICAgICAgICAgIH0KICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgIH0KICB9CiAgLyoqIEEgbGlzdCBpdGVtIGluc2lkZSBvZiBhIGxpc3QuICovCiAgY2xhc3MgTGlzdEl0ZW0gewogICAgICAvKiogVGhlIHN5bWJvbCAoJyonLCAnLScsICcxLicpIHVzZWQgdG8gZGVmaW5lIHRoaXMgbGlzdCBpdGVtLiAqLwogICAgICBzeW1ib2w7CiAgICAgIC8qKiBBIGxpbmsgd2hpY2ggcG9pbnRzIHRvIHRoaXMgdGFzaywgb3IgdG8gdGhlIGNsb3Nlc3QgYmxvY2sgdGhhdCB0aGlzIHRhc2sgaXMgY29udGFpbmVkIGluLiAqLwogICAgICBsaW5rOwogICAgICAvKiogQSBsaW5rIHRvIHRoZSBzZWN0aW9uIHRoYXQgY29udGFpbnMgdGhpcyBsaXN0IGVsZW1lbnQ7IGNvdWxkIGJlIGEgZmlsZSBpZiB0aGlzIGlzIG5vdCBpbiBhIHNlY3Rpb24uICovCiAgICAgIHNlY3Rpb247CiAgICAgIC8qKiBUaGUgdGV4dCBvZiB0aGlzIGxpc3QgaXRlbS4gVGhpcyBtYXkgYmUgbXVsdGlwbGUgbGluZXMgb2YgbWFya2Rvd24uICovCiAgICAgIHRleHQ7CiAgICAgIC8qKiBUaGUgbGluZSB0aGF0IHRoaXMgbGlzdCBpdGVtIHN0YXJ0cyBvbiBpbiB0aGUgZmlsZS4gKi8KICAgICAgbGluZTsKICAgICAgLyoqIFRoZSBudW1iZXIgb2YgbGluZXMgdGhhdCBkZWZpbmUgdGhpcyBsaXN0IGl0ZW0uICovCiAgICAgIGxpbmVDb3VudDsKICAgICAgLyoqIFRoZSBsaW5lIG51bWJlciBmb3IgdGhlIGZpcnN0IGxpc3QgaXRlbSBpbiB0aGUgbGlzdCB0aGlzIGl0ZW0gYmVsb25ncyB0by4gKi8KICAgICAgbGlzdDsKICAgICAgLyoqIEFueSBsaW5rcyBjb250YWluZWQgd2l0aGluIHRoaXMgbGlzdCBpdGVtLiAqLwogICAgICBsaW5rczsKICAgICAgLyoqIFRoZSB0YWdzIGNvbnRhaW5lZCB3aXRoaW4gdGhpcyBsaXN0IGl0ZW0uICovCiAgICAgIHRhZ3M7CiAgICAgIC8qKiBUaGUgcmF3IE9ic2lkaWFuLXByb3ZpZGVkIHBvc2l0aW9uIGZvciB3aGVyZSB0aGlzIHRhc2sgaXMuICovCiAgICAgIHBvc2l0aW9uOwogICAgICAvKiogVGhlIGxpbmUgbnVtYmVyIG9mIHRoZSBwYXJlbnQgbGlzdCBpdGVtLCBpZiBwcmVzZW50OyBpZiB0aGlzIGlzIHVuZGVmaW5lZCwgdGhpcyBpcyBhIHJvb3QgaXRlbS4gKi8KICAgICAgcGFyZW50OwogICAgICAvKiogVGhlIGxpbmUgbnVtYmVycyBvZiBjaGlsZHJlbiBvZiB0aGlzIGxpc3QgaXRlbS4gKi8KICAgICAgY2hpbGRyZW47CiAgICAgIC8qKiBUaGUgYmxvY2sgSUQgZm9yIHRoaXMgaXRlbSwgaWYgb25lIGlzIHByZXNlbnQuICovCiAgICAgIGJsb2NrSWQ7CiAgICAgIC8qKiBBbnkgZmllbGRzIGRlZmluZWQgaW4gdGhpcyBsaXN0IGl0ZW0uIEZvciB0YXNrcywgdGhpcyBpbmNsdWRlcyBmaWVsZHMgdW5kZXJuZWF0aCB0aGUgdGFzay4gKi8KICAgICAgZmllbGRzOwogICAgICB0YXNrOwogICAgICBjb25zdHJ1Y3Rvcihpbml0KSB7CiAgICAgICAgICBPYmplY3QuYXNzaWduKHRoaXMsIGluaXQpOwogICAgICAgICAgdGhpcy5maWVsZHMgPSB0aGlzLmZpZWxkcyB8fCBuZXcgTWFwKCk7CiAgICAgICAgICB0aGlzLnRhZ3MgPSB0aGlzLnRhZ3MgfHwgbmV3IFNldCgpOwogICAgICAgICAgdGhpcy5jaGlsZHJlbiA9IHRoaXMuY2hpbGRyZW4gfHwgW107CiAgICAgICAgICB0aGlzLmxpbmtzID0gdGhpcy5saW5rcyB8fCBbXTsKICAgICAgfQogICAgICBpZCgpIHsKICAgICAgICAgIHJldHVybiBgJHt0aGlzLmZpbGUoKS5wYXRofS0ke3RoaXMubGluZX1gOwogICAgICB9CiAgICAgIGZpbGUoKSB7CiAgICAgICAgICByZXR1cm4gdGhpcy5saW5rLnRvRmlsZSgpOwogICAgICB9CiAgICAgIG1hcmtkb3duKCkgewogICAgICAgICAgaWYgKHRoaXMudGFzaykKICAgICAgICAgICAgICByZXR1cm4gYCR7dGhpcy5zeW1ib2x9IFske3RoaXMudGFzay5jb21wbGV0ZWQgPyAieCIgOiAiICJ9XSAke3RoaXMudGV4dH1gOwogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIHJldHVybiBgJHt0aGlzLnN5bWJvbH0gJHt0aGlzLnRleHR9YDsKICAgICAgfQogICAgICBjcmVhdGVkKCkgewogICAgICAgICAgcmV0dXJuICh0aGlzLmZpZWxkcy5nZXQoImNyZWF0ZWQiKSA/PyB0aGlzLmZpZWxkcy5nZXQoImN0aW1lIikgPz8gdGhpcy5maWVsZHMuZ2V0KCJjZGF5IikpPy5bMF07CiAgICAgIH0KICAgICAgZHVlKCkgewogICAgICAgICAgcmV0dXJuICh0aGlzLmZpZWxkcy5nZXQoImR1ZSIpID8/IHRoaXMuZmllbGRzLmdldCgiZHVldGltZSIpID8/IHRoaXMuZmllbGRzLmdldCgiZHVlZGF5IikpPy5bMF07CiAgICAgIH0KICAgICAgY29tcGxldGVkKCkgewogICAgICAgICAgcmV0dXJuICh0aGlzLmZpZWxkcy5nZXQoImNvbXBsZXRlZCIpID8/CiAgICAgICAgICAgICAgdGhpcy5maWVsZHMuZ2V0KCJjb21wbGV0aW9uIikgPz8KICAgICAgICAgICAgICB0aGlzLmZpZWxkcy5nZXQoImNvbXB0aW1lIikgPz8KICAgICAgICAgICAgICB0aGlzLmZpZWxkcy5nZXQoImNvbXBkYXkiKSk/LlswXTsKICAgICAgfQogICAgICBzdGFydCgpIHsKICAgICAgICAgIHJldHVybiB0aGlzLmZpZWxkcy5nZXQoInN0YXJ0Iik/LlswXTsKICAgICAgfQogICAgICBzY2hlZHVsZWQoKSB7CiAgICAgICAgICByZXR1cm4gdGhpcy5maWVsZHMuZ2V0KCJzY2hlZHVsZWQiKT8uWzBdOwogICAgICB9CiAgICAgIC8qKiBDcmVhdGUgYW4gQVBJLWZyaWVuZGx5IGNvcHkgb2YgdGhpcyBsaXN0IGl0ZW0uIERlLWR1cGxpY2F0aW9uIGlzIGRvbmUgdmlhIHRoZSBwcm92aWRlZCBjYWNoZS4gKi8KICAgICAgc2VyaWFsaXplKGNhY2hlKSB7CiAgICAgICAgICAvLyBNYXAgY2hpbGRyZW4gdG8gdGhlaXIgc2VyaWFsaXplZC9kZS1kdXBsaWNhdGVkIGVxdWl2YWxlbnRzIHJpZ2h0IGF3YXkuCiAgICAgICAgICBsZXQgY2hpbGRyZW4gPSB0aGlzLmNoaWxkcmVuLm1hcChsID0+IGNhY2hlLmdldChsKSkuZmlsdGVyKChsKSA9PiBsICE9PSB1bmRlZmluZWQpOwogICAgICAgICAgbGV0IHJlc3VsdCA9IHsKICAgICAgICAgICAgICBzeW1ib2w6IHRoaXMuc3ltYm9sLAogICAgICAgICAgICAgIGxpbms6IHRoaXMubGluaywKICAgICAgICAgICAgICBzZWN0aW9uOiB0aGlzLnNlY3Rpb24sCiAgICAgICAgICAgICAgdGV4dDogdGhpcy50ZXh0LAogICAgICAgICAgICAgIHRhZ3M6IEFycmF5LmZyb20odGhpcy50YWdzKSwKICAgICAgICAgICAgICBsaW5lOiB0aGlzLmxpbmUsCiAgICAgICAgICAgICAgbGluZUNvdW50OiB0aGlzLmxpbmVDb3VudCwKICAgICAgICAgICAgICBsaXN0OiB0aGlzLmxpc3QsCiAgICAgICAgICAgICAgb3V0bGlua3M6IEFycmF5LmZyb20odGhpcy5saW5rcyksCiAgICAgICAgICAgICAgcGF0aDogdGhpcy5saW5rLnBhdGgsCiAgICAgICAgICAgICAgY2hpbGRyZW46IGNoaWxkcmVuLAogICAgICAgICAgICAgIHRhc2s6ICEhdGhpcy50YXNrLAogICAgICAgICAgICAgIGFubm90YXRlZDogdGhpcy5maWVsZHMuc2l6ZSA+IDAsCiAgICAgICAgICAgICAgcG9zaXRpb246IFZhbHVlcy5kZWVwQ29weSh0aGlzLnBvc2l0aW9uKSwKICAgICAgICAgICAgICBzdWJ0YXNrczogY2hpbGRyZW4sCiAgICAgICAgICAgICAgcmVhbDogISF0aGlzLnRhc2ssCiAgICAgICAgICAgICAgaGVhZGVyOiB0aGlzLnNlY3Rpb24sIC8vIEBkZXByZWNhdGVkLCB1c2UgJ2l0ZW0uc2VjdGlvbicgaW5zdGVhZC4KICAgICAgICAgIH07CiAgICAgICAgICBpZiAodGhpcy5wYXJlbnQgfHwgdGhpcy5wYXJlbnQgPT09IDApCiAgICAgICAgICAgICAgcmVzdWx0LnBhcmVudCA9IHRoaXMucGFyZW50OwogICAgICAgICAgaWYgKHRoaXMuYmxvY2tJZCkKICAgICAgICAgICAgICByZXN1bHQuYmxvY2tJZCA9IHRoaXMuYmxvY2tJZDsKICAgICAgICAgIGFkZEZpZWxkcyh0aGlzLmZpZWxkcywgcmVzdWx0KTsKICAgICAgICAgIGlmICh0aGlzLnRhc2spIHsKICAgICAgICAgICAgICByZXN1bHQuc3RhdHVzID0gdGhpcy50YXNrLnN0YXR1czsKICAgICAgICAgICAgICByZXN1bHQuY2hlY2tlZCA9IHRoaXMudGFzay5jaGVja2VkOwogICAgICAgICAgICAgIHJlc3VsdC5jb21wbGV0ZWQgPSB0aGlzLnRhc2suY29tcGxldGVkOwogICAgICAgICAgICAgIHJlc3VsdC5mdWxseUNvbXBsZXRlZCA9IHRoaXMudGFzay5mdWxseUNvbXBsZXRlZDsKICAgICAgICAgICAgICBsZXQgY3JlYXRlZCA9IHRoaXMuY3JlYXRlZCgpLCBkdWUgPSB0aGlzLmR1ZSgpLCBjb21wbGV0ZWQgPSB0aGlzLmNvbXBsZXRlZCgpLCBzdGFydCA9IHRoaXMuc3RhcnQoKSwgc2NoZWR1bGVkID0gdGhpcy5zY2hlZHVsZWQoKTsKICAgICAgICAgICAgICBpZiAoY3JlYXRlZCkKICAgICAgICAgICAgICAgICAgcmVzdWx0LmNyZWF0ZWQgPSBWYWx1ZXMuZGVlcENvcHkoY3JlYXRlZCk7CiAgICAgICAgICAgICAgaWYgKGR1ZSkKICAgICAgICAgICAgICAgICAgcmVzdWx0LmR1ZSA9IFZhbHVlcy5kZWVwQ29weShkdWUpOwogICAgICAgICAgICAgIGlmIChjb21wbGV0ZWQpCiAgICAgICAgICAgICAgICAgIHJlc3VsdC5jb21wbGV0aW9uID0gVmFsdWVzLmRlZXBDb3B5KGNvbXBsZXRlZCk7CiAgICAgICAgICAgICAgaWYgKHN0YXJ0KQogICAgICAgICAgICAgICAgICByZXN1bHQuc3RhcnQgPSBWYWx1ZXMuZGVlcENvcHkoc3RhcnQpOwogICAgICAgICAgICAgIGlmIChzY2hlZHVsZWQpCiAgICAgICAgICAgICAgICAgIHJlc3VsdC5zY2hlZHVsZWQgPSBWYWx1ZXMuZGVlcENvcHkoc2NoZWR1bGVkKTsKICAgICAgICAgIH0KICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgIH0KICB9CiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCiAgLy8gQ29udmVyc2lvbiAvIFNlcmlhbGl6YXRpb24gVXRpbGl0aWVzIC8vCiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCiAgLyoqIERlLWR1cGxpY2F0ZXMgbGlzdCBpdGVtcyBhY3Jvc3Mgc2VjdGlvbiBtZXRhZGF0YSBhbmQgcGFnZSBtZXRhZGF0YS4gKi8KICBjbGFzcyBMaXN0U2VyaWFsaXphdGlvbkNhY2hlIHsKICAgICAgbGlzdEl0ZW1zOwogICAgICBjYWNoZTsKICAgICAgc2VlbjsKICAgICAgY29uc3RydWN0b3IobGlzdEl0ZW1zKSB7CiAgICAgICAgICB0aGlzLmxpc3RJdGVtcyA9IHt9OwogICAgICAgICAgdGhpcy5jYWNoZSA9IHt9OwogICAgICAgICAgdGhpcy5zZWVuID0gbmV3IFNldCgpOwogICAgICAgICAgZm9yIChsZXQgaXRlbSBvZiBsaXN0SXRlbXMpCiAgICAgICAgICAgICAgdGhpcy5saXN0SXRlbXNbaXRlbS5saW5lXSA9IGl0ZW07CiAgICAgIH0KICAgICAgZ2V0KGxpbmVubykgewogICAgICAgICAgaWYgKGxpbmVubyBpbiB0aGlzLmNhY2hlKQogICAgICAgICAgICAgIHJldHVybiB0aGlzLmNhY2hlW2xpbmVub107CiAgICAgICAgICBlbHNlIGlmICh0aGlzLnNlZW4uaGFzKGxpbmVubykpIHsKICAgICAgICAgICAgICBjb25zb2xlLmxvZyhgRGF0YXZpZXc6IEVuY291bnRlcmVkIGEgY2lyY3VsYXIgbGlzdCAobGluZSBudW1iZXIgJHtsaW5lbm99OyBjaGlsZHJlbiAke3RoaXMubGlzdEl0ZW1zW2xpbmVub10uY2hpbGRyZW4uam9pbigiLCAiKX0pYCk7CiAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgICAgICAgIH0KICAgICAgICAgIHRoaXMuc2Vlbi5hZGQobGluZW5vKTsKICAgICAgICAgIGxldCByZXN1bHQgPSB0aGlzLmxpc3RJdGVtc1tsaW5lbm9dLnNlcmlhbGl6ZSh0aGlzKTsKICAgICAgICAgIHRoaXMuY2FjaGVbbGluZW5vXSA9IHJlc3VsdDsKICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgIH0KICB9CiAgZnVuY3Rpb24gYWRkRmllbGRzKGZpZWxkcywgdGFyZ2V0KSB7CiAgICAgIGZvciAobGV0IFtrZXksIHZhbHVlc10gb2YgZmllbGRzLmVudHJpZXMoKSkgewogICAgICAgICAgaWYgKGtleSBpbiB0YXJnZXQpCiAgICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgICB0YXJnZXRba2V5XSA9IHZhbHVlcy5sZW5ndGggPT0gMSA/IHZhbHVlc1swXSA6IHZhbHVlczsKICAgICAgfQogICAgICByZXR1cm4gdGFyZ2V0OwogIH0KCiAgLyoqIENvbW1vbiB1dGlsaXRpZXMgZm9yIGV4dHJhY3RpbmcgdGFncywgbGlua3MsIGFuZCBvdGhlciBidXNpbmVzcyBmcm9tIG1ldGFkYXRhLiAqLwogIGNvbnN0IFBPVEVOVElBTF9UQUdfTUFUQ0hFUiA9IC8jW15ccyw7XC46IVw/JyJgKClcW1xdXHtcfV0rL2dpdTsKICAvKiogRXh0cmFjdCBhbGwgdGFncyBmcm9tIHRoZSBnaXZlbiBzb3VyY2Ugc3RyaW5nLiAqLwogIGZ1bmN0aW9uIGV4dHJhY3RUYWdzJDEoc291cmNlKSB7CiAgICAgIGxldCByZXN1bHQgPSBuZXcgU2V0KCk7CiAgICAgIGxldCBtYXRjaGVzID0gc291cmNlLm1hdGNoQWxsKFBPVEVOVElBTF9UQUdfTUFUQ0hFUik7CiAgICAgIGZvciAobGV0IG1hdGNoIG9mIG1hdGNoZXMpIHsKICAgICAgICAgIGxldCBwYXJzZWQgPSBFWFBSRVNTSU9OLnRhZy5wYXJzZShtYXRjaFswXSk7CiAgICAgICAgICBpZiAocGFyc2VkLnN0YXR1cykKICAgICAgICAgICAgICByZXN1bHQuYWRkKHBhcnNlZC52YWx1ZSk7CiAgICAgIH0KICAgICAgcmV0dXJuIHJlc3VsdDsKICB9CgogIC8qKiBJbXBvcnRlciBmb3IgbWFya2Rvd24gZG9jdW1lbnRzLiAqLwogIC8qKiBFeHRyYWN0IG1hcmtkb3duIG1ldGFkYXRhIGZyb20gdGhlIGdpdmVuIE9ic2lkaWFuIG1hcmtkb3duIGZpbGUuICovCiAgZnVuY3Rpb24gcGFyc2VQYWdlKHBhdGgsIGNvbnRlbnRzLCBzdGF0LCBtZXRhZGF0YSkgewogICAgICBsZXQgdGFncyA9IG5ldyBTZXQoKTsKICAgICAgbGV0IGFsaWFzZXMgPSBuZXcgU2V0KCk7CiAgICAgIGxldCBmaWVsZHMgPSBuZXcgTWFwKCk7CiAgICAgIGxldCBsaW5rcyA9IFtdOwogICAgICAvLyBGaWxlIHRhZ3MsIGluY2x1ZGluZyBmcm9udC1tYXR0ZXIgYW5kIGluLWZpbGUgdGFncy4KICAgICAgKG1ldGFkYXRhLnRhZ3MgfHwgW10pLmZvckVhY2godCA9PiB0YWdzLmFkZCh0LnRhZy5zdGFydHNXaXRoKCIjIikgPyB0LnRhZyA6ICIjIiArIHQudGFnKSk7CiAgICAgIC8vIEZyb250LW1hdHRlciBmaWxlIHRhZ3MsIGFsaWFzZXMsIEFORCBmcm9udG1hdHRlciBwcm9wZXJ0aWVzLgogICAgICBpZiAobWV0YWRhdGEuZnJvbnRtYXR0ZXIpIHsKICAgICAgICAgIGZvciAobGV0IHRhZyBvZiBleHRyYWN0VGFncyhtZXRhZGF0YS5mcm9udG1hdHRlcikpIHsKICAgICAgICAgICAgICBpZiAoIXRhZy5zdGFydHNXaXRoKCIjIikpCiAgICAgICAgICAgICAgICAgIHRhZyA9ICIjIiArIHRhZzsKICAgICAgICAgICAgICB0YWdzLmFkZCh0YWcpOwogICAgICAgICAgfQogICAgICAgICAgZm9yIChsZXQgYWxpYXMgb2YgZXh0cmFjdEFsaWFzZXMobWV0YWRhdGEuZnJvbnRtYXR0ZXIpIHx8IFtdKQogICAgICAgICAgICAgIGFsaWFzZXMuYWRkKGFsaWFzKTsKICAgICAgICAgIGxldCBmcm9udEZpZWxkcyA9IHBhcnNlRnJvbnRtYXR0ZXIobWV0YWRhdGEuZnJvbnRtYXR0ZXIpOwogICAgICAgICAgZm9yIChsZXQgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGZyb250RmllbGRzKSkgewogICAgICAgICAgICAgIGlmIChrZXkgPT0gInBvc2l0aW9uIikKICAgICAgICAgICAgICAgICAgY29udGludWU7CiAgICAgICAgICAgICAgYWRkSW5saW5lRmllbGQoa2V5LCB2YWx1ZSwgZmllbGRzKTsKICAgICAgICAgIH0KICAgICAgfQogICAgICAvLyBBZGQgZnJvbnRtYXR0ZXIgbGlua3MgdG8gbGlua3MuCiAgICAgIGlmIChtZXRhZGF0YS5mcm9udG1hdHRlckxpbmtzKSB7CiAgICAgICAgICBmb3IgKGxldCByYXdMaW5rIG9mIG1ldGFkYXRhLmZyb250bWF0dGVyTGlua3MgfHwgW10pIHsKICAgICAgICAgICAgICBjb25zdCBsaW5rID0gTGluay5pbmZlcihyYXdMaW5rLmxpbmssIGZhbHNlLCByYXdMaW5rLmRpc3BsYXlUZXh0KTsKICAgICAgICAgICAgICBsaW5rcy5wdXNoKGxpbmspOwogICAgICAgICAgfQogICAgICB9CiAgICAgIC8vIExpbmtzIGluIG1ldGFkYXRhLgogICAgICBjb25zdCBsaW5rc0J5TGluZSA9IHt9OwogICAgICBmb3IgKGxldCByYXdMaW5rIG9mIG1ldGFkYXRhLmxpbmtzIHx8IFtdKSB7CiAgICAgICAgICBjb25zdCBsaW5rID0gTGluay5pbmZlcihyYXdMaW5rLmxpbmssIGZhbHNlLCByYXdMaW5rLmRpc3BsYXlUZXh0KTsKICAgICAgICAgIGNvbnN0IGxpbmUgPSByYXdMaW5rLnBvc2l0aW9uLnN0YXJ0LmxpbmU7CiAgICAgICAgICBsaW5rcy5wdXNoKGxpbmspOwogICAgICAgICAgaWYgKCEobGluZSBpbiBsaW5rc0J5TGluZSkpCiAgICAgICAgICAgICAgbGlua3NCeUxpbmVbbGluZV0gPSBbbGlua107CiAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgbGlua3NCeUxpbmVbbGluZV0ucHVzaChsaW5rKTsKICAgICAgfQogICAgICAvLyBFbWJlZCBMaW5rcyBpbiBtZXRhZGF0YS4KICAgICAgZm9yIChsZXQgcmF3RW1iZWQgb2YgbWV0YWRhdGEuZW1iZWRzIHx8IFtdKSB7CiAgICAgICAgICBjb25zdCBsaW5rID0gTGluay5pbmZlcihyYXdFbWJlZC5saW5rLCB0cnVlLCByYXdFbWJlZC5kaXNwbGF5VGV4dCk7CiAgICAgICAgICBjb25zdCBsaW5lID0gcmF3RW1iZWQucG9zaXRpb24uc3RhcnQubGluZTsKICAgICAgICAgIGxpbmtzLnB1c2gobGluayk7CiAgICAgICAgICBpZiAoIShsaW5lIGluIGxpbmtzQnlMaW5lKSkKICAgICAgICAgICAgICBsaW5rc0J5TGluZVtsaW5lXSA9IFtsaW5rXTsKICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICBsaW5rc0J5TGluZVtsaW5lXS5wdXNoKGxpbmspOwogICAgICB9CiAgICAgIC8vIE1lcmdlIGZyb250bWF0dGVyIGZpZWxkcyB3aXRoIHBhcnNlZCBmaWVsZHMuCiAgICAgIGxldCBtYXJrZG93bkRhdGEgPSBwYXJzZU1hcmtkb3duKHBhdGgsIGNvbnRlbnRzLnNwbGl0KCJcbiIpLCBtZXRhZGF0YSwgbGlua3NCeUxpbmUpOwogICAgICBtZXJnZUZpZWxkR3JvdXBzKGZpZWxkcywgbWFya2Rvd25EYXRhLmZpZWxkcyk7CiAgICAgIC8vIFN0cmlwICJwb3NpdGlvbiIgZnJvbSBmcm9udG1hdHRlciBzaW5jZSBpdCBpcyBPYnNpZGlhbiBkZXRlcm1pbmVkLgogICAgICBjb25zdCBmcm9udG1hdHRlciA9IG1ldGFkYXRhLmZyb250bWF0dGVyIHx8IHt9OwogICAgICBpZiAoZnJvbnRtYXR0ZXIgJiYgInBvc2l0aW9uIiBpbiBmcm9udG1hdHRlcikKICAgICAgICAgIGRlbGV0ZSBmcm9udG1hdHRlclsicG9zaXRpb24iXTsKICAgICAgcmV0dXJuIG5ldyBQYWdlTWV0YWRhdGEocGF0aCwgewogICAgICAgICAgdGFncywKICAgICAgICAgIGFsaWFzZXMsCiAgICAgICAgICBsaW5rcywKICAgICAgICAgIGxpc3RzOiBtYXJrZG93bkRhdGEubGlzdHMsCiAgICAgICAgICBmaWVsZHM6IGZpbmFsaXplSW5saW5lRmllbGRzKGZpZWxkcyksCiAgICAgICAgICBmcm9udG1hdHRlcjogZnJvbnRtYXR0ZXIsCiAgICAgICAgICBjdGltZTogRGF0ZVRpbWUuZnJvbU1pbGxpcyhzdGF0LmN0aW1lKSwKICAgICAgICAgIG10aW1lOiBEYXRlVGltZS5mcm9tTWlsbGlzKHN0YXQubXRpbWUpLAogICAgICAgICAgc2l6ZTogc3RhdC5zaXplLAogICAgICAgICAgZGF5OiBmaW5kRGF0ZShwYXRoLCBmaWVsZHMpLAogICAgICB9KTsKICB9CiAgLyoqIEV4dHJhY3QgdGFncyBpbnRlbGxpZ2VudGx5IGZyb20gZnJvbnRtYXR0ZXIuIEhhbmRsZXMgYXJyYXlzLCBudW1iZXJzLCBhbmQgc3RyaW5ncy4gKi8KICBmdW5jdGlvbiBleHRyYWN0VGFncyhtZXRhZGF0YSkgewogICAgICBsZXQgdGFnS2V5cyA9IE9iamVjdC5rZXlzKG1ldGFkYXRhKS5maWx0ZXIodCA9PiB0LnRvTG93ZXJDYXNlKCkgPT0gInRhZ3MiIHx8IHQudG9Mb3dlckNhc2UoKSA9PSAidGFnIik7CiAgICAgIHJldHVybiB0YWdLZXlzCiAgICAgICAgICAubWFwKGsgPT4gc3BsaXRGcm9udG1hdHRlclRhZ09yQWxpYXMobWV0YWRhdGFba10sIC9bLFxzXSsvKSkKICAgICAgICAgIC5yZWR1Y2UoKHAsIGMpID0+IHAuY29uY2F0KGMpLCBbXSkKICAgICAgICAgIC5tYXAoc3RyID0+IChzdHIuc3RhcnRzV2l0aCgiIyIpID8gc3RyIDogIiMiICsgc3RyKSk7CiAgfQogIC8qKiBFeHRyYWN0IGFsaWFzZXMgaW50ZWxsaWdlbnRseSBmcm9tIGZyb250bWF0dGVyLiBIYW5kbGVzIGFycmF5cywgbnVtYmVycywgYW5kIHN0cmluZ3MuICAqLwogIGZ1bmN0aW9uIGV4dHJhY3RBbGlhc2VzKG1ldGFkYXRhKSB7CiAgICAgIGxldCBhbGlhc0tleXMgPSBPYmplY3Qua2V5cyhtZXRhZGF0YSkuZmlsdGVyKHQgPT4gdC50b0xvd2VyQ2FzZSgpID09ICJhbGlhcyIgfHwgdC50b0xvd2VyQ2FzZSgpID09ICJhbGlhc2VzIik7CiAgICAgIGNvbnN0IHJlc3VsdCA9IFtdOwogICAgICBmb3IgKGxldCBrZXkgb2YgYWxpYXNLZXlzKSB7CiAgICAgICAgICBjb25zdCB2YWx1ZSA9IG1ldGFkYXRhW2tleV07CiAgICAgICAgICBpZiAoIXZhbHVlKQogICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKQogICAgICAgICAgICAgIHJlc3VsdC5wdXNoKC4uLnZhbHVlLm1hcCh2ID0+ICgiIiArIHYpLnRyaW0oKSkpOwogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIHJlc3VsdC5wdXNoKC4uLnNwbGl0RnJvbnRtYXR0ZXJUYWdPckFsaWFzKHZhbHVlLCAvLC8pKTsKICAgICAgfQogICAgICByZXR1cm4gcmVzdWx0OwogIH0KICAvKiogU3BsaXQgYSBmcm9udG1hdHRlciBsaXN0IGludG8gc2VwYXJhdGUgZWxlbWVudHM7IGhhbmRsZXMgYWN0dWFsIGxpc3RzLCBjb21tYSBzZXBhcmF0ZWQgbGlzdHMsIGFuZCBzaW5nbGUgZWxlbWVudHMuICovCiAgZnVuY3Rpb24gc3BsaXRGcm9udG1hdHRlclRhZ09yQWxpYXMoZGF0YSwgb24pIHsKICAgICAgaWYgKGRhdGEgPT0gbnVsbCB8fCBkYXRhID09IHVuZGVmaW5lZCkKICAgICAgICAgIHJldHVybiBbXTsKICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZGF0YSkpIHsKICAgICAgICAgIHJldHVybiBkYXRhCiAgICAgICAgICAgICAgLmZpbHRlcihzID0+ICEhcykKICAgICAgICAgICAgICAubWFwKHMgPT4gc3BsaXRGcm9udG1hdHRlclRhZ09yQWxpYXMocywgb24pKQogICAgICAgICAgICAgIC5yZWR1Y2UoKHAsIGMpID0+IHAuY29uY2F0KGMpLCBbXSk7CiAgICAgIH0KICAgICAgLy8gRm9yY2UgdG8gYSBzdHJpbmcgdG8gaGFuZGxlIG51bWJlcnMgYW5kIHNvIG9uLgogICAgICByZXR1cm4gKCIiICsgZGF0YSkKICAgICAgICAgIC5zcGxpdChvbikKICAgICAgICAgIC5maWx0ZXIodCA9PiAhIXQpCiAgICAgICAgICAubWFwKHQgPT4gdC50cmltKCkpCiAgICAgICAgICAuZmlsdGVyKHQgPT4gdC5sZW5ndGggPiAwKTsKICB9CiAgLyoqIFBhcnNlIHJhdyAobmV3bGluZS1kZWxpbWl0ZWQpIG1hcmtkb3duLCByZXR1cm5pbmcgaW5saW5lIGZpZWxkcywgbGlzdCBpdGVtcywgYW5kIG90aGVyIG1ldGFkYXRhLiAqLwogIGZ1bmN0aW9uIHBhcnNlTWFya2Rvd24ocGF0aCwgY29udGVudHMsIG1ldGFkYXRhLCBsaW5rc0J5TGluZSkgewogICAgICBsZXQgZmllbGRzID0gbmV3IE1hcCgpOwogICAgICAvLyBFeHRyYWN0IHRhc2sgZGF0YSBhbmQgYXBwZW5kIHRoZSBnbG9iYWwgZGF0YSBleHRyYWN0ZWQgZnJvbSB0aGVtIHRvIG91ciBmaWVsZHMuCiAgICAgIGxldCBbbGlzdHMsIGV4dHJhRGF0YV0gPSBwYXJzZUxpc3RzKHBhdGgsIGNvbnRlbnRzLCBtZXRhZGF0YSwgbGlua3NCeUxpbmUpOwogICAgICBmb3IgKGxldCBba2V5LCB2YWx1ZXNdIG9mIGV4dHJhRGF0YS5lbnRyaWVzKCkpIHsKICAgICAgICAgIGlmICghZmllbGRzLmhhcyhrZXkpKQogICAgICAgICAgICAgIGZpZWxkcy5zZXQoa2V5LCB2YWx1ZXMpOwogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIGZpZWxkcy5zZXQoa2V5LCBmaWVsZHMuZ2V0KGtleSkuY29uY2F0KHZhbHVlcykpOwogICAgICB9CiAgICAgIC8vIFRoZSBPYnNpZGlhbiBtZXRhZGF0YSBjYWNoZSB3aWxsIHRyYWNrIGxpc3QgZWxlbWVudHMgaW5zaWRlIG9mIG90aGVyIGVsZW1lbnQgZ3JvdXBzIChsaWtlIGFubm90YXRpb25zIGFuZAogICAgICAvLyBjYWxsb3V0cykuLi4gdGhpcyBtZWFucyB3ZSBtaWdodCBzZWUgbWV0YWRhdGEgdHdpY2UsIHNvIHNraXAgdGhlbSBub3cuIFZlcnkgYW5ub3lpbmcuCiAgICAgIGNvbnN0IGxpc3RMaW5lc1RvU2tpcCA9IG5ldyBTZXQoKTsKICAgICAgZm9yIChjb25zdCBsaW5lIG9mIGxpc3RzKSB7CiAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxpbmUubGluZUNvdW50OyBpKyspCiAgICAgICAgICAgICAgbGlzdExpbmVzVG9Ta2lwLmFkZChsaW5lLmxpbmUgKyBpKTsKICAgICAgfQogICAgICAvLyBPbmx5IHBhcnNlIGhlYWRpbmcgYW5kIHBhcmFncmFwaCBlbGVtZW50cyBmb3IgaW5saW5lIGZpZWxkczsgd2Ugd2lsbCBwYXJzZSBsaXN0IG1ldGFkYXRhIHNlcGFyYXRlbHkuCiAgICAgIGZvciAobGV0IHNlY3Rpb24gb2YgbWV0YWRhdGEuc2VjdGlvbnMgfHwgW10pIHsKICAgICAgICAgIGlmIChzZWN0aW9uLnR5cGUgPT0gImxpc3QiIHx8IHNlY3Rpb24udHlwZSA9PSAicnVsaW5nIikKICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgIGZvciAobGV0IGxpbmVubyA9IHNlY3Rpb24ucG9zaXRpb24uc3RhcnQubGluZTsgbGluZW5vIDw9IHNlY3Rpb24ucG9zaXRpb24uZW5kLmxpbmU7IGxpbmVubysrKSB7CiAgICAgICAgICAgICAgbGV0IGxpbmUgPSBjb250ZW50c1tsaW5lbm9dOwogICAgICAgICAgICAgIGlmIChsaW5lID09IHVuZGVmaW5lZCB8fCBsaW5lID09IG51bGwpCiAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgIGlmIChsaXN0TGluZXNUb1NraXAuaGFzKGxpbmVubykpCiAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgIC8vIEZhc3QgYmFpbC1vdXQgZm9yIGxpbmVzIHRoYXQgYXJlIHRvbyBsb25nIG9yIGRvIG5vdCBjb250YWluICc6OicuCiAgICAgICAgICAgICAgaWYgKGxpbmUubGVuZ3RoID4gMzI3NjggfHwgIWxpbmUuaW5jbHVkZXMoIjo6IikpCiAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgICAgIGxpbmUgPSBsaW5lLnRyaW0oKTsKICAgICAgICAgICAgICBsZXQgaW5saW5lRmllbGRzID0gZXh0cmFjdElubGluZUZpZWxkcyhsaW5lKTsKICAgICAgICAgICAgICBpZiAoaW5saW5lRmllbGRzLmxlbmd0aCA+IDApIHsKICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaWZpZWxkIG9mIGlubGluZUZpZWxkcykKICAgICAgICAgICAgICAgICAgICAgIGFkZFJhd0lubGluZUZpZWxkKGlmaWVsZCwgZmllbGRzKTsKICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgZWxzZSB7CiAgICAgICAgICAgICAgICAgIGxldCBmdWxsTGluZSA9IGV4dHJhY3RGdWxsTGluZUZpZWxkKGxpbmUpOwogICAgICAgICAgICAgICAgICBpZiAoZnVsbExpbmUpCiAgICAgICAgICAgICAgICAgICAgICBhZGRSYXdJbmxpbmVGaWVsZChmdWxsTGluZSwgZmllbGRzKTsKICAgICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgIH0KICAgICAgcmV0dXJuIHsgZmllbGRzLCBsaXN0cyB9OwogIH0KICAvLyBUT0RPOiBDb25zaWRlciB1c2luZyBhbiBhY3R1YWwgcGFyc2VyIGluIGxpZXUgb2YgYSBtb3JlIGV4cGVuc2l2ZSByZWdleC4KICBjb25zdCBMSVNUX0lURU1fUkVHRVggPSAvXltccz5dKihcZCtcLnxcZCtcKXxcKnwtfFwrKVxzKihcWy57MCwxfVxdKT9ccyooLiopJC9tdTsKICAvKioKICAgKiBQYXJzZSBsaXN0IGl0ZW1zIGZyb20gdGhlIHBhZ2UgKyBtZXRhZGF0YS4gVGhpcyByZXF1aXJlcyBzb21lIGFkZGl0aW9uYWwgcGFyc2luZyBhYm92ZSB3aGF0ZXZlciBPYnNpZGlhbiBwcm92aWRlcywKICAgKiBzaW5jZSBPYnNpZGlhbiBvbmx5IGdpdmVzIGxpbmUgbnVtYmVycy4KICAgKi8KICBmdW5jdGlvbiBwYXJzZUxpc3RzKHBhdGgsIGNvbnRlbnQsIG1ldGFkYXRhLCBsaW5rc0J5TGluZSkgewogICAgICBsZXQgY2FjaGUgPSB7fTsKICAgICAgLy8gUGxhY2UgYWxsIG9mIHRoZSB2YWx1ZXMgaW4gdGhlIGNhY2hlIGJlZm9yZSByZXNvbHZpbmcgY2hpbGRyZW4gJiBtZXRhZGF0YSByZWxhdGlvbnNoaXBzLgogICAgICBmb3IgKGxldCByYXdFbGVtZW50IG9mIG1ldGFkYXRhLmxpc3RJdGVtcyB8fCBbXSkgewogICAgICAgICAgLy8gTWF0Y2ggb24gdGhlIGZpcnN0IGxpbmUgdG8gZ2V0IHRoZSBzeW1ib2wgYW5kIGZpcnN0IGxpbmUgb2YgdGV4dC4KICAgICAgICAgIGxldCByYXdNYXRjaCA9IExJU1RfSVRFTV9SRUdFWC5leGVjKGNvbnRlbnRbcmF3RWxlbWVudC5wb3NpdGlvbi5zdGFydC5saW5lXSk7CiAgICAgICAgICBpZiAoIXJhd01hdGNoKQogICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgLy8gQW5kIHRoZW4gc3RyaXAgdW5uZWNlc3Nhcnkgc3BhY2luZyBmcm9tIHRoZSByZW1haW5pbmcgbGluZXMuCiAgICAgICAgICBsZXQgdGV4dFBhcnRzID0gW3Jhd01hdGNoWzNdXQogICAgICAgICAgICAgIC5jb25jYXQoY29udGVudC5zbGljZShyYXdFbGVtZW50LnBvc2l0aW9uLnN0YXJ0LmxpbmUgKyAxLCByYXdFbGVtZW50LnBvc2l0aW9uLmVuZC5saW5lICsgMSkpCiAgICAgICAgICAgICAgLm1hcCh0ID0+IHQudHJpbSgpKTsKICAgICAgICAgIGxldCB0ZXh0V2l0aE5ld2xpbmUgPSB0ZXh0UGFydHMuam9pbigiXG4iKTsKICAgICAgICAgIGxldCB0ZXh0Tm9OZXdsaW5lID0gdGV4dFBhcnRzLmpvaW4oIiAiKTsKICAgICAgICAgIC8vIEZpbmQgdGhlIGxpc3QgdGhhdCB3ZSBhcmUgYSBwYXJ0IG9mIGJ5IGxpbmUuCiAgICAgICAgICBsZXQgY29udGFpbmluZ0xpc3RJZCA9IChtZXRhZGF0YS5zZWN0aW9ucyB8fCBbXSkuZmluZEluZGV4KHMgPT4gcy50eXBlID09ICJsaXN0IiAmJgogICAgICAgICAgICAgIHMucG9zaXRpb24uc3RhcnQubGluZSA8PSByYXdFbGVtZW50LnBvc2l0aW9uLnN0YXJ0LmxpbmUgJiYKICAgICAgICAgICAgICBzLnBvc2l0aW9uLmVuZC5saW5lID49IHJhd0VsZW1lbnQucG9zaXRpb24uc3RhcnQubGluZSk7CiAgICAgICAgICAvLyBGaW5kIHRoZSBzZWN0aW9uIHdlIGJlbG9uZyB0byBhcyB3ZWxsLgogICAgICAgICAgbGV0IHNlY3Rpb25OYW1lID0gZmluZFByZXZpb3VzSGVhZGVyKHJhd0VsZW1lbnQucG9zaXRpb24uc3RhcnQubGluZSwgbWV0YWRhdGEuaGVhZGluZ3MgfHwgW10pOwogICAgICAgICAgbGV0IHNlY3Rpb25MaW5rID0gc2VjdGlvbk5hbWUgPT09IHVuZGVmaW5lZCA/IExpbmsuZmlsZShwYXRoKSA6IExpbmsuaGVhZGVyKHBhdGgsIHNlY3Rpb25OYW1lKTsKICAgICAgICAgIGxldCBjbG9zZXN0TGluayA9IHJhd0VsZW1lbnQuaWQgPT09IHVuZGVmaW5lZCA/IHNlY3Rpb25MaW5rIDogTGluay5ibG9jayhwYXRoLCByYXdFbGVtZW50LmlkKTsKICAgICAgICAgIC8vIEdhdGhlciBhbnkgbGlua3MgdGhhdCBvY2N1ciBvbiB0aGUgc2FtZSBsaW5lcyBhcyB0aGUgdGFzay4KICAgICAgICAgIGNvbnN0IGxpbmtzID0gW107CiAgICAgICAgICBmb3IgKGxldCBsaW5lID0gcmF3RWxlbWVudC5wb3NpdGlvbi5zdGFydC5saW5lOyBsaW5lIDw9IHJhd0VsZW1lbnQucG9zaXRpb24uZW5kLmxpbmU7IGxpbmUrKykgewogICAgICAgICAgICAgIGlmIChsaW5rc0J5TGluZVtsaW5lXSkKICAgICAgICAgICAgICAgICAgbGlua3MucHVzaCguLi5saW5rc0J5TGluZVtsaW5lXSk7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBDb25zdHJ1Y3QgdW5pdmVyc2FsIGluZm9ybWF0aW9uIGFib3V0IHRoaXMgZWxlbWVudCAoYmVmb3JlIHRhc2tzKS4KICAgICAgICAgIGxldCBpdGVtID0gbmV3IExpc3RJdGVtKHsKICAgICAgICAgICAgICBzeW1ib2w6IHJhd01hdGNoWzFdLAogICAgICAgICAgICAgIGxpbms6IGNsb3Nlc3RMaW5rLAogICAgICAgICAgICAgIGxpbmtzOiBsaW5rcywKICAgICAgICAgICAgICBzZWN0aW9uOiBzZWN0aW9uTGluaywKICAgICAgICAgICAgICB0ZXh0OiB0ZXh0V2l0aE5ld2xpbmUsCiAgICAgICAgICAgICAgdGFnczogZXh0cmFjdFRhZ3MkMSh0ZXh0Tm9OZXdsaW5lKSwKICAgICAgICAgICAgICBsaW5lOiByYXdFbGVtZW50LnBvc2l0aW9uLnN0YXJ0LmxpbmUsCiAgICAgICAgICAgICAgbGluZUNvdW50OiByYXdFbGVtZW50LnBvc2l0aW9uLmVuZC5saW5lIC0gcmF3RWxlbWVudC5wb3NpdGlvbi5zdGFydC5saW5lICsgMSwKICAgICAgICAgICAgICBsaXN0OiBjb250YWluaW5nTGlzdElkID09IC0xID8gLTEgOiAobWV0YWRhdGEuc2VjdGlvbnMgfHwgW10pW2NvbnRhaW5pbmdMaXN0SWRdLnBvc2l0aW9uLnN0YXJ0LmxpbmUsCiAgICAgICAgICAgICAgcG9zaXRpb246IHJhd0VsZW1lbnQucG9zaXRpb24sCiAgICAgICAgICAgICAgY2hpbGRyZW46IFtdLAogICAgICAgICAgICAgIGJsb2NrSWQ6IHJhd0VsZW1lbnQuaWQsCiAgICAgICAgICB9KTsKICAgICAgICAgIGlmIChyYXdFbGVtZW50LnBhcmVudCA+PSAwICYmIHJhd0VsZW1lbnQucGFyZW50ICE9IGl0ZW0ubGluZSkKICAgICAgICAgICAgICBpdGVtLnBhcmVudCA9IHJhd0VsZW1lbnQucGFyZW50OwogICAgICAgICAgLy8gU2V0IHVwIHRoZSBiYXNpYyB0YXNrIGluZm9ybWF0aW9uIGZvciBub3csIHRob3VnaCB3ZSBoYXZlIHRvIHJlY29tcHV0ZSBgZnVsbHlDb21wdXRlZGAgbGF0ZXIuCiAgICAgICAgICBpZiAocmF3RWxlbWVudC50YXNrKSB7CiAgICAgICAgICAgICAgaXRlbS50YXNrID0gewogICAgICAgICAgICAgICAgICBzdGF0dXM6IHJhd0VsZW1lbnQudGFzaywKICAgICAgICAgICAgICAgICAgY2hlY2tlZDogcmF3RWxlbWVudC50YXNrICE9ICIiICYmIHJhd0VsZW1lbnQudGFzayAhPSAiICIsCiAgICAgICAgICAgICAgICAgIGNvbXBsZXRlZDogcmF3RWxlbWVudC50YXNrID09ICJYIiB8fCByYXdFbGVtZW50LnRhc2sgPT0gIngiLAogICAgICAgICAgICAgICAgICBmdWxseUNvbXBsZXRlZDogcmF3RWxlbWVudC50YXNrID09ICJYIiB8fCByYXdFbGVtZW50LnRhc2sgPT0gIngiLAogICAgICAgICAgICAgIH07CiAgICAgICAgICB9CiAgICAgICAgICAvLyBFeHRyYWN0IGlubGluZSBmaWVsZHM7IGV4dHJhY3QgZnVsbC1saW5lIGZpZWxkcyBvbmx5IGlmIHdlIGFyZSBOT1QgYSB0YXNrLgogICAgICAgICAgaXRlbS5maWVsZHMgPSBuZXcgTWFwKCk7CiAgICAgICAgICBmb3IgKGxldCBlbGVtZW50IG9mIGV4dHJhY3RJbmxpbmVGaWVsZHModGV4dE5vTmV3bGluZSwgdHJ1ZSkpCiAgICAgICAgICAgICAgYWRkUmF3SW5saW5lRmllbGQoZWxlbWVudCwgaXRlbS5maWVsZHMpOwogICAgICAgICAgaWYgKCFyYXdFbGVtZW50LnRhc2sgJiYgaXRlbS5maWVsZHMuc2l6ZSA9PSAwKSB7CiAgICAgICAgICAgICAgbGV0IGZ1bGxMaW5lID0gZXh0cmFjdEZ1bGxMaW5lRmllbGQodGV4dE5vTmV3bGluZSk7CiAgICAgICAgICAgICAgaWYgKGZ1bGxMaW5lKQogICAgICAgICAgICAgICAgICBhZGRSYXdJbmxpbmVGaWVsZChmdWxsTGluZSwgaXRlbS5maWVsZHMpOwogICAgICAgICAgfQogICAgICAgICAgY2FjaGVbaXRlbS5saW5lXSA9IGl0ZW07CiAgICAgIH0KICAgICAgLy8gVHJlZSB1cGRhdGluZyBwYXNzZXMuIFVwZGF0ZSBjaGlsZCBsaXN0cy4gUHJvcGFnYXRlIG1ldGFkYXRhIHVwIHRvIHBhcmVudCB0YXNrcy4gVXBkYXRlIHRhc2sgYGZ1bGx5Q29tcGxldGVkYC4KICAgICAgbGV0IGxpdGVyYWxzID0gbmV3IE1hcCgpOwogICAgICBmb3IgKGxldCBsaXN0SXRlbSBvZiBPYmplY3QudmFsdWVzKGNhY2hlKSkgewogICAgICAgICAgLy8gUGFzcyAxOiBVcGRhdGUgY2hpbGQgbGlzdHMuCiAgICAgICAgICBpZiAobGlzdEl0ZW0ucGFyZW50ICE9PSB1bmRlZmluZWQgJiYgbGlzdEl0ZW0ucGFyZW50IGluIGNhY2hlKSB7CiAgICAgICAgICAgICAgbGV0IHBhcmVudCA9IGNhY2hlW2xpc3RJdGVtLnBhcmVudF07CiAgICAgICAgICAgICAgcGFyZW50LmNoaWxkcmVuLnB1c2gobGlzdEl0ZW0ubGluZSk7CiAgICAgICAgICB9CiAgICAgICAgICAvLyBQYXNzIDI6IFByb3BhZ2F0ZSBtZXRhZGF0YSB1cCB0byB0aGUgcGFyZW50IHRhc2sgb3Igcm9vdCBlbGVtZW50LgogICAgICAgICAgaWYgKCFsaXN0SXRlbS50YXNrKSB7CiAgICAgICAgICAgICAgbWVyZ2VGaWVsZEdyb3VwcyhsaXRlcmFscywgbGlzdEl0ZW0uZmllbGRzKTsKICAgICAgICAgICAgICAvLyBUT0RPIChibGFja3NtaXRoZ3UpOiBUaGUgYmVsb3cgY29kZSBwcm9wZXJseSBQcm9wYWdhdGVzIG1ldGFkYXRhIHVwIHRvIHRoZSBuZWFyZXN0IHRhc2ssIHdoaWNoIGlzIHRoZQogICAgICAgICAgICAgIC8vIG1vcmUgaW50dWl0aXZlIGJlaGF2aW9yLiBGb3Igbm93LCB0aG91Z2gsIHdlIHdpbGwga2VlcCB0aGUgZXhpc3RpbmcgbG9naWMuCiAgICAgICAgICAgICAgLyoKICAgICAgICAgICAgICBsZXQgcm9vdDogTGlzdEl0ZW0gfCB1bmRlZmluZWQgPSBsaXN0SXRlbTsKICAgICAgICAgICAgICB3aGlsZSAoISFyb290ICYmICFyb290LnRhc2spIHJvb3QgPSBjYWNoZVtyb290LnBhcmVudCA/PyAtMV07CgogICAgICAgICAgICAgIC8vIElmIHRoZSByb290IGlzIG51bGwsIGFwcGVuZCB0aGlzIG1ldGFkYXRhIHRvIHRoZSByb290OyBvdGhlcndpc2UsIGFwcGVuZCB0byB0aGUgdGFzay4KICAgICAgICAgICAgICBtZXJnZUZpZWxkR3JvdXBzKHJvb3QgPT09IHVuZGVmaW5lZCB8fCByb290ID09IG51bGwgPyBsaXRlcmFscyA6IHJvb3QuZmllbGRzLCBsaXN0SXRlbS5maWVsZHMpOwogICAgICAgICAgICAgICovCiAgICAgICAgICB9CiAgICAgICAgICAvLyBQYXNzIDM6IFByb3BhZ2F0ZSBgZnVsbHlDb21wbGV0ZWRgIHVwIHRoZSB0YXNrIHRyZWUuIFRoaXMgaXMgYSBsaXR0bGUgbGVzcyBlZmZpY2llbnQgdGhhbiBqdXN0IGRvaW5nIGEgc2ltcGxlCiAgICAgICAgICAvLyBERlMgdXNpbmcgdGhlIGNoaWxkcmVuIElEcywgYnV0IGl0J3MgcHJvYmFibHkgZmluZS4KICAgICAgICAgIGlmIChsaXN0SXRlbS50YXNrKSB7CiAgICAgICAgICAgICAgbGV0IGN1cnIgPSBsaXN0SXRlbTsKICAgICAgICAgICAgICB3aGlsZSAoISFjdXJyKSB7CiAgICAgICAgICAgICAgICAgIGlmIChjdXJyLnRhc2spCiAgICAgICAgICAgICAgICAgICAgICBjdXJyLnRhc2suZnVsbHlDb21wbGV0ZWQgPSBjdXJyLnRhc2suZnVsbHlDb21wbGV0ZWQgJiYgbGlzdEl0ZW0udGFzay5jb21wbGV0ZWQ7CiAgICAgICAgICAgICAgICAgIGN1cnIgPSBjYWNoZVtjdXJyLnBhcmVudCA/PyAtMV07CiAgICAgICAgICAgICAgfQogICAgICAgICAgfQogICAgICB9CiAgICAgIHJldHVybiBbT2JqZWN0LnZhbHVlcyhjYWNoZSksIGxpdGVyYWxzXTsKICB9CiAgLyoqIEF0dGVtcHQgdG8gZmluZCBhIGRhdGUgYXNzb2NpYXRlZCB3aXRoIHRoZSBnaXZlbiBwYWdlIGZyb20gbWV0YWRhdGEgb3IgZmlsZW5hbWVzLiAqLwogIGZ1bmN0aW9uIGZpbmREYXRlKGZpbGUsIGZpZWxkcykgewogICAgICBmb3IgKGxldCBrZXkgb2YgZmllbGRzLmtleXMoKSkgewogICAgICAgICAgaWYgKCEoa2V5LnRvTG9jYWxlTG93ZXJDYXNlKCkgPT0gImRhdGUiIHx8IGtleS50b0xvY2FsZUxvd2VyQ2FzZSgpID09ICJkYXkiKSkKICAgICAgICAgICAgICBjb250aW51ZTsKICAgICAgICAgIGxldCB2YWx1ZSA9IGZpZWxkcy5nZXQoa2V5KTsKICAgICAgICAgIGlmIChWYWx1ZXMuaXNEYXRlKHZhbHVlKSkgewogICAgICAgICAgICAgIHJldHVybiB2YWx1ZTsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgaWYgKFZhbHVlcy5pc0FycmF5KHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPiAwICYmIFZhbHVlcy5pc0RhdGUodmFsdWVbMF0pKSB7CiAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlWzBdOwogICAgICAgICAgfQogICAgICAgICAgZWxzZSBpZiAoVmFsdWVzLmlzTGluayh2YWx1ZSkpIHsKICAgICAgICAgICAgICBsZXQgZGF0ZSA9IGV4dHJhY3REYXRlKHZhbHVlLnBhdGgpID8/IGV4dHJhY3REYXRlKHZhbHVlLnN1YnBhdGggPz8gIiIpID8/IGV4dHJhY3REYXRlKHZhbHVlLmRpc3BsYXkgPz8gIiIpOwogICAgICAgICAgICAgIGlmIChkYXRlKQogICAgICAgICAgICAgICAgICByZXR1cm4gZGF0ZTsKICAgICAgICAgIH0KICAgICAgfQogICAgICByZXR1cm4gZXh0cmFjdERhdGUoZ2V0RmlsZVRpdGxlKGZpbGUpKTsKICB9CiAgLyoqIFJlY3Vyc2l2ZWx5IGNvbnZlcnQgZnJvbnRtYXR0ZXIgaW50byBmaWVsZHMuIFdlIGhhdmUgdG8gZGFuY2UgYXJvdW5kIFlBTUwgc3RydWN0dXJlLiAqLwogIGZ1bmN0aW9uIHBhcnNlRnJvbnRtYXR0ZXIodmFsdWUpIHsKICAgICAgaWYgKHZhbHVlID09IG51bGwpIHsKICAgICAgICAgIHJldHVybiBudWxsOwogICAgICB9CiAgICAgIGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gIm9iamVjdCIpIHsKICAgICAgICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkgewogICAgICAgICAgICAgIGxldCByZXN1bHQgPSBbXTsKICAgICAgICAgICAgICBmb3IgKGxldCBjaGlsZCBvZiB2YWx1ZSkgewogICAgICAgICAgICAgICAgICByZXN1bHQucHVzaChwYXJzZUZyb250bWF0dGVyKGNoaWxkKSk7CiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHJldHVybiByZXN1bHQ7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIGlmICh2YWx1ZSBpbnN0YW5jZW9mIERhdGUpIHsKICAgICAgICAgICAgICBsZXQgZGF0ZVBhcnNlID0gRGF0ZVRpbWUuZnJvbUpTRGF0ZSh2YWx1ZSk7CiAgICAgICAgICAgICAgcmV0dXJuIGRhdGVQYXJzZTsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgewogICAgICAgICAgICAgIGxldCBvYmplY3QgPSB2YWx1ZTsKICAgICAgICAgICAgICBsZXQgcmVzdWx0ID0ge307CiAgICAgICAgICAgICAgZm9yIChsZXQga2V5IGluIG9iamVjdCkgewogICAgICAgICAgICAgICAgICByZXN1bHRba2V5XSA9IHBhcnNlRnJvbnRtYXR0ZXIob2JqZWN0W2tleV0pOwogICAgICAgICAgICAgIH0KICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgICAgfQogICAgICB9CiAgICAgIGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gIm51bWJlciIpIHsKICAgICAgICAgIHJldHVybiB2YWx1ZTsKICAgICAgfQogICAgICBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09ICJib29sZWFuIikgewogICAgICAgICAgcmV0dXJuIHZhbHVlOwogICAgICB9CiAgICAgIGVsc2UgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gInN0cmluZyIpIHsKICAgICAgICAgIGxldCBkYXRlUGFyc2UgPSBFWFBSRVNTSU9OLmRhdGUucGFyc2UodmFsdWUpOwogICAgICAgICAgaWYgKGRhdGVQYXJzZS5zdGF0dXMpCiAgICAgICAgICAgICAgcmV0dXJuIGRhdGVQYXJzZS52YWx1ZTsKICAgICAgICAgIGxldCBkdXJhdGlvblBhcnNlID0gRVhQUkVTU0lPTi5kdXJhdGlvbi5wYXJzZSh2YWx1ZSk7CiAgICAgICAgICBpZiAoZHVyYXRpb25QYXJzZS5zdGF0dXMpCiAgICAgICAgICAgICAgcmV0dXJuIGR1cmF0aW9uUGFyc2UudmFsdWU7CiAgICAgICAgICBsZXQgbGlua1BhcnNlID0gRVhQUkVTU0lPTi5lbWJlZExpbmsucGFyc2UodmFsdWUpOwogICAgICAgICAgaWYgKGxpbmtQYXJzZS5zdGF0dXMpCiAgICAgICAgICAgICAgcmV0dXJuIGxpbmtQYXJzZS52YWx1ZTsKICAgICAgICAgIHJldHVybiB2YWx1ZTsKICAgICAgfQogICAgICAvLyBCYWNrdXAgaWYgd2UgZG9uJ3QgdW5kZXJzdGFuZCB0aGUgdHlwZS4KICAgICAgcmV0dXJuIG51bGw7CiAgfQogIC8qKiBBZGQgYSBwYXJzZWQgaW5saW5lIGZpZWxkIHRvIHRoZSBvdXRwdXQgbWFwLiAqLwogIGZ1bmN0aW9uIGFkZFJhd0lubGluZUZpZWxkKGZpZWxkLCBvdXRwdXQpIHsKICAgICAgYWRkSW5saW5lRmllbGQoZmllbGQua2V5LCBwYXJzZUlubGluZVZhbHVlKGZpZWxkLnZhbHVlKSwgb3V0cHV0KTsKICB9CiAgLyoqIEFkZCBhIHJhdyBpbmxpbmUgZmllbGQgdG8gYW4gb3V0cHV0IG1hcCwgY2Fub25pY2FsaXppbmcgYXMgbmVlZGVkLiAqLwogIGZ1bmN0aW9uIGFkZElubGluZUZpZWxkKGtleSwgdmFsdWUsIG91dHB1dCkgewogICAgICBpZiAoIW91dHB1dC5oYXMoa2V5KSkKICAgICAgICAgIG91dHB1dC5zZXQoa2V5LCBbdmFsdWVdKTsKICAgICAgZWxzZQogICAgICAgICAgb3V0cHV0LmdldChrZXkpPy5wdXNoKHZhbHVlKTsKICB9CiAgLyoqIEdpdmVuIGEgcmF3IGxpc3Qgb2YgaW5saW5lIGZpZWxkIHZhbHVlcywgYWRkIG5vcm1hbGl6ZWQga2V5cyBhbmQgc3F1YXNoIHRoZW0uICovCiAgZnVuY3Rpb24gZmluYWxpemVJbmxpbmVGaWVsZHMoZmllbGRzKSB7CiAgICAgIC8vIENvbXB1dGUgdW5pcXVlIG5vcm1hbGl6ZWQga2V5cyAodGhhdCBkbyBub3Qgb3ZlcmxhcCB3LyB0aGUgZmllbGRzKS4KICAgICAgbGV0IG5vcm1hbGl6ZWQgPSBuZXcgTWFwKCk7CiAgICAgIGZvciAobGV0IFtrZXksIHZhbHVlc10gb2YgZmllbGRzLmVudHJpZXMoKSkgewogICAgICAgICAgbGV0IG5vcm1LZXkgPSBjYW5vbmljYWxpemVWYXJOYW1lKGtleSk7CiAgICAgICAgICBpZiAobm9ybUtleSA9PSAiIiB8fCBmaWVsZHMuaGFzKG5vcm1LZXkpKQogICAgICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgICAgaWYgKCFub3JtYWxpemVkLmhhcyhub3JtS2V5KSkKICAgICAgICAgICAgICBub3JtYWxpemVkLnNldChub3JtS2V5LCB2YWx1ZXMpOwogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIG5vcm1hbGl6ZWQuc2V0KG5vcm1LZXksIG5vcm1hbGl6ZWQuZ2V0KG5vcm1LZXkpLmNvbmNhdCh2YWx1ZXMpKTsKICAgICAgfQogICAgICAvLyBDb21iaW5lIG5vcm1hbGl6ZWQgKyBub3JtYWwga2V5cy4KICAgICAgbGV0IGludGVyaW0gPSBuZXcgTWFwKCk7CiAgICAgIG1lcmdlRmllbGRHcm91cHMoaW50ZXJpbSwgZmllbGRzKTsKICAgICAgbWVyZ2VGaWVsZEdyb3VwcyhpbnRlcmltLCBub3JtYWxpemVkKTsKICAgICAgLy8gQW5kIHRoZW4gZmxhdHRlbiB0aGVtLgogICAgICBsZXQgcmVzdWx0ID0gbmV3IE1hcCgpOwogICAgICBmb3IgKGxldCBba2V5LCB2YWx1ZV0gb2YgaW50ZXJpbS5lbnRyaWVzKCkpIHsKICAgICAgICAgIGlmICh2YWx1ZS5sZW5ndGggPT0gMSkKICAgICAgICAgICAgICByZXN1bHQuc2V0KGtleSwgdmFsdWVbMF0pOwogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIHJlc3VsdC5zZXQoa2V5LCB2YWx1ZSk7CiAgICAgIH0KICAgICAgcmV0dXJuIHJlc3VsdDsKICB9CiAgLyoqIENvcHkgYWxsIGZpZWxkcyBvZiAnc291cmNlJyBpbnRvICd0YXJnZXQnLiAqLwogIGZ1bmN0aW9uIG1lcmdlRmllbGRHcm91cHModGFyZ2V0LCBzb3VyY2UpIHsKICAgICAgZm9yIChsZXQga2V5IG9mIHNvdXJjZS5rZXlzKCkpIHsKICAgICAgICAgIGlmICghdGFyZ2V0LmhhcyhrZXkpKQogICAgICAgICAgICAgIHRhcmdldC5zZXQoa2V5LCBzb3VyY2UuZ2V0KGtleSkpOwogICAgICAgICAgZWxzZQogICAgICAgICAgICAgIHRhcmdldC5zZXQoa2V5LCB0YXJnZXQuZ2V0KGtleSkuY29uY2F0KHNvdXJjZS5nZXQoa2V5KSkpOwogICAgICB9CiAgfQogIC8qKiBGaW5kIHRoZSBoZWFkZXIgdGhhdCBpcyBtb3N0IGltbWVkaWF0ZWx5IGFib3ZlIHRoZSBnaXZlbiBsaW5lIG51bWJlci4gKi8KICBmdW5jdGlvbiBmaW5kUHJldmlvdXNIZWFkZXIobGluZSwgaGVhZGVycykgewogICAgICBpZiAoaGVhZGVycy5sZW5ndGggPT0gMCkKICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7CiAgICAgIGlmIChoZWFkZXJzWzBdLnBvc2l0aW9uLnN0YXJ0LmxpbmUgPiBsaW5lKQogICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgICAgbGV0IGluZGV4ID0gaGVhZGVycy5sZW5ndGggLSAxOwogICAgICB3aGlsZSAoaW5kZXggPj0gMCAmJiBoZWFkZXJzW2luZGV4XS5wb3NpdGlvbi5zdGFydC5saW5lID4gbGluZSkKICAgICAgICAgIGluZGV4LS07CiAgICAgIHJldHVybiBoZWFkZXJzW2luZGV4XS5oZWFkaW5nOwogIH0KCiAgLyoqIEFjdHVhbCBpbXBvcnQgaW1wbGVtZW50YXRpb24gYmFja2VuZC4gVGhpcyBtdXN0IHJlbWFpbiBzZXBhcmF0ZSBmcm9tIGBpbXBvcnQtZW50cnlgIHNpbmNlIGl0IGlzIHVzZWQgd2l0aG91dCB3ZWIgd29ya2Vycy4gKi8KICBmdW5jdGlvbiBydW5JbXBvcnQocGF0aCwgY29udGVudHMsIHN0YXRzLCBtZXRhZGF0YSkgewogICAgICByZXR1cm4gcGFyc2VQYWdlKHBhdGgsIGNvbnRlbnRzLCBzdGF0cywgbWV0YWRhdGEpOwogIH0KCiAgLyoqIFNpbXBsaWZpZXMgcGFzc2luZyBkYXRhdmlldyB2YWx1ZXMgYWNyb3NzIHRoZSBKUyB3ZWIgd29ya2VyIGJhcnJpZXIuICovCiAgdmFyIFRyYW5zZmVyYWJsZTsKICAoZnVuY3Rpb24gKFRyYW5zZmVyYWJsZSkgewogICAgICAvKiogQ29udmVydCBhIGxpdGVyYWwgdmFsdWUgdG8gYSBzZXJpYWxpemVyLWZyaWVuZGx5IHRyYW5zZmVyYWJsZSB2YWx1ZS4gKi8KICAgICAgZnVuY3Rpb24gdHJhbnNmZXJhYmxlKHZhbHVlKSB7CiAgICAgICAgICAvLyBIYW5kbGUgc2ltcGxlIHVuaXZlcnNhbCB0eXBlcyBmaXJzdC4KICAgICAgICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIE1hcCkgewogICAgICAgICAgICAgIGxldCBjb3BpZWQgPSBuZXcgTWFwKCk7CiAgICAgICAgICAgICAgZm9yIChsZXQgW2tleSwgdmFsXSBvZiB2YWx1ZS5lbnRyaWVzKCkpCiAgICAgICAgICAgICAgICAgIGNvcGllZC5zZXQodHJhbnNmZXJhYmxlKGtleSksIHRyYW5zZmVyYWJsZSh2YWwpKTsKICAgICAgICAgICAgICByZXR1cm4gY29waWVkOwogICAgICAgICAgfQogICAgICAgICAgZWxzZSBpZiAodmFsdWUgaW5zdGFuY2VvZiBTZXQpIHsKICAgICAgICAgICAgICBsZXQgY29waWVkID0gbmV3IFNldCgpOwogICAgICAgICAgICAgIGZvciAobGV0IHZhbCBvZiB2YWx1ZSkKICAgICAgICAgICAgICAgICAgY29waWVkLmFkZCh0cmFuc2ZlcmFibGUodmFsKSk7CiAgICAgICAgICAgICAgcmV0dXJuIGNvcGllZDsKICAgICAgICAgIH0KICAgICAgICAgIGxldCB3cmFwcGVkID0gVmFsdWVzLndyYXBWYWx1ZSh2YWx1ZSk7CiAgICAgICAgICBpZiAod3JhcHBlZCA9PT0gdW5kZWZpbmVkKQogICAgICAgICAgICAgIHRocm93IEVycm9yKCJVbnJlY29nbml6ZWQgdHJhbnNmZXJhYmxlIHZhbHVlOiAiICsgdmFsdWUpOwogICAgICAgICAgc3dpdGNoICh3cmFwcGVkLnR5cGUpIHsKICAgICAgICAgICAgICBjYXNlICJudWxsIjoKICAgICAgICAgICAgICBjYXNlICJudW1iZXIiOgogICAgICAgICAgICAgIGNhc2UgInN0cmluZyI6CiAgICAgICAgICAgICAgY2FzZSAiYm9vbGVhbiI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB3cmFwcGVkLnZhbHVlOwogICAgICAgICAgICAgIGNhc2UgImRhdGUiOgogICAgICAgICAgICAgICAgICByZXR1cm4gewogICAgICAgICAgICAgICAgICAgICAgIl9fX3RyYW5zZmVyLXR5cGUiOiAiZGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZTogdHJhbnNmZXJhYmxlKHdyYXBwZWQudmFsdWUudG9PYmplY3QoKSksCiAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgem9uZTogd3JhcHBlZC52YWx1ZS56b25lLmVxdWFscyhTeXN0ZW1ab25lLmluc3RhbmNlKSA/IHVuZGVmaW5lZCA6IHdyYXBwZWQudmFsdWUuem9uZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICAgIGNhc2UgImR1cmF0aW9uIjoKICAgICAgICAgICAgICAgICAgcmV0dXJuIHsgIl9fX3RyYW5zZmVyLXR5cGUiOiAiZHVyYXRpb24iLCB2YWx1ZTogdHJhbnNmZXJhYmxlKHdyYXBwZWQudmFsdWUudG9PYmplY3QoKSkgfTsKICAgICAgICAgICAgICBjYXNlICJhcnJheSI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB3cmFwcGVkLnZhbHVlLm1hcCh2ID0+IHRyYW5zZmVyYWJsZSh2KSk7CiAgICAgICAgICAgICAgY2FzZSAibGluayI6CiAgICAgICAgICAgICAgICAgIHJldHVybiB7ICJfX190cmFuc2Zlci10eXBlIjogImxpbmsiLCB2YWx1ZTogdHJhbnNmZXJhYmxlKHdyYXBwZWQudmFsdWUudG9PYmplY3QoKSkgfTsKICAgICAgICAgICAgICBjYXNlICJvYmplY3QiOgogICAgICAgICAgICAgICAgICBsZXQgcmVzdWx0ID0ge307CiAgICAgICAgICAgICAgICAgIGZvciAobGV0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyh3cmFwcGVkLnZhbHVlKSkKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdFtrZXldID0gdHJhbnNmZXJhYmxlKHZhbHVlKTsKICAgICAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgIH0KICAgICAgfQogICAgICBUcmFuc2ZlcmFibGUudHJhbnNmZXJhYmxlID0gdHJhbnNmZXJhYmxlOwogICAgICAvKiogQ29udmVydCBhIHRyYW5zZmVyYWJsZSB2YWx1ZSBiYWNrIHRvIGEgbGl0ZXJhbCB2YWx1ZSB3ZSBjYW4gd29yayB3aXRoLiAqLwogICAgICBmdW5jdGlvbiB2YWx1ZSh0cmFuc2ZlcmFibGUpIHsKICAgICAgICAgIGlmICh0cmFuc2ZlcmFibGUgPT09IG51bGwpIHsKICAgICAgICAgICAgICByZXR1cm4gbnVsbDsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgaWYgKHRyYW5zZmVyYWJsZSA9PT0gdW5kZWZpbmVkKSB7CiAgICAgICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgaWYgKHRyYW5zZmVyYWJsZSBpbnN0YW5jZW9mIE1hcCkgewogICAgICAgICAgICAgIGxldCByZWFsID0gbmV3IE1hcCgpOwogICAgICAgICAgICAgIGZvciAobGV0IFtrZXksIHZhbF0gb2YgdHJhbnNmZXJhYmxlLmVudHJpZXMoKSkKICAgICAgICAgICAgICAgICAgcmVhbC5zZXQodmFsdWUoa2V5KSwgdmFsdWUodmFsKSk7CiAgICAgICAgICAgICAgcmV0dXJuIHJlYWw7CiAgICAgICAgICB9CiAgICAgICAgICBlbHNlIGlmICh0cmFuc2ZlcmFibGUgaW5zdGFuY2VvZiBTZXQpIHsKICAgICAgICAgICAgICBsZXQgcmVhbCA9IG5ldyBTZXQoKTsKICAgICAgICAgICAgICBmb3IgKGxldCB2YWwgb2YgdHJhbnNmZXJhYmxlKQogICAgICAgICAgICAgICAgICByZWFsLmFkZCh2YWx1ZSh2YWwpKTsKICAgICAgICAgICAgICByZXR1cm4gcmVhbDsKICAgICAgICAgIH0KICAgICAgICAgIGVsc2UgaWYgKEFycmF5LmlzQXJyYXkodHJhbnNmZXJhYmxlKSkgewogICAgICAgICAgICAgIHJldHVybiB0cmFuc2ZlcmFibGUubWFwKHYgPT4gdmFsdWUodikpOwogICAgICAgICAgfQogICAgICAgICAgZWxzZSBpZiAodHlwZW9mIHRyYW5zZmVyYWJsZSA9PT0gIm9iamVjdCIpIHsKICAgICAgICAgICAgICBpZiAoIl9fX3RyYW5zZmVyLXR5cGUiIGluIHRyYW5zZmVyYWJsZSkgewogICAgICAgICAgICAgICAgICBzd2l0Y2ggKHRyYW5zZmVyYWJsZVsiX19fdHJhbnNmZXItdHlwZSJdKSB7CiAgICAgICAgICAgICAgICAgICAgICBjYXNlICJkYXRlIjoKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXQgZGF0ZU9wdHMgPSB2YWx1ZSh0cmFuc2ZlcmFibGUub3B0aW9ucyk7CiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGRhdGVEYXRhID0gdmFsdWUodHJhbnNmZXJhYmxlLnZhbHVlKTsKICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gRGF0ZVRpbWUuZnJvbU9iamVjdChkYXRlRGF0YSwgeyB6b25lOiBkYXRlT3B0cy56b25lIH0pOwogICAgICAgICAgICAgICAgICAgICAgY2FzZSAiZHVyYXRpb24iOgogICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBEdXJhdGlvbi5mcm9tT2JqZWN0KHZhbHVlKHRyYW5zZmVyYWJsZS52YWx1ZSkpOwogICAgICAgICAgICAgICAgICAgICAgY2FzZSAibGluayI6CiAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIExpbmsuZnJvbU9iamVjdCh2YWx1ZSh0cmFuc2ZlcmFibGUudmFsdWUpKTsKICAgICAgICAgICAgICAgICAgICAgIGRlZmF1bHQ6CiAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgRXJyb3IoYFVucmVjb2duaXplZCB0cmFuc2ZlciB0eXBlICcke3RyYW5zZmVyYWJsZVsiX19fdHJhbnNmZXItdHlwZSJdfSdgKTsKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIH0KICAgICAgICAgICAgICBsZXQgcmVzdWx0ID0ge307CiAgICAgICAgICAgICAgZm9yIChsZXQgW2tleSwgdmFsXSBvZiBPYmplY3QuZW50cmllcyh0cmFuc2ZlcmFibGUpKQogICAgICAgICAgICAgICAgICByZXN1bHRba2V5XSA9IHZhbHVlKHZhbCk7CiAgICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICAgIH0KICAgICAgICAgIHJldHVybiB0cmFuc2ZlcmFibGU7CiAgICAgIH0KICAgICAgVHJhbnNmZXJhYmxlLnZhbHVlID0gdmFsdWU7CiAgfSkoVHJhbnNmZXJhYmxlIHx8IChUcmFuc2ZlcmFibGUgPSB7fSkpOwoKICAvKiogRW50cnktcG9pbnQgc2NyaXB0IHVzZWQgYnkgdGhlIGluZGV4IGFzIGEgd2ViIHdvcmtlci4gKi8KICAvKiogQW4gaW1wb3J0IHdoaWNoIGNhbiBmYWlsIGFuZCByYWlzZSBhbiBleGNlcHRpb24sIHdoaWNoIHdpbGwgYmUgY2F1Z2h0IGJ5IHRoZSBoYW5kbGVyLiAqLwogIGZ1bmN0aW9uIGZhaWxhYmxlSW1wb3J0KHBhdGgsIGNvbnRlbnRzLCBzdGF0LCBtZXRhZGF0YSkgewogICAgICBpZiAobWV0YWRhdGEgPT09IHVuZGVmaW5lZCB8fCBtZXRhZGF0YSA9PT0gbnVsbCkgewogICAgICAgICAgdGhyb3cgRXJyb3IoYENhbm5vdCBpbmRleCBmaWxlLCBzaW5jZSBpdCBoYXMgbm8gT2JzaWRpYW4gZmlsZSBtZXRhZGF0YS5gKTsKICAgICAgfQogICAgICByZXR1cm4gcnVuSW1wb3J0KHBhdGgsIGNvbnRlbnRzLCBzdGF0LCBtZXRhZGF0YSk7CiAgfQogIG9ubWVzc2FnZSA9IGFzeW5jIChldnQpID0+IHsKICAgICAgdHJ5IHsKICAgICAgICAgIGxldCB7IHBhdGgsIGNvbnRlbnRzLCBzdGF0LCBtZXRhZGF0YSB9ID0gZXZ0LmRhdGE7CiAgICAgICAgICBsZXQgcmVzdWx0ID0gZmFpbGFibGVJbXBvcnQocGF0aCwgY29udGVudHMsIHN0YXQsIG1ldGFkYXRhKTsKICAgICAgICAgIHBvc3RNZXNzYWdlKHsgcGF0aDogZXZ0LmRhdGEucGF0aCwgcmVzdWx0OiBUcmFuc2ZlcmFibGUudHJhbnNmZXJhYmxlKHJlc3VsdCkgfSk7CiAgICAgIH0KICAgICAgY2F0Y2ggKGVycm9yKSB7CiAgICAgICAgICBjb25zb2xlLmxvZyhlcnJvcik7CiAgICAgICAgICBwb3N0TWVzc2FnZSh7CiAgICAgICAgICAgICAgcGF0aDogZXZ0LmRhdGEucGF0aCwKICAgICAgICAgICAgICByZXN1bHQ6IHsKICAgICAgICAgICAgICAgICAgJGVycm9yOiBgRmFpbGVkIHRvIGluZGV4IGZpbGU6ICR7ZXZ0LmRhdGEucGF0aH06ICR7ZXJyb3J9YCwKICAgICAgICAgICAgICB9LAogICAgICAgICAgfSk7CiAgICAgIH0KICB9OwoKfSkoKTsKCg==', null, false);
+/* eslint-enable */
+
+/** Controls and creates Dataview file importers, allowing for asynchronous loading and parsing of files. */
+/** Multi-threaded file parser which debounces rapid file requests automatically. */
+class FileImporter extends obsidian.Component {
+ numWorkers;
+ vault;
+ metadataCache;
+ /* Background workers which do the actual file parsing. */
+ workers;
+ /** Tracks which workers are actively parsing a file, to make sure we properly delegate results. */
+ busy;
+ /** List of files which have been queued for a reload. */
+ reloadQueue;
+ /** Fast-access set which holds the list of files queued to be reloaded; used for debouncing. */
+ reloadSet;
+ /** Paths -> promises for file reloads which have not yet been queued. */
+ callbacks;
+ constructor(numWorkers, vault, metadataCache) {
+ super();
+ this.numWorkers = numWorkers;
+ this.vault = vault;
+ this.metadataCache = metadataCache;
+ this.workers = [];
+ this.busy = [];
+ this.reloadQueue = [];
+ this.reloadSet = new Set();
+ this.callbacks = new Map();
+ for (let index = 0; index < numWorkers; index++) {
+ let worker = new WorkerFactory({ name: "Dataview Indexer " + (index + 1) });
+ worker.onmessage = evt => this.finish(evt.data.path, Transferable.value(evt.data.result), index);
+ this.workers.push(worker);
+ this.register(() => worker.terminate());
+ this.busy.push(false);
+ }
+ }
+ /**
+ * Queue the given file for reloading. Multiple reload requests for the same file in a short time period will be de-bounced
+ * and all be resolved by a single actual file reload.
+ */
+ reload(file) {
+ let promise = new Promise((resolve, reject) => {
+ if (this.callbacks.has(file.path))
+ this.callbacks.get(file.path)?.push([resolve, reject]);
+ else
+ this.callbacks.set(file.path, [[resolve, reject]]);
+ });
+ // De-bounce repeated requests for the same file.
+ if (this.reloadSet.has(file.path))
+ return promise;
+ this.reloadSet.add(file.path);
+ // Immediately run this task if there are available workers; otherwise, add it to the queue.
+ let workerId = this.nextAvailableWorker();
+ if (workerId !== undefined) {
+ this.send(file, workerId);
+ }
+ else {
+ this.reloadQueue.push(file);
+ }
+ return promise;
+ }
+ /** Finish the parsing of a file, potentially queueing a new file. */
+ finish(path, data, index) {
+ // Cache the callbacks before we do book-keeping.
+ let calls = [].concat(this.callbacks.get(path) ?? []);
+ // Book-keeping to clear metadata & allow the file to be re-loaded again.
+ this.reloadSet.delete(path);
+ this.callbacks.delete(path);
+ // Notify the queue this file is available for new work.
+ this.busy[index] = false;
+ // Queue a new job onto this worker.
+ let job = this.reloadQueue.shift();
+ if (job !== undefined)
+ this.send(job, index);
+ // Resolve promises to let users know this file has finished.
+ if ("$error" in data) {
+ for (let [_, reject] of calls)
+ reject(data["$error"]);
+ }
+ else {
+ for (let [callback, _] of calls)
+ callback(data);
+ }
+ }
+ /** Send a new task to the given worker ID. */
+ send(file, workerId) {
+ this.busy[workerId] = true;
+ this.vault.cachedRead(file).then(c => this.workers[workerId].postMessage({
+ path: file.path,
+ contents: c,
+ stat: file.stat,
+ metadata: this.metadataCache.getFileCache(file),
+ }));
+ }
+ /** Find the next available, non-busy worker; return undefined if all workers are busy. */
+ nextAvailableWorker() {
+ let index = this.busy.indexOf(false);
+ return index == -1 ? undefined : index;
+ }
+}
+
+/** Stores various indices on all files in the vault to make dataview generation fast. */
+/** Aggregate index which has several sub-indices and will initialize all of them. */
+class FullIndex extends obsidian.Component {
+ app;
+ indexVersion;
+ onChange;
+ /** Generate a full index from the given vault. */
+ static create(app, indexVersion, onChange) {
+ return new FullIndex(app, indexVersion, onChange);
+ }
+ /** Whether all files in the vault have been indexed at least once. */
+ initialized;
+ /** I/O access to the Obsidian vault contents. */
+ vault;
+ /** Access to in-memory metadata, useful for parsing and metadata lookups. */
+ metadataCache;
+ /** Persistent IndexedDB backing store, used for faster startup. */
+ persister;
+ /* Maps path -> markdown metadata for all markdown pages. */
+ pages;
+ /** Map files -> tags in that file, and tags -> files. This version includes subtags. */
+ tags;
+ /** Map files -> exact tags in that file, and tags -> files. This version does not automatically add subtags. */
+ etags;
+ /** Map files -> linked files in that file, and linked file -> files that link to it. */
+ links;
+ /** Search files by path prefix. */
+ prefix;
+ /** Allows for efficient lookups of whether a file is starred or not. */
+ starred;
+ /** Caches data in CSV files. */
+ // TODO: CSV parsing should be done by a worker thread asynchronously to avoid frontend stalls.
+ csv;
+ /**
+ * The current "revision" of the index, which monotonically increases for every index change. Use this to determine
+ * if you are up to date.
+ */
+ revision;
+ /** Asynchronously parses files in the background using web workers. */
+ importer;
+ /** Construct a new index using the app data and a current data version. */
+ constructor(app, indexVersion, onChange) {
+ super();
+ this.app = app;
+ this.indexVersion = indexVersion;
+ this.onChange = onChange;
+ this.initialized = false;
+ this.vault = app.vault;
+ this.metadataCache = app.metadataCache;
+ this.pages = new Map();
+ this.tags = new ValueCaseInsensitiveIndexMap();
+ this.etags = new ValueCaseInsensitiveIndexMap();
+ this.links = new IndexMap();
+ this.revision = 0;
+ // Caches metadata via durable storage to speed up cache initialization when Obsidian restarts.
+ this.persister = new LocalStorageCache(app.appId || "shared", indexVersion);
+ // Handles asynchronous reloading of files on web workers.
+ this.addChild((this.importer = new FileImporter(2, this.vault, this.metadataCache)));
+ // Prefix listens to file creation/deletion/rename, and not modifies, so we let it set up it's own listeners.
+ this.addChild((this.prefix = PrefixIndex.create(this.vault, () => this.touch())));
+ // The CSV cache also needs to listen to filesystem events for cache invalidation.
+ this.addChild((this.csv = new CsvCache(this.vault)));
+ // The starred cache fetches starred entries semi-regularly via an interval.
+ this.addChild((this.starred = new StarredCache(this.app, () => this.touch())));
+ }
+ /** Trigger a metadata event on the metadata cache. */
+ trigger(...args) {
+ this.metadataCache.trigger("dataview:metadata-change", ...args);
+ }
+ /** "Touch" the index, incrementing the revision number and causing downstream views to reload. */
+ touch() {
+ this.revision += 1;
+ this.onChange();
+ }
+ /** Runs through the whole vault to set up initial file metadata. */
+ initialize() {
+ // The metadata cache is updated on initial file index and file loads.
+ this.registerEvent(this.metadataCache.on("resolve", file => this.reload(file)));
+ // Renames do not set off the metadata cache; catch these explicitly.
+ this.registerEvent(this.vault.on("rename", this.rename, this));
+ // File creation does cause a metadata change, but deletes do not. Clear the caches for this.
+ this.registerEvent(this.vault.on("delete", af => {
+ if (!(af instanceof obsidian.TFile) || !PathFilters.markdown(af.path))
+ return;
+ let file = af;
+ this.pages.delete(file.path);
+ this.tags.delete(file.path);
+ this.etags.delete(file.path);
+ this.links.delete(file.path);
+ this.touch();
+ this.trigger("delete", file);
+ }));
+ // Asynchronously initialize actual content in the background.
+ this._initialize(this.vault.getMarkdownFiles());
+ }
+ /** Drops the local storage cache and re-indexes all files; this should generally be used if you expect cache issues. */
+ async reinitialize() {
+ await this.persister.recreate();
+ const files = this.vault.getMarkdownFiles();
+ const start = Date.now();
+ let promises = files.map(file => this.reload(file));
+ await Promise.all(promises);
+ console.log(`Dataview: re-initialized index with ${files.length} files (${(Date.now() - start) / 1000.0}s)`);
+ }
+ /** Internal asynchronous initializer. */
+ async _initialize(files) {
+ let reloadStart = Date.now();
+ let promises = files.map(l => this.reload(l));
+ let results = await Promise.all(promises);
+ let cached = 0, skipped = 0;
+ for (let item of results) {
+ if (item.skipped) {
+ skipped += 1;
+ continue;
+ }
+ if (item.cached)
+ cached += 1;
+ }
+ this.initialized = true;
+ this.metadataCache.trigger("dataview:index-ready");
+ console.log(`Dataview: all ${files.length} files have been indexed in ${(Date.now() - reloadStart) / 1000.0}s (${cached} cached, ${skipped} skipped).`);
+ // Drop keys for files which do not exist anymore.
+ let remaining = await this.persister.synchronize(files.map(l => l.path));
+ if (remaining.size > 0) {
+ console.log(`Dataview: Dropped cache entries for ${remaining.size} deleted files.`);
+ }
+ }
+ rename(file, oldPath) {
+ if (!(file instanceof obsidian.TFile) || !PathFilters.markdown(file.path))
+ return;
+ if (this.pages.has(oldPath)) {
+ const oldMeta = this.pages.get(oldPath);
+ this.pages.delete(oldPath);
+ if (oldMeta) {
+ oldMeta.path = file.path;
+ this.pages.set(file.path, oldMeta);
+ }
+ }
+ this.tags.rename(oldPath, file.path);
+ this.links.rename(oldPath, file.path);
+ this.etags.rename(oldPath, file.path);
+ this.touch();
+ this.trigger("rename", file, oldPath);
+ }
+ /** Queue a file for reloading; this is done asynchronously in the background and may take a few seconds. */
+ async reload(file) {
+ if (!PathFilters.markdown(file.path))
+ return { cached: false, skipped: true };
+ // The first load of a file is attempted from persisted cache; subsequent loads just use the importer.
+ if (this.pages.has(file.path) || this.initialized) {
+ await this.import(file);
+ return { cached: false, skipped: false };
+ }
+ else {
+ // Check the cache for the latest data; if it is out of date or non-existent, then reload.
+ return this.persister.loadFile(file.path).then(async (cached) => {
+ if (!cached || cached.time < file.stat.mtime || cached.version != this.indexVersion) {
+ // This cache value is out of data, reload via the importer and update the cache.
+ // We will skip files with no active file metadata - they will be caught by a later reload
+ // via the 'resolve' metadata event.
+ let fileCache = this.metadataCache.getFileCache(file);
+ if (fileCache === undefined || fileCache === null)
+ return { cached: false, skipped: true };
+ await this.import(file);
+ return { cached: false, skipped: false };
+ }
+ else {
+ // Use the cached data since it is up to date and on the same version.
+ this.finish(file, cached.data);
+ return { cached: true, skipped: false };
+ }
+ });
+ }
+ }
+ /** Import a file directly from disk, skipping the cache. */
+ async import(file) {
+ return this.importer.reload(file).then(r => {
+ this.finish(file, r);
+ this.persister.storeFile(file.path, r);
+ });
+ }
+ /** Finish the reloading of file metadata by adding it to in memory indexes. */
+ finish(file, parsed) {
+ let meta = PageMetadata.canonicalize(parsed, link => {
+ let realPath = this.metadataCache.getFirstLinkpathDest(link.path, file.path);
+ if (realPath)
+ return link.withPath(realPath.path);
+ else
+ return link;
+ });
+ this.pages.set(file.path, meta);
+ this.tags.set(file.path, meta.fullTags());
+ this.etags.set(file.path, meta.tags);
+ this.links.set(file.path, new Set(meta.links.map(l => l.path)));
+ this.touch();
+ this.trigger("update", file);
+ }
+}
+/** Indexes files by their full prefix - essentially a simple prefix tree. */
+class PrefixIndex extends obsidian.Component {
+ vault;
+ updateRevision;
+ static create(vault, updateRevision) {
+ return new PrefixIndex(vault, updateRevision);
+ }
+ constructor(vault, updateRevision) {
+ super();
+ this.vault = vault;
+ this.updateRevision = updateRevision;
+ }
+ *walk(folder, filter) {
+ for (const file of folder.children) {
+ if (file instanceof obsidian.TFolder) {
+ yield* this.walk(file, filter);
+ }
+ else if (filter ? filter(file.path) : true) {
+ yield file.path;
+ }
+ }
+ }
+ /** Get the list of all files under the given path. */
+ get(prefix, filter) {
+ let folder = this.vault.getAbstractFileByPath(prefix || "/");
+ return new Set(folder instanceof obsidian.TFolder ? this.walk(folder, filter) : []);
+ }
+ /** Determines if the given path exists in the prefix index. */
+ pathExists(path) {
+ return this.vault.getAbstractFileByPath(path || "/") != null;
+ }
+ /** Determines if the given prefix exists in the prefix index. */
+ nodeExists(prefix) {
+ return this.vault.getAbstractFileByPath(prefix || "/") instanceof obsidian.TFolder;
+ }
+ /**
+ * Use the in-memory prefix index to convert a relative path to an absolute one.
+ */
+ resolveRelative(path, origin) {
+ if (!origin)
+ return path;
+ else if (path.startsWith("/"))
+ return path.substring(1);
+ let relativePath = getParentFolder(origin) + "/" + path;
+ if (this.pathExists(relativePath))
+ return relativePath;
+ else
+ return path;
+ }
+}
+/** Simple path filters which filter file types. */
+var PathFilters;
+(function (PathFilters) {
+ function csv(path) {
+ return path.toLowerCase().endsWith(".csv");
+ }
+ PathFilters.csv = csv;
+ function markdown(path) {
+ let lcPath = path.toLowerCase();
+ return lcPath.endsWith(".md") || lcPath.endsWith(".markdown");
+ }
+ PathFilters.markdown = markdown;
+})(PathFilters || (PathFilters = {}));
+/**
+ * Caches in-use CSVs to make high-frequency reloads (such as actively looking at a document
+ * that uses CSV) fast.
+ */
+class CsvCache extends obsidian.Component {
+ vault;
+ static CACHE_EXPIRY_SECONDS = 5 * 60;
+ // Cache of loaded CSVs; old entries will periodically be removed
+ cache;
+ // Periodic job which clears out the cache based on time.
+ cacheClearInterval;
+ constructor(vault) {
+ super();
+ this.vault = vault;
+ this.cache = new Map();
+ // Force-flush the cache on CSV file deletions or modifications.
+ this.registerEvent(this.vault.on("modify", file => {
+ if (file instanceof obsidian.TFile && PathFilters.csv(file.path))
+ this.cache.delete(file.path);
+ }));
+ this.registerEvent(this.vault.on("delete", file => {
+ if (file instanceof obsidian.TFile && PathFilters.csv(file.path))
+ this.cache.delete(file.path);
+ }));
+ }
+ /** Load a CSV file from the cache, doing a fresh load if it has not been loaded. */
+ async get(path) {
+ // Clear old entries on every fresh load, since the path being loaded may be stale.
+ this.clearOldEntries();
+ let existing = this.cache.get(path);
+ if (existing)
+ return Result.success(existing.data);
+ else {
+ let value = await this.loadInternal(path);
+ if (value.successful)
+ this.cache.set(path, { data: value.value, loadTime: DateTime.now() });
+ return value;
+ }
+ }
+ /** Do the actual raw loading of a CSV path (which is either local or an HTTP request). */
+ async loadInternal(path) {
+ // Allow http://, https://, and file:// prefixes which use AJAX.
+ if (path.startsWith("http://") || path.startsWith("https://") || path.startsWith("file://")) {
+ try {
+ let result = await fetch(path, {
+ method: "GET",
+ mode: "no-cors",
+ redirect: "follow",
+ });
+ return Result.success(parseCsv(await result.text()));
+ }
+ catch (ex) {
+ return Result.failure("" + ex + "\n\n" + ex.stack);
+ }
+ }
+ // Otherwise, assume it is a fully-qualified file path.
+ try {
+ let fileData = await this.vault.adapter.read(path);
+ return Result.success(parseCsv(fileData));
+ }
+ catch (ex) {
+ return Result.failure(`Failed to load data from path '${path}'.`);
+ }
+ }
+ /** Clear old entries in the cache (as measured by insertion time). */
+ clearOldEntries() {
+ let currentTime = DateTime.now();
+ let keysToRemove = new Set();
+ for (let [key, value] of this.cache.entries()) {
+ let entryAge = Math.abs(currentTime.diff(value.loadTime, "seconds").seconds);
+ if (entryAge > CsvCache.CACHE_EXPIRY_SECONDS)
+ keysToRemove.add(key);
+ }
+ keysToRemove.forEach(key => this.cache.delete(key));
+ }
+}
+/** Optional connector to the Obsidian 'Starred' plugin which allows for efficiently querying if a file is starred or not. */
+class StarredCache extends obsidian.Component {
+ app;
+ onUpdate;
+ /** Initial delay before checking the cache; we need to wait for it to asynchronously load the initial stars. */
+ static INITIAL_DELAY = 4 * 1000;
+ /** How frequently to check for star updates. */
+ static REFRESH_INTERVAL = 30 * 1000;
+ /** Set of all starred file paths. */
+ stars;
+ constructor(app, onUpdate) {
+ super();
+ this.app = app;
+ this.onUpdate = onUpdate;
+ this.stars = StarredCache.fetch(this.app);
+ this.registerInterval(window.setInterval(() => this.reload(), StarredCache.REFRESH_INTERVAL));
+ const initialHandler = window.setTimeout(() => this.reload(), StarredCache.INITIAL_DELAY);
+ this.register(() => window.clearTimeout(initialHandler));
+ }
+ /** Determines if the given path is starred. */
+ starred(path) {
+ return this.stars.has(path);
+ }
+ reload() {
+ let newStars = StarredCache.fetch(this.app);
+ if (!setsEqual(this.stars, newStars)) {
+ this.stars = newStars;
+ this.onUpdate();
+ }
+ }
+ /** Fetch all starred files from the stars plugin, if present. */
+ static fetch(app) {
+ let items = app?.internalPlugins?.plugins?.bookmarks?.instance?.items;
+ if (items == undefined)
+ return new Set();
+ // Retrieve all grouped (nested) items, returning a flat array
+ const flattenItems = (items) => {
+ let children = [];
+ return items
+ .map(i => {
+ if (i.type == "group" && i.items && i.items.length) {
+ children = [...children, ...i.items];
+ }
+ return i;
+ })
+ .concat(children.length ? flattenItems(children) : children);
+ };
+ items = flattenItems(items);
+ return new Set(items.filter((l) => l.type === "file").map(l => l.path));
+ }
+}
+/** A generic index which indexes variables of the form key -> value[], allowing both forward and reverse lookups. */
+class IndexMap {
+ /** Maps key -> values for that key. */
+ map;
+ /** Cached inverse map; maps value -> keys that reference that value. */
+ invMap;
+ /** Create a new, empty index map. */
+ constructor() {
+ this.map = new Map();
+ this.invMap = new Map();
+ }
+ /** Returns all values for the given key. */
+ get(key) {
+ let result = this.map.get(key);
+ if (result) {
+ return new Set(result);
+ }
+ else {
+ return new Set();
+ }
+ }
+ /** Returns all keys that reference the given key. Mutating the returned set is not allowed. */
+ getInverse(value) {
+ return this.invMap.get(value) || IndexMap.EMPTY_SET;
+ }
+ /** Sets the key to the given values; this will delete the old mapping for the key if one was present. */
+ set(key, values) {
+ if (!values.size) {
+ // no need to store if no values
+ this.delete(key);
+ return this;
+ }
+ let oldValues = this.map.get(key);
+ if (oldValues) {
+ for (let value of oldValues) {
+ // Only delete the ones we're not adding back
+ if (!values.has(key))
+ this.invMap.get(value)?.delete(key);
+ }
+ }
+ this.map.set(key, values);
+ for (let value of values) {
+ if (!this.invMap.has(value))
+ this.invMap.set(value, new Set([key]));
+ else
+ this.invMap.get(value)?.add(key);
+ }
+ return this;
+ }
+ /** Clears all values for the given key so they can be re-added. */
+ delete(key) {
+ let oldValues = this.map.get(key);
+ if (!oldValues)
+ return false;
+ this.map.delete(key);
+ for (let value of oldValues) {
+ this.invMap.get(value)?.delete(key);
+ }
+ return true;
+ }
+ /** Rename all references to the given key to a new value. */
+ rename(oldKey, newKey) {
+ let oldValues = this.map.get(oldKey);
+ if (!oldValues)
+ return false;
+ this.delete(oldKey);
+ this.set(newKey, oldValues);
+ return true;
+ }
+ /** Clear the entire index. */
+ clear() {
+ this.map.clear();
+ this.invMap.clear();
+ }
+ static EMPTY_SET = Object.freeze(new Set());
+}
+/** Index map wrapper which is case-insensitive in the key. */
+class ValueCaseInsensitiveIndexMap {
+ delegate;
+ /** Create a new, empty case insensitive index map. */
+ constructor(delegate = new IndexMap()) {
+ this.delegate = delegate;
+ }
+ /** Returns all values for the given key. */
+ get(key) {
+ return this.delegate.get(key);
+ }
+ /** Returns all keys that reference the given value. Mutating the returned set is not allowed. */
+ getInverse(value) {
+ return this.delegate.getInverse(value.toLocaleLowerCase());
+ }
+ /** Sets the key to the given values; this will delete the old mapping for the key if one was present. */
+ set(key, values) {
+ this.delegate.set(key, new Set(Array.from(values).map(v => v.toLocaleLowerCase())));
+ return this;
+ }
+ /** Clears all values for the given key so they can be re-added. */
+ delete(key) {
+ return this.delegate.delete(key);
+ }
+ /** Rename all references to the given key to a new value. */
+ rename(oldKey, newKey) {
+ return this.delegate.rename(oldKey, newKey);
+ }
+ /** Clear the entire index. */
+ clear() {
+ this.delegate.clear();
+ }
+}
+
+/** Collect data matching a source query. */
+/** Find source paths which match the given source. */
+function matchingSourcePaths(source, index, originFile = "") {
+ switch (source.type) {
+ case "empty":
+ return Result.success(new Set());
+ case "tag":
+ return Result.success(index.tags.getInverse(source.tag));
+ case "csv":
+ return Result.success(new Set([index.prefix.resolveRelative(source.path, originFile)]));
+ case "folder":
+ // Prefer loading from the folder at the given path.
+ if (index.prefix.nodeExists(source.folder))
+ return Result.success(index.prefix.get(source.folder, PathFilters.markdown));
+ // But allow for loading individual files if they exist.
+ if (index.prefix.pathExists(source.folder))
+ return Result.success(new Set([source.folder]));
+ else if (index.prefix.pathExists(source.folder + ".md"))
+ return Result.success(new Set([source.folder + ".md"]));
+ // For backwards-compat, return an empty result even if the folder does not exist.
+ return Result.success(new Set());
+ case "link":
+ let fullPath = index.metadataCache.getFirstLinkpathDest(source.file, originFile)?.path;
+ if (!fullPath) {
+ // Look in links which includes unresolved links
+ return Result.success(index.links.getInverse(source.file));
+ }
+ if (source.direction === "incoming") {
+ // To find all incoming links (i.e., things that link to this), use the index that Obsidian provides.
+ // TODO: Use an actual index so this isn't a fullscan.
+ let resolved = index.metadataCache.resolvedLinks;
+ let incoming = new Set();
+ for (let [key, value] of Object.entries(resolved)) {
+ if (fullPath in value)
+ incoming.add(key);
+ }
+ return Result.success(incoming);
+ }
+ else {
+ let resolved = index.metadataCache.resolvedLinks;
+ if (!(fullPath in resolved))
+ return Result.failure(`Could not find file "${source.file}" during link lookup - does it exist?`);
+ return Result.success(new Set(Object.keys(index.metadataCache.resolvedLinks[fullPath])));
+ }
+ case "binaryop":
+ return Result.flatMap2(matchingSourcePaths(source.left, index, originFile), matchingSourcePaths(source.right, index, originFile), (left, right) => {
+ if (source.op == "&") {
+ let result = new Set();
+ for (let elem of right) {
+ if (left.has(elem))
+ result.add(elem);
+ }
+ return Result.success(result);
+ }
+ else if (source.op == "|") {
+ let result = new Set(left);
+ for (let elem of right)
+ result.add(elem);
+ return Result.success(result);
+ }
+ else {
+ return Result.failure(`Unrecognized operator '${source.op}'.`);
+ }
+ });
+ case "negate":
+ return matchingSourcePaths(source.child, index, originFile).map(child => {
+ // TODO: This is obviously very inefficient. Can be improved by complicating the
+ // return type of this function & optimizing 'and' / 'or'.
+ let allFiles = new Set(index.vault.getMarkdownFiles().map(f => f.path));
+ child.forEach(f => allFiles.delete(f));
+ return allFiles;
+ });
+ }
+}
+/** Convert a path to the data for that path; usually markdown pages, but could also be other file types (like CSV). */
+async function resolvePathData(path, index) {
+ if (PathFilters.csv(path))
+ return resolveCsvData(path, index);
+ else
+ return resolveMarkdownData(path, index);
+}
+// TODO: We shouldn't be doing path normalization here relative to an origin file,
+/** Convert a CSV path to the data in the CSV (in dataview format). */
+async function resolveCsvData(path, index) {
+ let rawData = await index.csv.get(path);
+ return rawData.map(rows => {
+ return rows.map((row, index) => {
+ return {
+ id: `${path}#${index}`,
+ data: row,
+ };
+ });
+ });
+}
+/** Convert a path pointing to a markdown page, into the associated metadata. */
+function resolveMarkdownData(path, index) {
+ let page = index.pages.get(path);
+ if (!page)
+ return Result.success([]);
+ return Result.success([
+ {
+ id: Link.file(path),
+ data: page.serialize(index),
+ },
+ ]);
+}
+/** Resolve a source to the collection of data rows that it matches. */
+async function resolveSource(source, index, originFile = "") {
+ let paths = matchingSourcePaths(source, index, originFile);
+ if (!paths.successful)
+ return Result.failure(paths.error);
+ let result = [];
+ for (let path of paths.value) {
+ let resolved = await resolvePathData(path, index);
+ if (!resolved.successful)
+ return resolved;
+ for (let val of resolved.value)
+ result.push(val);
+ }
+ return Result.success(result);
+}
+
+// cyrb53 (c) 2018 bryc (github.com/bryc). License: Public domain. Attribution appreciated.
+// A fast and simple 64-bit (or 53-bit) string hash function with decent collision resistance.
+// Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
+// See https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript/52171480#52171480
+// https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
+function cyrb53(str, seed = 0) {
+ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
+ for (let i = 0, ch; i < str.length; i++) {
+ ch = str.charCodeAt(i);
+ h1 = Math.imul(h1 ^ ch, 2654435761);
+ h2 = Math.imul(h2 ^ ch, 1597334677);
+ }
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
+ h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
+ h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
+ // For a full 64-bit value we could return
+ // [h2>>>0, h1>>>0]
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0); // ;
+}
+
+/** Default function implementations for the expression evaluator. */
+/**
+ * Allows for the creation of functions that check the number and type of their arguments, and dispatch
+ * to different implementations based on the types of the inputs.
+ */
+class FunctionBuilder {
+ name;
+ variants;
+ vectorized;
+ constructor(name) {
+ this.name = name;
+ this.variants = [];
+ this.vectorized = {};
+ }
+ /** Add a general function variant which accepts any number of arguments of any type. */
+ vararg(impl) {
+ this.variants.push({ args: [], varargs: true, impl });
+ return this;
+ }
+ /** Add a function variant which takes in a single argument. */
+ add1(argType, impl) {
+ this.variants.push({
+ args: [argType],
+ varargs: false,
+ impl: (c, ...rest) => impl(rest[0], c),
+ });
+ return this;
+ }
+ /** Add a function variant which takes in two typed arguments. */
+ add2(arg1, arg2, impl) {
+ this.variants.push({
+ args: [arg1, arg2],
+ varargs: false,
+ impl: (c, ...rest) => impl(rest[0], rest[1], c),
+ });
+ return this;
+ }
+ /** Add a function variant which takes in three typed arguments. */
+ add3(arg1, arg2, arg3, impl) {
+ this.variants.push({
+ args: [arg1, arg2, arg3],
+ varargs: false,
+ impl: (c, ...rest) => impl(rest[0], rest[1], rest[2], c),
+ });
+ return this;
+ }
+ /** Add vectorized variants which accept the given number of arguments and delegate. */
+ vectorize(numArgs, positions) {
+ this.vectorized[numArgs] = positions;
+ return this;
+ }
+ /** Return a function which checks the number and type of arguments, passing them on to the first matching variant. */
+ build() {
+ let self = (context, ...args) => {
+ let types = [];
+ for (let arg of args) {
+ let argType = Values.typeOf(arg);
+ if (!argType)
+ throw Error(`Unrecognized argument type for argument '${arg}'`);
+ types.push(argType);
+ }
+ // Handle vectorization, possibly in multiple fields.
+ if (this.vectorized[types.length]) {
+ let vectorizedPositions = this.vectorized[types.length].filter(k => types[k] == "array");
+ if (vectorizedPositions.length > 0) {
+ let minLength = vectorizedPositions
+ .map(p => args[p].length)
+ .reduce((p, c) => Math.min(p, c));
+ // Call the subfunction for each element in the longest array.
+ // If you call a vectorized function with different-length arrays,
+ // the output is limited by the length of the shortest array.
+ let result = [];
+ for (let vpos = 0; vpos < minLength; vpos++) {
+ let subargs = [];
+ for (let index = 0; index < args.length; index++) {
+ if (vectorizedPositions.includes(index)) {
+ let arr = args[index];
+ subargs.push(arr[vpos]);
+ }
+ else {
+ subargs.push(args[index]);
+ }
+ }
+ result.push(self(context, ...subargs));
+ }
+ return result;
+ }
+ }
+ outer: for (let variant of this.variants) {
+ if (variant.varargs)
+ return variant.impl(context, ...args);
+ if (variant.args.length != types.length)
+ continue;
+ for (let index = 0; index < variant.args.length; index++) {
+ if (variant.args[index] != "*" && variant.args[index] != types[index])
+ continue outer;
+ }
+ return variant.impl(context, ...args);
+ }
+ throw Error(`No implementation of '${this.name}' found for arguments: ${types.join(", ")}`);
+ };
+ return self;
+ }
+}
+/** Utilities for managing function implementations. */
+var Functions;
+(function (Functions) {
+ /** Bind a context to a function implementation, yielding a function which does not need the context argument. */
+ function bind(func, context) {
+ return (...args) => func(context, ...args);
+ }
+ Functions.bind = bind;
+ /** Bind a context to all functions in the given map, yielding a new map of bound functions. */
+ function bindAll(funcs, context) {
+ let result = {};
+ for (let [key, func] of Object.entries(funcs)) {
+ result[key] = Functions.bind(func, context);
+ }
+ return result;
+ }
+ Functions.bindAll = bindAll;
+})(Functions || (Functions = {}));
+/**
+ * Collection of all defined functions; defined here so that they can be called from within dataview,
+ * and test code.
+ */
+var DefaultFunctions;
+(function (DefaultFunctions) {
+ DefaultFunctions.typeOf = new FunctionBuilder("type")
+ .add1("array", _ => "array")
+ .add1("boolean", _ => "boolean")
+ .add1("date", _ => "date")
+ .add1("duration", _ => "duration")
+ .add1("function", _ => "function")
+ .add1("widget", _ => "widget")
+ .add1("link", _ => "link")
+ .add1("null", _ => "null")
+ .add1("number", _ => "number")
+ .add1("object", _ => "object")
+ .add1("string", _ => "string")
+ .add1("*", _ => "unknown")
+ .build();
+ /** Compute the length of a data type. */
+ DefaultFunctions.length = new FunctionBuilder("length")
+ .add1("array", a => a.length)
+ .add1("object", a => Object.keys(a).length)
+ .add1("string", a => a.length)
+ .add1("null", _a => 0)
+ .build();
+ /** List constructor function. */
+ DefaultFunctions.list = (_context, ...args) => args;
+ /** Object constructor function. */
+ DefaultFunctions.object = (_context, ...args) => {
+ if (args.length % 2 != 0)
+ throw Error("object() requires an even number of arguments");
+ let result = {};
+ for (let index = 0; index < args.length; index += 2) {
+ let key = args[index];
+ if (!Values.isString(key))
+ throw Error("keys should be of type string for object(key1, value1, ...)");
+ result[key] = args[index + 1];
+ }
+ return result;
+ };
+ /** Internal link constructor function. */
+ DefaultFunctions.link = new FunctionBuilder("link")
+ .add1("string", (a, c) => Link.file(c.linkHandler.normalize(a), false))
+ .add1("link", a => a)
+ .add1("null", _a => null)
+ .vectorize(1, [0])
+ .add2("string", "string", (t, d, c) => Link.file(c.linkHandler.normalize(t), false, d))
+ .add3("string", "string", "boolean", (t, d, e, c) => Link.file(c.linkHandler.normalize(t), e, d))
+ .add2("link", "string", (t, d) => t.withDisplay(d))
+ .add2("null", "*", () => null)
+ .add2("*", "null", (t, _n, c) => DefaultFunctions.link(c, t))
+ .vectorize(2, [0, 1])
+ .build();
+ /** Embed and un-embed a link. */
+ DefaultFunctions.embed = new FunctionBuilder("embed")
+ .add1("link", l => l.toEmbed())
+ .vectorize(1, [0])
+ .add2("link", "boolean", (l, e, c) => (e ? l.toEmbed() : l.fromEmbed()))
+ .add1("null", () => null)
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .build();
+ /** External link constructor function. */
+ DefaultFunctions.elink = new FunctionBuilder("elink")
+ .add2("string", "string", (a, d) => Widgets.externalLink(a, d))
+ .add2("string", "null", (s, _n, c) => DefaultFunctions.elink(c, s, s))
+ .add2("null", "*", () => null)
+ .vectorize(2, [0])
+ .add1("string", (a, c) => DefaultFunctions.elink(c, a, a))
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ /** Date constructor function. */
+ DefaultFunctions.date = new FunctionBuilder("date")
+ .add1("string", str => {
+ let parsedDate = EXPRESSION.datePlus.parse(str);
+ if (parsedDate.status)
+ return parsedDate.value;
+ else
+ return null;
+ })
+ .add1("date", d => d)
+ .add1("link", (link, c) => {
+ // Try to parse from the display...
+ if (link.display) {
+ let parsedDate = EXPRESSION.date.parse(link.display);
+ if (parsedDate.status)
+ return parsedDate.value;
+ }
+ // Then try to parse from the path...
+ let parsedDate = EXPRESSION.date.parse(link.path);
+ if (parsedDate.status)
+ return parsedDate.value;
+ // Then pull it from the file.
+ let resolved = c.linkHandler.resolve(link.path);
+ if (resolved && resolved?.file?.day) {
+ return resolved?.file?.day;
+ }
+ return null;
+ })
+ .add2("string", "string", (d, f) => {
+ if (f === "x" || f === "X") {
+ let match = NUMBER_REGEX.exec(d);
+ if (match)
+ return DateTime.fromMillis(Number.parseInt(match[0]) * (f === "X" ? 1000 : 1));
+ else {
+ throw Error("Not a number for format( (${ f }): ${ d }");
+ }
+ }
+ else {
+ let parsedDate = DateTime.fromFormat(d, f);
+ if (parsedDate.isValid)
+ return parsedDate;
+ else {
+ throw Error(`Can't handle format (${f}) on date string (${d})`);
+ }
+ }
+ })
+ .add2("null", "string", () => null)
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ /** Duration constructor function. */
+ DefaultFunctions.dur = new FunctionBuilder("dur")
+ .add1("string", str => {
+ let parsedDur = EXPRESSION.duration.parse(str.trim());
+ if (parsedDur.status)
+ return parsedDur.value;
+ else
+ return null;
+ })
+ .add1("duration", d => d)
+ .add1("null", d => d)
+ .vectorize(1, [0])
+ .build();
+ /** Format a date using a luxon/moment-style date format. */
+ DefaultFunctions.dateformat = new FunctionBuilder("dateformat")
+ .add2("date", "string", (date, format) => date.toFormat(format, { locale: currentLocale() }))
+ .add2("null", "string", (_nul, _format) => null)
+ .vectorize(2, [0])
+ .build();
+ DefaultFunctions.durationformat = new FunctionBuilder("durationformat")
+ .add2("duration", "string", (dur, format) => dur.toFormat(format))
+ .add2("null", "string", (_nul, _format) => null)
+ .vectorize(2, [0])
+ .build();
+ DefaultFunctions.localtime = new FunctionBuilder("localtime")
+ .add1("date", d => d.toLocal())
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ const NUMBER_REGEX = /-?[0-9]+(\.[0-9]+)?/;
+ /** Number constructor function. */
+ DefaultFunctions.number = new FunctionBuilder("number")
+ .add1("number", a => a)
+ .add1("string", str => {
+ let match = NUMBER_REGEX.exec(str);
+ if (match)
+ return Number.parseFloat(match[0]);
+ else
+ return null;
+ })
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ /** Format a number using a standard currency format. */
+ DefaultFunctions.currencyformat = new FunctionBuilder("currencyformat")
+ .add2("number", "string", (num, format) => Intl.NumberFormat(currentLocale(), { style: "currency", currency: format }).format(num))
+ .add2("null", "string", (_nul, _format) => null)
+ .add1("number", num => Intl.NumberFormat(currentLocale(), { style: "currency", currency: "USD" }).format(num))
+ .add1("null", () => null)
+ .vectorize(2, [0])
+ .build();
+ /**
+ * Convert any value to a reasonable internal string representation. Most useful for dates, strings, numbers, and
+ * so on.
+ */
+ DefaultFunctions.string = new FunctionBuilder("string").add1("*", (a, ctx) => Values.toString(a, ctx.settings)).build();
+ DefaultFunctions.round = new FunctionBuilder("round")
+ .add1("number", n => Math.round(n))
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .add2("number", "number", (n, p) => {
+ if (p <= 0)
+ return Math.round(n);
+ return parseFloat(n.toFixed(p));
+ })
+ .add2("number", "null", n => Math.round(n))
+ .add2("null", "*", () => null)
+ .vectorize(2, [0])
+ .build();
+ DefaultFunctions.trunc = new FunctionBuilder("trunc")
+ .add1("number", n => Math.trunc(n))
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ DefaultFunctions.floor = new FunctionBuilder("floor")
+ .add1("number", n => Math.floor(n))
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ DefaultFunctions.ceil = new FunctionBuilder("ceil")
+ .add1("number", n => Math.ceil(n))
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ DefaultFunctions.min = new FunctionBuilder("min")
+ .add2("*", "null", (a, _n) => a)
+ .add2("null", "*", (_n, a) => a)
+ .add2("*", "*", (a, b, ctx) => (Values.compareValue(a, b, ctx.linkHandler.normalize) <= 0 ? a : b))
+ .add1("array", (a, ctx) => DefaultFunctions.min(ctx, ...a))
+ .vararg((ctx, ...args) => (args.length == 0 ? null : args.reduce((p, c) => DefaultFunctions.min(ctx, p, c))))
+ .build();
+ DefaultFunctions.max = new FunctionBuilder("max")
+ .add2("*", "null", (a, _n) => a)
+ .add2("null", "*", (_n, a) => a)
+ .add2("*", "*", (a, b, ctx) => (Values.compareValue(a, b, ctx.linkHandler.normalize) > 0 ? a : b))
+ .add1("array", (a, ctx) => DefaultFunctions.max(ctx, ...a))
+ .vararg((ctx, ...args) => (args.length == 0 ? null : args.reduce((p, c) => DefaultFunctions.max(ctx, p, c))))
+ .build();
+ DefaultFunctions.minby = new FunctionBuilder("minby")
+ .add2("array", "function", (arr, func, ctx) => {
+ if (arr.length == 0)
+ return null;
+ let values = arr.map(v => {
+ return { value: v, mapped: func(ctx, v) };
+ });
+ let filtered = values.filter(v => !Values.isNull(v.mapped));
+ if (filtered.length == 0)
+ return arr[0];
+ return filtered.reduce((p, c) => {
+ if (Values.compareValue(p.mapped, c.mapped, ctx.linkHandler.normalize) <= 0)
+ return p;
+ else
+ return c;
+ }).value;
+ })
+ .add2("null", "function", (_arr, _func, _ctx) => null)
+ .build();
+ DefaultFunctions.maxby = new FunctionBuilder("maxby")
+ .add2("array", "function", (arr, func, ctx) => {
+ if (arr.length == 0)
+ return null;
+ let values = arr.map(v => {
+ return { value: v, mapped: func(ctx, v) };
+ });
+ let filtered = values.filter(v => !Values.isNull(v.mapped));
+ if (filtered.length == 0)
+ return arr[0];
+ return filtered.reduce((p, c) => {
+ if (Values.compareValue(p.mapped, c.mapped, ctx.linkHandler.normalize) > 0)
+ return p;
+ else
+ return c;
+ }).value;
+ })
+ .add2("null", "function", (_arr, _func, _ctx) => null)
+ .build();
+ DefaultFunctions.striptime = new FunctionBuilder("striptime")
+ .add1("date", d => DateTime.fromObject({ year: d.year, month: d.month, day: d.day }))
+ .add1("null", _n => null)
+ .vectorize(1, [0])
+ .build();
+ // Default contains, which looks through data structures recursively.
+ DefaultFunctions.contains = new FunctionBuilder("contains")
+ .add2("array", "*", (l, elem, context) => l.some(e => DefaultFunctions.contains(context, e, elem)))
+ .add2("string", "string", (haystack, needle) => haystack.includes(needle))
+ .add2("object", "string", (obj, key) => key in obj)
+ .add2("*", "*", (elem1, elem2, context) => context.evaluate(Fields.binaryOp(Fields.literal(elem1), "=", Fields.literal(elem2))).orElseThrow())
+ .vectorize(2, [1])
+ .build();
+ // Case insensitive version of contains.
+ DefaultFunctions.icontains = new FunctionBuilder("icontains")
+ .add2("array", "*", (l, elem, context) => l.some(e => DefaultFunctions.icontains(context, e, elem)))
+ .add2("string", "string", (haystack, needle) => haystack.toLocaleLowerCase().includes(needle.toLocaleLowerCase()))
+ .add2("object", "string", (obj, key) => key in obj)
+ .add2("*", "*", (elem1, elem2, context) => context.evaluate(Fields.binaryOp(Fields.literal(elem1), "=", Fields.literal(elem2))).orElseThrow())
+ .vectorize(2, [1])
+ .build();
+ // "exact" contains, does not look recursively.
+ DefaultFunctions.econtains = new FunctionBuilder("econtains")
+ .add2("array", "*", (l, elem, context) => l.some(e => context.evaluate(Fields.binaryOp(Fields.literal(elem), "=", Fields.literal(e))).orElseThrow()))
+ .add2("string", "string", (haystack, needle) => haystack.includes(needle))
+ .add2("object", "string", (obj, key) => key in obj)
+ .add2("*", "*", (elem1, elem2, context) => context.evaluate(Fields.binaryOp(Fields.literal(elem1), "=", Fields.literal(elem2))).orElseThrow())
+ .vectorize(2, [1])
+ .build();
+ // Case insensitive contains which looks for exact word matches (i.e., boundary-to-boundary match).
+ DefaultFunctions.containsword = new FunctionBuilder("containsword")
+ .add2("string", "string", (hay, needle) => !!hay.match(new RegExp(".*\\b" + escapeRegex(needle) + "\\b.*", "i")))
+ .add2("null", "*", (_a, _b) => null)
+ .add2("*", "null", (_a, _b) => null)
+ .vectorize(2, [0, 1])
+ .build();
+ /** Extract 0 or more keys from a given object via indexing. */
+ DefaultFunctions.extract = (context, ...args) => {
+ if (args.length == 0)
+ return "extract(object, key1, ...) requires at least 1 argument";
+ // Manually handle vectorization in the first argument.
+ let object = args[0];
+ if (Values.isArray(object))
+ return object.map(v => DefaultFunctions.extract(context, v, ...args.slice(1)));
+ let result = {};
+ for (let index = 1; index < args.length; index++) {
+ let key = args[index];
+ if (!Values.isString(key))
+ throw Error("extract(object, key1, ...) must be called with string keys");
+ result[key] = context.evaluate(Fields.index(Fields.literal(object), Fields.literal(key))).orElseThrow();
+ }
+ return result;
+ };
+ // Reverse an array or string.
+ DefaultFunctions.reverse = new FunctionBuilder("reverse")
+ .add1("array", l => {
+ let result = [];
+ for (let index = l.length - 1; index >= 0; index--)
+ result.push(l[index]);
+ return result;
+ })
+ .add1("string", l => {
+ let result = "";
+ for (let c = 0; c < l.length; c++)
+ result += l[l.length - c - 1];
+ return result;
+ })
+ .add1("*", e => e)
+ .build();
+ // Sort an array; if given two arguments, sorts by the key returned.
+ DefaultFunctions.sort = new FunctionBuilder("sort")
+ .add1("array", (list, context) => DefaultFunctions.sort(context, list, (_ctx, a) => a))
+ .add2("array", "function", (list, key, context) => {
+ let result = [].concat(list);
+ result.sort((a, b) => {
+ let akey = key(context, a);
+ let bkey = key(context, b);
+ let le = context
+ .evaluate(Fields.binaryOp(Fields.literal(akey), "<", Fields.literal(bkey)))
+ .orElseThrow();
+ if (Values.isTruthy(le))
+ return -1;
+ let eq = context
+ .evaluate(Fields.binaryOp(Fields.literal(akey), "=", Fields.literal(bkey)))
+ .orElseThrow();
+ if (Values.isTruthy(eq))
+ return 0;
+ return 1;
+ });
+ return result;
+ })
+ .add1("*", e => e)
+ .build();
+ DefaultFunctions.regextest = new FunctionBuilder("regextest")
+ .add2("string", "string", (pattern, field) => RegExp(pattern).test(field))
+ .add2("null", "*", (_n, _a) => false)
+ .add2("*", "null", (_a, _n) => false)
+ .vectorize(2, [0, 1])
+ .build();
+ DefaultFunctions.regexmatch = new FunctionBuilder("regexmatch")
+ .add2("string", "string", (pattern, field) => {
+ if (!pattern.startsWith("^") && !pattern.endsWith("$"))
+ pattern = "^" + pattern + "$";
+ return !!field.match(pattern);
+ })
+ .add2("null", "*", (_n, _a) => false)
+ .add2("*", "null", (_a, _n) => false)
+ .vectorize(2, [0, 1])
+ .build();
+ DefaultFunctions.regexreplace = new FunctionBuilder("regexreplace")
+ .add3("string", "string", "string", (field, pat, rep) => {
+ try {
+ let reg = new RegExp(pat, "g");
+ return field.replace(reg, rep);
+ }
+ catch (ex) {
+ throw Error(`Invalid regexp '${pat}' in regexreplace`);
+ }
+ })
+ .add3("null", "*", "*", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("*", "*", "null", () => null)
+ .vectorize(3, [0, 1, 2])
+ .build();
+ DefaultFunctions.lower = new FunctionBuilder("lower")
+ .add1("string", s => s.toLocaleLowerCase())
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ DefaultFunctions.upper = new FunctionBuilder("upper")
+ .add1("string", s => s.toLocaleUpperCase())
+ .add1("null", () => null)
+ .vectorize(1, [0])
+ .build();
+ DefaultFunctions.replace = new FunctionBuilder("replace")
+ .add3("string", "string", "string", (str, pat, repr) => str.split(pat).join(repr))
+ .add3("null", "*", "*", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("*", "*", "null", () => null)
+ .vectorize(3, [0, 1, 2])
+ .build();
+ // Ensure undefined matches turn into empty strings for split/2 and split/3.
+ const splitImpl = (str, delim, limit) => str.split(new RegExp(delim), limit).map(str => str || "");
+ /** Split a string on a given string. */
+ DefaultFunctions.split = new FunctionBuilder("split")
+ .add2("string", "string", (string, splitter) => splitImpl(string, splitter))
+ .add3("string", "string", "number", (string, splitter, limit) => splitImpl(string, splitter, limit))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .add3("*", "*", "null", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("null", "*", "*", () => null)
+ .build();
+ DefaultFunctions.startswith = new FunctionBuilder("startswith")
+ .add2("string", "string", (str, starting) => str.startsWith(starting))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .build();
+ DefaultFunctions.endswith = new FunctionBuilder("endswith")
+ .add2("string", "string", (str, ending) => str.endsWith(ending))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .build();
+ DefaultFunctions.padleft = new FunctionBuilder("padleft")
+ .add2("string", "number", (str, len) => str.padStart(len, " "))
+ .add3("string", "number", "string", (str, len, padding) => str.padStart(len, padding))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .add3("null", "*", "*", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("*", "*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .vectorize(3, [0, 1, 2])
+ .build();
+ DefaultFunctions.padright = new FunctionBuilder("padright")
+ .add2("string", "number", (str, len) => str.padEnd(len, " "))
+ .add3("string", "number", "string", (str, len, padding) => str.padEnd(len, padding))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .add3("null", "*", "*", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("*", "*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .vectorize(3, [0, 1, 2])
+ .build();
+ DefaultFunctions.substring = new FunctionBuilder("substring")
+ .add2("string", "number", (str, start) => str.substring(start))
+ .add3("string", "number", "number", (str, start, end) => str.substring(start, end))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .add3("null", "*", "*", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("*", "*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .vectorize(3, [0, 1, 2])
+ .build();
+ DefaultFunctions.truncate = new FunctionBuilder("truncate")
+ .add3("string", "number", "string", (str, length, suffix) => {
+ if (str.length > length - suffix.length) {
+ return str.substring(0, Math.max(0, length - suffix.length)) + suffix;
+ }
+ else {
+ return str;
+ }
+ })
+ .add2("string", "number", (str, length, ctx) => DefaultFunctions.truncate(ctx, str, length, "..."))
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .add3("null", "*", "*", () => null)
+ .add3("*", "null", "*", () => null)
+ .add3("*", "*", "null", () => null)
+ .vectorize(2, [0, 1])
+ .vectorize(3, [0, 1, 2])
+ .build();
+ DefaultFunctions.fdefault = new FunctionBuilder("default")
+ .add2("*", "*", (v, bk) => (Values.isNull(v) ? bk : v))
+ .vectorize(2, [0, 1])
+ .build();
+ DefaultFunctions.ldefault = new FunctionBuilder("ldefault")
+ .add2("*", "*", (v, bk) => (Values.isNull(v) ? bk : v))
+ .build();
+ // Returns the display name of the element.
+ DefaultFunctions.display = new FunctionBuilder("display")
+ .add1("null", () => "")
+ .add1("array", (a, ctx) => {
+ return a.map(e => DefaultFunctions.display(ctx, e)).join(", ");
+ })
+ .add1("string", (str) => normalizeMarkdown(str))
+ .add1("link", (a, ctx) => {
+ if (a.display) {
+ return DefaultFunctions.display(ctx, a.display);
+ }
+ else {
+ return Values.toString(a, ctx.settings).replace(/\[\[.*\|(.*)\]\]/, "$1");
+ }
+ })
+ .add1("*", (a, ctx) => {
+ return Values.toString(a, ctx.settings);
+ })
+ .build();
+ DefaultFunctions.choice = new FunctionBuilder("choice")
+ .add3("*", "*", "*", (b, left, right) => (Values.isTruthy(b) ? left : right))
+ .vectorize(3, [0])
+ .build();
+ DefaultFunctions.hash = new FunctionBuilder("hash")
+ .add2("string", "number", (seed, variant) => {
+ return cyrb53(seed, variant);
+ })
+ .add2("string", "string", (seed, text) => {
+ return cyrb53(seed + text);
+ })
+ .add3("string", "string", "number", (seed, text, variant) => {
+ return cyrb53(seed + text, variant);
+ })
+ .build();
+ DefaultFunctions.reduce = new FunctionBuilder("reduce")
+ .add2("array", "string", (lis, op, context) => {
+ if (lis.length == 0)
+ return null;
+ if (op != "+" && op != "-" && op != "*" && op != "/" && op != "&" && op != "|")
+ throw Error("reduce(array, op) supports '+', '-', '/', '*', '&', and '|'");
+ let value = lis[0];
+ for (let index = 1; index < lis.length; index++) {
+ value = context
+ .evaluate(Fields.binaryOp(Fields.literal(value), op, Fields.literal(lis[index])))
+ .orElseThrow();
+ }
+ return value;
+ })
+ .add2("array", "function", (lis, op, context) => {
+ if (lis.length == 0)
+ return null;
+ let value = lis[0];
+ for (let index = 1; index < lis.length; index++) {
+ // Skip null values to reduce the pain of summing over fields that may or may not exist.
+ if (Values.isNull(lis[index]))
+ continue;
+ value = op(context, value, lis[index]);
+ }
+ return value;
+ })
+ .add2("null", "*", () => null)
+ .add2("*", "null", () => null)
+ .vectorize(2, [1])
+ .build();
+ DefaultFunctions.sum = new FunctionBuilder("sum")
+ .add1("array", (arr, c) => DefaultFunctions.reduce(c, arr, "+"))
+ .add1("*", e => e)
+ .build();
+ DefaultFunctions.average = new FunctionBuilder("average")
+ .add1("array", (array, context) => {
+ if (array.length == 0)
+ return null;
+ const add = DefaultFunctions.sum(context, array);
+ if (add == null || add == undefined)
+ return null;
+ return context
+ .evaluate(Fields.binaryOp(Fields.literal(add), "/", Fields.literal(array.length)))
+ .orElseThrow();
+ })
+ .add1("*", e => e)
+ .build();
+ DefaultFunctions.product = new FunctionBuilder("product")
+ .add1("array", (arr, c) => DefaultFunctions.reduce(c, arr, "*"))
+ .add1("*", e => e)
+ .build();
+ DefaultFunctions.join = new FunctionBuilder("join")
+ .add2("array", "string", (arr, sep, ctx) => arr.map(e => Values.toString(e, ctx.settings)).join(sep))
+ .add2("array", "null", (arr, _s, context) => DefaultFunctions.join(context, arr, ", "))
+ .add2("*", "string", (elem, sep, ctx) => Values.toString(elem, ctx.settings))
+ .add1("array", (arr, context) => DefaultFunctions.join(context, arr, ", "))
+ .add1("*", (e, ctx) => Values.toString(e, ctx.settings))
+ .vectorize(2, [1])
+ .build();
+ DefaultFunctions.any = new FunctionBuilder("any")
+ .add1("array", arr => arr.some(v => Values.isTruthy(v)))
+ .add2("array", "function", (arr, f, ctx) => arr.some(v => Values.isTruthy(f(ctx, v))))
+ .vararg((_ctx, ...args) => args.some(v => Values.isTruthy(v)))
+ .build();
+ DefaultFunctions.all = new FunctionBuilder("all")
+ .add1("array", arr => arr.every(v => Values.isTruthy(v)))
+ .add2("array", "function", (arr, f, ctx) => arr.every(v => Values.isTruthy(f(ctx, v))))
+ .vararg((_ctx, ...args) => args.every(v => Values.isTruthy(v)))
+ .build();
+ DefaultFunctions.none = new FunctionBuilder("all")
+ .add1("array", arr => !arr.some(v => Values.isTruthy(v)))
+ .add2("array", "function", (arr, f, ctx) => !arr.some(v => Values.isTruthy(f(ctx, v))))
+ .vararg((_ctx, ...args) => !args.some(v => Values.isTruthy(v)))
+ .build();
+ DefaultFunctions.filter = new FunctionBuilder("filter")
+ .add2("array", "function", (arr, f, ctx) => arr.filter(v => Values.isTruthy(f(ctx, v))))
+ .add2("null", "*", () => null)
+ .build();
+ DefaultFunctions.unique = new FunctionBuilder("unique")
+ .add1("array", (arr, ctx) => DataArray.wrap(arr, ctx.settings).distinct().array())
+ .add1("null", () => null)
+ .build();
+ DefaultFunctions.map = new FunctionBuilder("map")
+ .add2("array", "function", (arr, f, ctx) => arr.map(v => f(ctx, v)))
+ .add2("null", "*", () => null)
+ .build();
+ DefaultFunctions.nonnull = new FunctionBuilder("nonnull")
+ .add1("array", arr => arr.filter(v => Values.typeOf(v) != "null"))
+ .vararg((_ctx, ...args) => args.filter(v => Values.typeOf(v) != "null"))
+ .build();
+ /** Gets an object containing a link's own properties */
+ DefaultFunctions.meta = new FunctionBuilder("meta")
+ .add1("link", link => ({
+ display: link.display ?? null,
+ embed: link.embed,
+ path: link.path,
+ subpath: link.subpath ?? null,
+ type: link.type,
+ }))
+ .build();
+ // Concatenates sub-array elements into a new array
+ DefaultFunctions.flat = new FunctionBuilder("flat")
+ .add1("array", a => {
+ return a.flat();
+ })
+ .add2("array", "number", (a, n) => {
+ // @ts-ignore
+ return a.flat(n);
+ })
+ .add1("null", () => null)
+ .build();
+ // Slices the array into a new array
+ DefaultFunctions.slice = new FunctionBuilder("slice")
+ .add1("array", a => {
+ return a.slice();
+ })
+ .add2("array", "number", (a, start) => {
+ return a.slice(start);
+ })
+ .add3("array", "number", "number", (a, start, end) => {
+ return a.slice(start, end);
+ })
+ .add1("null", () => null)
+ .build();
+ // Returns the first non-null value from the array as a single element
+ DefaultFunctions.firstvalue = new FunctionBuilder("firstvalue")
+ .add1("array", a => {
+ let nonnull = a.filter(v => Values.typeOf(v) != "null");
+ let res = nonnull.length != 0 ? nonnull[0] : null;
+ return res;
+ })
+ .add1("null", () => null)
+ .build();
+})(DefaultFunctions || (DefaultFunctions = {}));
+/** Default function implementations for the expression evaluator. */
+// Keep functions in same order as they're documented !!
+const DEFAULT_FUNCTIONS = {
+ // Constructors
+ object: DefaultFunctions.object,
+ list: DefaultFunctions.list,
+ array: DefaultFunctions.list,
+ date: DefaultFunctions.date,
+ dur: DefaultFunctions.dur,
+ number: DefaultFunctions.number,
+ string: DefaultFunctions.string,
+ link: DefaultFunctions.link,
+ embed: DefaultFunctions.embed,
+ elink: DefaultFunctions.elink,
+ typeof: DefaultFunctions.typeOf,
+ // Numeric Operations
+ round: DefaultFunctions.round,
+ trunc: DefaultFunctions.trunc,
+ floor: DefaultFunctions.floor,
+ ceil: DefaultFunctions.ceil,
+ min: DefaultFunctions.min,
+ max: DefaultFunctions.max,
+ sum: DefaultFunctions.sum,
+ product: DefaultFunctions.product,
+ average: DefaultFunctions.average,
+ minby: DefaultFunctions.minby,
+ maxby: DefaultFunctions.maxby,
+ // Object, Arrays, and String operations
+ contains: DefaultFunctions.contains,
+ icontains: DefaultFunctions.icontains,
+ econtains: DefaultFunctions.econtains,
+ containsword: DefaultFunctions.containsword,
+ extract: DefaultFunctions.extract,
+ sort: DefaultFunctions.sort,
+ reverse: DefaultFunctions.reverse,
+ length: DefaultFunctions.length,
+ nonnull: DefaultFunctions.nonnull,
+ firstvalue: DefaultFunctions.firstvalue,
+ all: DefaultFunctions.all,
+ any: DefaultFunctions.any,
+ none: DefaultFunctions.none,
+ join: DefaultFunctions.join,
+ filter: DefaultFunctions.filter,
+ map: DefaultFunctions.map,
+ flat: DefaultFunctions.flat,
+ slice: DefaultFunctions.slice,
+ unique: DefaultFunctions.unique,
+ reduce: DefaultFunctions.reduce,
+ // String Operations
+ regextest: DefaultFunctions.regextest,
+ regexmatch: DefaultFunctions.regexmatch,
+ regexreplace: DefaultFunctions.regexreplace,
+ replace: DefaultFunctions.replace,
+ lower: DefaultFunctions.lower,
+ upper: DefaultFunctions.upper,
+ split: DefaultFunctions.split,
+ startswith: DefaultFunctions.startswith,
+ endswith: DefaultFunctions.endswith,
+ padleft: DefaultFunctions.padleft,
+ padright: DefaultFunctions.padright,
+ substring: DefaultFunctions.substring,
+ truncate: DefaultFunctions.truncate,
+ // Utility Operations
+ default: DefaultFunctions.fdefault,
+ ldefault: DefaultFunctions.ldefault,
+ display: DefaultFunctions.display,
+ choice: DefaultFunctions.choice,
+ striptime: DefaultFunctions.striptime,
+ dateformat: DefaultFunctions.dateformat,
+ durationformat: DefaultFunctions.durationformat,
+ currencyformat: DefaultFunctions.currencyformat,
+ localtime: DefaultFunctions.localtime,
+ hash: DefaultFunctions.hash,
+ meta: DefaultFunctions.meta,
+};
+
+/** Provides a global dispatch table for evaluating binary operators, including comparison. */
+/** Provides implementations for binary operators on two types using a registry. */
+class BinaryOpHandler {
+ map;
+ static create() {
+ return new BinaryOpHandler();
+ }
+ constructor() {
+ this.map = new Map();
+ }
+ register(left, op, right, func) {
+ this.map.set(BinaryOpHandler.repr(op, left, right), func);
+ return this;
+ }
+ registerComm(left, op, right, func) {
+ return this.register(left, op, right, func).register(right, op, left, (a, b, ctx) => func(b, a, ctx));
+ }
+ /** Implement a comparison function. */
+ compare(type, compare) {
+ return this.register(type, "<", type, (a, b, ctx) => compare(a, b, ctx) < 0)
+ .register(type, "<=", type, (a, b, ctx) => compare(a, b, ctx) <= 0)
+ .register(type, ">", type, (a, b, ctx) => compare(a, b, ctx) > 0)
+ .register(type, ">=", type, (a, b, ctx) => compare(a, b, ctx) >= 0)
+ .register(type, "=", type, (a, b, ctx) => compare(a, b, ctx) == 0)
+ .register(type, "!=", type, (a, b, ctx) => compare(a, b, ctx) != 0);
+ }
+ /** Attempt to evaluate the given binary operator on the two literal fields. */
+ evaluate(op, left, right, ctx) {
+ let leftType = Values.typeOf(left);
+ let rightType = Values.typeOf(right);
+ if (!leftType)
+ return Result.failure(`Unrecognized value '${left}'`);
+ else if (!rightType)
+ return Result.failure(`Unrecognized value '${right}'`);
+ let handler = this.map.get(BinaryOpHandler.repr(op, leftType, rightType));
+ if (handler)
+ return Result.success(handler(left, right, ctx));
+ // Right-'*' fallback:
+ let handler2 = this.map.get(BinaryOpHandler.repr(op, leftType, "*"));
+ if (handler2)
+ return Result.success(handler2(left, right, ctx));
+ // Left-'*' fallback:
+ let handler3 = this.map.get(BinaryOpHandler.repr(op, "*", rightType));
+ if (handler3)
+ return Result.success(handler3(left, right, ctx));
+ // Double '*' fallback.
+ let handler4 = this.map.get(BinaryOpHandler.repr(op, "*", "*"));
+ if (handler4)
+ return Result.success(handler4(left, right, ctx));
+ return Result.failure(`No implementation found for '${leftType} ${op} ${rightType}'`);
+ }
+ /** Create a string representation of the given triplet for unique lookup in the map. */
+ static repr(op, left, right) {
+ return `${left},${op},${right}`;
+ }
+}
+/** Configure and create a binary OP handler with the given parameters. */
+function createBinaryOps(linkNormalizer) {
+ return (BinaryOpHandler.create()
+ // TODO: Consider not using a universal comparison function.
+ .compare("*", (a, b) => Values.compareValue(a, b, linkNormalizer))
+ // Global boolean operations.
+ .register("*", "&", "*", (a, b) => Values.isTruthy(a) && Values.isTruthy(b))
+ .register("*", "|", "*", (a, b) => Values.isTruthy(a) || Values.isTruthy(b))
+ // Number implementations.
+ .register("number", "+", "number", (a, b) => a + b)
+ .register("number", "-", "number", (a, b) => a - b)
+ .register("number", "*", "number", (a, b) => a * b)
+ .register("number", "/", "number", (a, b) => a / b)
+ .register("number", "%", "number", (a, b) => a % b)
+ // String implementations.
+ .register("string", "+", "*", (a, b, ctx) => a + Values.toString(b, ctx.settings))
+ .register("*", "+", "string", (a, b, ctx) => Values.toString(a, ctx.settings) + b)
+ .registerComm("string", "*", "number", (a, b) => (b < 0 ? "" : a.repeat(b)))
+ // Date Operations.
+ .register("date", "-", "date", (a, b) => {
+ return normalizeDuration(a.diff(b, ["years", "months", "days", "hours", "minutes", "seconds", "milliseconds"]));
+ })
+ .register("date", "-", "duration", (a, b) => a.minus(b))
+ .registerComm("date", "+", "duration", (a, b) => a.plus(b))
+ // Duration Operations.
+ .register("duration", "+", "duration", (a, b) => normalizeDuration(a.plus(b)))
+ .register("duration", "-", "duration", (a, b) => normalizeDuration(a.minus(b)))
+ .register("duration", "/", "number", (a, b) => normalizeDuration(a.mapUnits(x => x / b)))
+ .registerComm("duration", "*", "number", (a, b) => normalizeDuration(a.mapUnits(x => x * b)))
+ // Array operations.
+ .register("array", "+", "array", (a, b) => [].concat(a).concat(b))
+ // Object operations.
+ .register("object", "+", "object", (a, b) => Object.assign({}, a, b))
+ // Null handling operators.
+ .register("null", "+", "null", (_a, _b) => null)
+ .register("null", "-", "null", (_a, _b) => null)
+ .register("null", "*", "null", (_a, _b) => null)
+ .register("null", "/", "null", (_a, _b) => null)
+ .register("null", "%", "null", (_a, _b) => null)
+ .register("date", "+", "null", (_a, _b) => null)
+ .register("null", "+", "date", (_a, _b) => null)
+ .register("date", "-", "null", (_a, _b) => null)
+ .register("null", "-", "date", (_a, _b) => null));
+}
+
+/** Core implementation of the query language evaluation engine. */
+/**
+ * Evaluation context that expressions can be evaluated in. Includes global state, as well as available functions and a handler
+ * for binary operators.
+ */
+class Context {
+ linkHandler;
+ settings;
+ globals;
+ binaryOps;
+ functions;
+ /**
+ * Create a new context with the given namespace of globals, as well as optionally with custom binary operator, function,
+ * and link handlers.
+ */
+ constructor(linkHandler, settings, globals = {}, binaryOps = createBinaryOps(linkHandler.normalize), functions = DEFAULT_FUNCTIONS) {
+ this.linkHandler = linkHandler;
+ this.settings = settings;
+ this.globals = globals;
+ this.binaryOps = binaryOps;
+ this.functions = functions;
+ }
+ /** Set a global value in this context. */
+ set(name, value) {
+ this.globals[name] = value;
+ return this;
+ }
+ /** Get the value of a global variable by name. Returns null if not present. */
+ get(name) {
+ return this.globals[name] ?? null;
+ }
+ /** Try to evaluate an arbitrary field in this context, raising an exception on failure. */
+ tryEvaluate(field, data = {}) {
+ return this.evaluate(field, data).orElseThrow();
+ }
+ /** Evaluate an arbitrary field in this context. */
+ evaluate(field, data = {}) {
+ switch (field.type) {
+ case "literal":
+ return Result.success(field.value);
+ case "variable":
+ if (field.name in data)
+ return Result.success(data[field.name]);
+ else if (field.name in this.globals)
+ return Result.success(this.globals[field.name]);
+ else
+ return Result.success(null);
+ case "negated":
+ return this.evaluate(field.child, data).map(s => !Values.isTruthy(s));
+ case "binaryop":
+ return Result.flatMap2(this.evaluate(field.left, data), this.evaluate(field.right, data), (a, b) => this.binaryOps.evaluate(field.op, a, b, this));
+ case "list":
+ let result = [];
+ for (let child of field.values) {
+ let subeval = this.evaluate(child, data);
+ if (!subeval.successful)
+ return subeval;
+ result.push(subeval.value);
+ }
+ return Result.success(result);
+ case "object":
+ let objResult = {};
+ for (let [key, child] of Object.entries(field.values)) {
+ let subeval = this.evaluate(child, data);
+ if (!subeval.successful)
+ return subeval;
+ objResult[key] = subeval.value;
+ }
+ return Result.success(objResult);
+ case "lambda":
+ // Just relying on JS to capture 'data' for us implicitly; unsure
+ // if this is correct thing to do. Could cause weird behaviors.
+ return Result.success((ctx, ...args) => {
+ let copy = Object.assign({}, data);
+ for (let arg = 0; arg < Math.min(args.length, field.arguments.length); arg++) {
+ copy[field.arguments[arg]] = args[arg];
+ }
+ return ctx.evaluate(field.value, copy).orElseThrow();
+ });
+ case "function":
+ let rawFunc = field.func.type == "variable"
+ ? Result.success(field.func.name)
+ : this.evaluate(field.func, data);
+ if (!rawFunc.successful)
+ return rawFunc;
+ let func = rawFunc.value;
+ let args = [];
+ for (let arg of field.arguments) {
+ let resolved = this.evaluate(arg, data);
+ if (!resolved.successful)
+ return resolved;
+ args.push(resolved.value);
+ }
+ let call;
+ if (Values.isFunction(func))
+ call = func;
+ else if (Values.isString(func) && func in this.functions)
+ call = this.functions[func];
+ else if (Values.isString(func))
+ return Result.failure(`Unrecognized function name '${func}'`);
+ else
+ return Result.failure(`Cannot call type '${Values.typeOf(func)}' as a function`);
+ try {
+ return Result.success(call(this, ...args));
+ }
+ catch (e) {
+ return Result.failure(e.message);
+ }
+ case "index":
+ // TODO: Will move this out to an 'primitives' module and add more content to it.
+ let literalIndex = this.evaluate(field.index, data);
+ let checkedIndex = literalIndex.flatMap(s => Values.isString(s) || Values.isNumber(s) || Values.isNull(s)
+ ? Result.success(s)
+ : Result.failure("Can only index with a string or number"));
+ if (!checkedIndex.successful)
+ return checkedIndex;
+ let index = checkedIndex.value;
+ if (Values.isNull(index))
+ return Result.success(null);
+ let checkedObject = field.object.type == "variable" && field.object.name == "row"
+ ? Result.success(Object.assign({}, this.globals, data))
+ : this.evaluate(field.object, data);
+ if (!checkedObject.successful)
+ return checkedObject;
+ let object = Values.wrapValue(checkedObject.value);
+ if (!object)
+ return Result.failure("Unrecognized object to index into: " + object);
+ switch (object.type) {
+ case "object":
+ if (!Values.isString(index))
+ return Result.failure('can only index into objects with strings (a.b or a["b"])');
+ return Result.success(object.value[index] ?? null);
+ case "link":
+ if (!Values.isString(index))
+ return Result.failure('can only index into links with strings (a.b or a["b"])');
+ let linkValue = this.linkHandler.resolve(object.value.path);
+ if (Values.isNull(linkValue))
+ return Result.success(null);
+ return Result.success(linkValue[index] ?? null);
+ case "array":
+ if (Values.isNumber(index)) {
+ if (index >= object.value.length || index < 0)
+ return Result.success(null);
+ else
+ return Result.success(object.value[index]);
+ }
+ else if (Values.isString(index)) {
+ let result = [];
+ for (let value of object.value) {
+ let next = this.evaluate(Fields.index(Fields.literal(value), Fields.literal(index)));
+ if (!next.successful)
+ continue;
+ result.push(next.value);
+ }
+ return Result.success(result);
+ }
+ else {
+ return Result.failure("Array indexing requires either a number (to get a specific element), or a string (to map all elements inside the array)");
+ }
+ case "string":
+ if (!Values.isNumber(index))
+ return Result.failure("string indexing requires a numeric index (string[index])");
+ if (index >= object.value.length || index < 0)
+ return Result.success(null);
+ return Result.success(object.value[index]);
+ case "date":
+ if (!Values.isString(index))
+ return Result.failure("date indexing requires a string representing the unit");
+ switch (index) {
+ case "year":
+ return Result.success(object.value.year);
+ case "month":
+ return Result.success(object.value.month);
+ case "weekyear":
+ return Result.success(object.value.weekNumber);
+ case "week":
+ return Result.success(Math.floor(object.value.day / 7) + 1);
+ case "weekday":
+ return Result.success(object.value.weekday);
+ case "day":
+ return Result.success(object.value.day);
+ case "hour":
+ return Result.success(object.value.hour);
+ case "minute":
+ return Result.success(object.value.minute);
+ case "second":
+ return Result.success(object.value.second);
+ case "millisecond":
+ return Result.success(object.value.millisecond);
+ default:
+ return Result.success(null);
+ }
+ case "duration":
+ if (!Values.isString(index))
+ return Result.failure("duration indexing requires a string representing the unit");
+ switch (index) {
+ case "year":
+ case "years":
+ return Result.success(object.value.shiftTo("years").years);
+ case "month":
+ case "months":
+ return Result.success(object.value.shiftTo("months").months);
+ case "weeks":
+ return Result.success(object.value.shiftTo("weeks").weeks);
+ case "day":
+ case "days":
+ return Result.success(object.value.shiftTo("days").days);
+ case "hour":
+ case "hours":
+ return Result.success(object.value.shiftTo("hours").hours);
+ case "minute":
+ case "minutes":
+ return Result.success(object.value.shiftTo("minutes").minutes);
+ case "second":
+ case "seconds":
+ return Result.success(object.value.shiftTo("seconds").seconds);
+ case "millisecond":
+ case "milliseconds":
+ return Result.success(object.value.shiftTo("milliseconds").milliseconds);
+ default:
+ return Result.success(null);
+ }
+ default:
+ return Result.success(null);
+ }
+ }
+ }
+}
+
+function iden(x) {
+ return x;
+}
+/** Shared execution code which just takes in arbitrary data, runs operations over it, and returns it + per-row errors. */
+function executeCore(rows, context, ops) {
+ let diagnostics = [];
+ let identMeaning = { type: "path" };
+ let startTime = Date.now();
+ for (let op of ops) {
+ let opStartTime = Date.now();
+ let incomingRows = rows.length;
+ let errors = [];
+ switch (op.type) {
+ case "where":
+ let whereResult = [];
+ for (let index = 0; index < rows.length; index++) {
+ let row = rows[index];
+ let value = context.evaluate(op.clause, row.data);
+ if (!value.successful)
+ errors.push({ index, message: value.error });
+ else if (Values.isTruthy(value.value))
+ whereResult.push(row);
+ }
+ rows = whereResult;
+ break;
+ case "sort":
+ let sortFields = op.fields;
+ let taggedData = [];
+ outer: for (let index = 0; index < rows.length; index++) {
+ let row = rows[index];
+ let rowSorts = [];
+ for (let sIndex = 0; sIndex < sortFields.length; sIndex++) {
+ let value = context.evaluate(sortFields[sIndex].field, row.data);
+ if (!value.successful) {
+ errors.push({ index, message: value.error });
+ continue outer;
+ }
+ rowSorts.push(value.value);
+ }
+ taggedData.push({ data: row, fields: rowSorts });
+ }
+ // Sort rows by the sort fields, and then return the finished result.
+ taggedData.sort((a, b) => {
+ for (let index = 0; index < sortFields.length; index++) {
+ let factor = sortFields[index].direction === "ascending" ? 1 : -1;
+ let le = context.binaryOps
+ .evaluate("<", a.fields[index], b.fields[index], context)
+ .orElse(false);
+ if (Values.isTruthy(le))
+ return factor * -1;
+ let ge = context.binaryOps
+ .evaluate(">", a.fields[index], b.fields[index], context)
+ .orElse(false);
+ if (Values.isTruthy(ge))
+ return factor * 1;
+ }
+ return 0;
+ });
+ rows = taggedData.map(v => v.data);
+ break;
+ case "limit":
+ let limiting = context.evaluate(op.amount);
+ if (!limiting.successful)
+ return Result.failure("Failed to execute 'limit' statement: " + limiting.error);
+ if (!Values.isNumber(limiting.value))
+ return Result.failure(`Failed to execute 'limit' statement: limit should be a number, but got '${Values.typeOf(limiting.value)}' (${limiting.value})`);
+ rows = rows.slice(0, limiting.value);
+ break;
+ case "group":
+ let groupData = [];
+ for (let index = 0; index < rows.length; index++) {
+ let value = context.evaluate(op.field.field, rows[index].data);
+ if (!value.successful) {
+ errors.push({ index, message: value.error });
+ continue;
+ }
+ groupData.push({ data: rows[index], key: value.value });
+ }
+ // Sort by the key, which we will group on shortly.
+ groupData.sort((a, b) => {
+ let le = context.binaryOps.evaluate("<", a.key, b.key, context).orElse(false);
+ if (Values.isTruthy(le))
+ return -1;
+ let ge = context.binaryOps.evaluate(">", a.key, b.key, context).orElse(false);
+ if (Values.isTruthy(ge))
+ return 1;
+ return 0;
+ });
+ // Then walk through and find fields that are equal.
+ let finalGroupData = [];
+ if (groupData.length > 0)
+ finalGroupData.push({
+ key: groupData[0].key,
+ rows: [groupData[0].data.data],
+ [op.field.name]: groupData[0].key,
+ });
+ for (let index = 1; index < groupData.length; index++) {
+ let curr = groupData[index], prev = groupData[index - 1];
+ if (context.binaryOps.evaluate("=", curr.key, prev.key, context).orElse(false)) {
+ finalGroupData[finalGroupData.length - 1].rows.push(curr.data.data);
+ }
+ else {
+ finalGroupData.push({
+ key: curr.key,
+ rows: [curr.data.data],
+ [op.field.name]: curr.key,
+ });
+ }
+ }
+ rows = finalGroupData.map(d => {
+ return { id: d.key, data: d };
+ });
+ identMeaning = { type: "group", name: op.field.name, on: identMeaning };
+ break;
+ case "flatten":
+ let flattenResult = [];
+ for (let index = 0; index < rows.length; index++) {
+ let row = rows[index];
+ let value = context.evaluate(op.field.field, row.data);
+ if (!value.successful) {
+ errors.push({ index, message: value.error });
+ continue;
+ }
+ let datapoints = Values.isArray(value.value) ? value.value : [value.value];
+ for (let v of datapoints) {
+ let copy = Values.deepCopy(row);
+ copy.data[op.field.name] = v;
+ flattenResult.push(copy);
+ }
+ }
+ rows = flattenResult;
+ if (identMeaning.type == "group" && identMeaning.name == op.field.name)
+ identMeaning = identMeaning.on;
+ break;
+ default:
+ return Result.failure("Unrecognized query operation '" + op.type + "'");
+ }
+ if (errors.length >= incomingRows && incomingRows > 0) {
+ return Result.failure(`Every row during operation '${op.type}' failed with an error; first ${Math.min(3, errors.length)}:\n
+ ${errors
+ .slice(0, 3)
+ .map(d => "- " + d.message)
+ .join("\n")}`);
+ }
+ diagnostics.push({
+ incomingRows,
+ errors,
+ outgoingRows: rows.length,
+ timeMs: Date.now() - opStartTime,
+ });
+ }
+ return Result.success({
+ data: rows,
+ idMeaning: identMeaning,
+ ops,
+ diagnostics,
+ timeMs: Date.now() - startTime,
+ });
+}
+/** Expanded version of executeCore which adds an additional "extraction" step to the pipeline. */
+function executeCoreExtract(rows, context, ops, fields) {
+ let internal = executeCore(rows, context, ops);
+ if (!internal.successful)
+ return internal;
+ let core = internal.value;
+ let startTime = Date.now();
+ let errors = [];
+ let res = [];
+ outer: for (let index = 0; index < core.data.length; index++) {
+ let page = { id: core.data[index].id, data: {} };
+ for (let [name, field] of Object.entries(fields)) {
+ let value = context.evaluate(field, core.data[index].data);
+ if (!value.successful) {
+ errors.push({ index: index, message: value.error });
+ continue outer;
+ }
+ page.data[name] = value.value;
+ }
+ res.push(page);
+ }
+ if (errors.length >= core.data.length && core.data.length > 0) {
+ return Result.failure(`Every row during final data extraction failed with an error; first ${Math.max(errors.length, 3)}:\n
+ ${errors
+ .slice(0, 3)
+ .map(d => "- " + d.message)
+ .join("\n")}`);
+ }
+ let execTime = Date.now() - startTime;
+ return Result.success({
+ data: res,
+ idMeaning: core.idMeaning,
+ diagnostics: core.diagnostics.concat([
+ {
+ timeMs: execTime,
+ incomingRows: core.data.length,
+ outgoingRows: res.length,
+ errors,
+ },
+ ]),
+ ops: core.ops.concat([{ type: "extract", fields }]),
+ timeMs: core.timeMs + execTime,
+ });
+}
+/** Execute a list-based query, returning the final results. */
+async function executeList(query, index, origin, settings) {
+ // Start by collecting all of the files that match the 'from' queries.
+ let fileset = await resolveSource(query.source, index, origin);
+ if (!fileset.successful)
+ return Result.failure(fileset.error);
+ // Extract information about the origin page to add to the root context.
+ let rootContext = new Context(defaultLinkHandler(index, origin), settings, {
+ this: index.pages.get(origin)?.serialize(index) ?? {},
+ });
+ let targetField = query.header.format;
+ let showId = query.header.showId;
+ let fields = targetField ? { target: targetField } : {};
+ return executeCoreExtract(fileset.value, rootContext, query.operations, fields).map(core => {
+ let data;
+ if (showId && targetField) {
+ data = core.data.map(p => Widgets.listPair(p.id, p.data["target"] ?? null));
+ }
+ else if (targetField) {
+ data = core.data.map(p => p.data["target"] ?? null);
+ }
+ else {
+ data = core.data.map(p => p.id);
+ }
+ return { primaryMeaning: core.idMeaning, core, data };
+ });
+}
+/** Execute a table query. */
+async function executeTable(query, index, origin, settings) {
+ // Start by collecting all of the files that match the 'from' queries.
+ let fileset = await resolveSource(query.source, index, origin);
+ if (!fileset.successful)
+ return Result.failure(fileset.error);
+ // Extract information about the origin page to add to the root context.
+ let rootContext = new Context(defaultLinkHandler(index, origin), settings, {
+ this: index.pages.get(origin)?.serialize(index) ?? {},
+ });
+ let targetFields = query.header.fields;
+ let showId = query.header.showId;
+ let fields = {};
+ for (let field of targetFields)
+ fields[field.name] = field.field;
+ return executeCoreExtract(fileset.value, rootContext, query.operations, fields).map(core => {
+ if (showId) {
+ const idName = core.idMeaning.type === "group" ? core.idMeaning.name : settings.tableIdColumnName;
+ let names = [idName].concat(targetFields.map(f => f.name));
+ let data = core.data.map(p => [p.id].concat(targetFields.map(f => p.data[f.name])));
+ return { core, names, data, idMeaning: core.idMeaning };
+ }
+ else {
+ let names = targetFields.map(f => f.name);
+ let data = core.data.map(p => targetFields.map(f => p.data[f.name]));
+ return { core, names, data, idMeaning: core.idMeaning };
+ }
+ });
+}
+/** Maps a raw core execution result to a task grouping which is much easier to render. */
+function extractTaskGroupings(id, rows) {
+ switch (id.type) {
+ case "path":
+ return rows;
+ case "group":
+ let key = id.name;
+ return rows.map(r => iden({
+ key: r[key],
+ rows: extractTaskGroupings(id.on, r.rows),
+ }));
+ }
+}
+/** Execute a task query, returning all matching tasks. */
+async function executeTask(query, origin, index, settings) {
+ let fileset = matchingSourcePaths(query.source, index, origin);
+ if (!fileset.successful)
+ return Result.failure(fileset.error);
+ // Collect tasks from pages which match.
+ let incomingTasks = [];
+ for (let path of fileset.value) {
+ let page = index.pages.get(path);
+ if (!page)
+ continue;
+ let pageData = page.serialize(index);
+ let pageTasks = pageData.file.tasks.map(t => {
+ const tcopy = Values.deepCopy(t);
+ // Add page data to this copy.
+ for (let [key, value] of Object.entries(pageData)) {
+ if (key in tcopy)
+ continue;
+ tcopy[key] = value;
+ }
+ return { id: `${pageData.path}#${t.line}`, data: tcopy };
+ });
+ for (let task of pageTasks)
+ incomingTasks.push(task);
+ }
+ // Extract information about the origin page to add to the root context.
+ let rootContext = new Context(defaultLinkHandler(index, origin), settings, {
+ this: index.pages.get(origin)?.serialize(index) ?? {},
+ });
+ return executeCore(incomingTasks, rootContext, query.operations).map(core => {
+ return {
+ core,
+ tasks: extractTaskGroupings(core.idMeaning, core.data.map(r => r.data)),
+ };
+ });
+}
+/** Execute a single field inline a file, returning the evaluated result. */
+function executeInline(field, origin, index, settings) {
+ return new Context(defaultLinkHandler(index, origin), settings, {
+ this: index.pages.get(origin)?.serialize(index) ?? {},
+ }).evaluate(field);
+}
+/** The default link resolver used when creating contexts. */
+function defaultLinkHandler(index, origin) {
+ return {
+ resolve: link => {
+ let realFile = index.metadataCache.getFirstLinkpathDest(link, origin);
+ if (!realFile)
+ return null;
+ let realPage = index.pages.get(realFile.path);
+ if (!realPage)
+ return null;
+ return realPage.serialize(index);
+ },
+ normalize: link => {
+ let realFile = index.metadataCache.getFirstLinkpathDest(link, origin);
+ return realFile?.path ?? link;
+ },
+ exists: link => {
+ let realFile = index.metadataCache.getFirstLinkpathDest(link, origin);
+ return !!realFile;
+ },
+ };
+}
+/** Execute a calendar-based query, returning the final results. */
+async function executeCalendar(query, index, origin, settings) {
+ // Start by collecting all of the files that match the 'from' queries.
+ let fileset = await resolveSource(query.source, index, origin);
+ if (!fileset.successful)
+ return Result.failure(fileset.error);
+ // Extract information about the origin page to add to the root context.
+ let rootContext = new Context(defaultLinkHandler(index, origin), settings, {
+ this: index.pages.get(origin)?.serialize(index) ?? {},
+ });
+ let targetField = query.header.field.field;
+ let fields = {
+ target: targetField,
+ link: Fields.indexVariable("file.link"),
+ };
+ return executeCoreExtract(fileset.value, rootContext, query.operations, fields).map(core => {
+ let data = core.data.map(p => iden({
+ date: p.data["target"],
+ link: p.data["link"],
+ }));
+ return { core, data };
+ });
+}
+
+function compareVersions(v1, v2) {
+ // validate input and split into segments
+ const n1 = validateAndParse(v1);
+ const n2 = validateAndParse(v2);
+
+ // pop off the patch
+ const p1 = n1.pop();
+ const p2 = n2.pop();
+
+ // validate numbers
+ const r = compareSegments(n1, n2);
+ if (r !== 0) return r;
+
+ // validate pre-release
+ if (p1 && p2) {
+ return compareSegments(p1.split('.'), p2.split('.'));
+ } else if (p1 || p2) {
+ return p1 ? -1 : 1;
+ }
+
+ return 0;
+}
+
+const validate = (v) =>
+ typeof v === 'string' && /^[v\d]/.test(v) && semver.test(v);
+
+const compare = (v1, v2, operator) => {
+ // validate input operator
+ assertValidOperator(operator);
+
+ // since result of compareVersions can only be -1 or 0 or 1
+ // a simple map can be used to replace switch
+ const res = compareVersions(v1, v2);
+
+ return operatorResMap[operator].includes(res);
+};
+
+const satisfies = (v, r) => {
+ // if no range operator then "="
+ const m = r.match(/^([<>=~^]+)/);
+ const op = m ? m[1] : '=';
+
+ // if gt/lt/eq then operator compare
+ if (op !== '^' && op !== '~') return compare(v, r, op);
+
+ // else range of either "~" or "^" is assumed
+ const [v1, v2, v3] = validateAndParse(v);
+ const [r1, r2, r3] = validateAndParse(r);
+ if (compareStrings(v1, r1) !== 0) return false;
+ if (op === '^') {
+ return compareSegments([v2, v3], [r2, r3]) >= 0;
+ }
+ if (compareStrings(v2, r2) !== 0) return false;
+ return compareStrings(v3, r3) >= 0;
+};
+
+// export CJS style for parity
+compareVersions.validate = validate;
+compareVersions.compare = compare;
+compareVersions.satisfies = satisfies;
+
+const semver =
+ /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i;
+
+const validateAndParse = (v) => {
+ if (typeof v !== 'string') {
+ throw new TypeError('Invalid argument expected string');
+ }
+ const match = v.match(semver);
+ if (!match) {
+ throw new Error(`Invalid argument not valid semver ('${v}' received)`);
+ }
+ match.shift();
+ return match;
+};
+
+const isWildcard = (s) => s === '*' || s === 'x' || s === 'X';
+
+const tryParse = (v) => {
+ const n = parseInt(v, 10);
+ return isNaN(n) ? v : n;
+};
+
+const forceType = (a, b) =>
+ typeof a !== typeof b ? [String(a), String(b)] : [a, b];
+
+const compareStrings = (a, b) => {
+ if (isWildcard(a) || isWildcard(b)) return 0;
+ const [ap, bp] = forceType(tryParse(a), tryParse(b));
+ if (ap > bp) return 1;
+ if (ap < bp) return -1;
+ return 0;
+};
+
+const compareSegments = (a, b) => {
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
+ const r = compareStrings(a[i] || 0, b[i] || 0);
+ if (r !== 0) return r;
+ }
+ return 0;
+};
+
+const operatorResMap = {
+ '>': [1],
+ '>=': [0, 1],
+ '=': [0],
+ '<=': [-1, 0],
+ '<': [-1],
+};
+
+const allowedOperators = Object.keys(operatorResMap);
+
+const assertValidOperator = (op) => {
+ if (typeof op !== 'string') {
+ throw new TypeError(
+ `Invalid operator type, expected string but got ${typeof op}`
+ );
+ }
+ if (allowedOperators.indexOf(op) === -1) {
+ throw new Error(
+ `Invalid operator, expected one of ${allowedOperators.join('|')}`
+ );
+ }
+};
+
+var n,l$1,u$1,i$1,o$1,r$1,f$1,e$1,c$1={},s$1=[],a$1=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,h$1=Array.isArray;function v$1(n,l){for(var u in l)n[u]=l[u];return n}function p$1(n){var l=n.parentNode;l&&l.removeChild(n);}function y$1(l,u,t){var i,o,r,f={};for(r in u)"key"==r?i=u[r]:"ref"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return d$1(l,f,i,o,null)}function d$1(n,t,i,o,r){var f={type:n,props:t,key:i,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u$1:r};return null==r&&null!=l$1.vnode&&l$1.vnode(f),f}function _$1(){return {current:null}}function k$2(n){return n.children}function b$1(n,l){this.props=n,this.context=l;}function g$2(n,l){if(null==l)return n.__?g$2(n.__,n.__.__k.indexOf(n)+1):null;for(var u;ll&&i$1.sort(f$1));x$2.__r=0;}function P$1(n,l,u,t,i,o,r,f,e,a,v){var p,y,_,b,m,w,x,P,C,H=0,I=t&&t.__k||s$1,T=I.length,j=T,z=l.length;for(u.__k=[],p=0;p0?d$1(b.type,b.props,b.key,b.ref?b.ref:null,b.__v):b)?(b.__=u,b.__b=u.__b+1,-1===(P=A$2(b,I,x=p+H,j))?_=c$1:(_=I[P]||c$1,I[P]=void 0,j--),L$1(n,b,_,i,o,r,f,e,a,v),m=b.__e,(y=b.ref)&&_.ref!=y&&(_.ref&&O$1(_.ref,null,b),v.push(y,b.__c||m,b)),null!=m&&(null==w&&(w=m),(C=_===c$1||null===_.__v)?-1==P&&H--:P!==x&&(P===x+1?H++:P>x?j>z-x?H+=P-x:H--:H=P(null!=e?1:0))for(;r>=0||f=0){if((e=l[r])&&i==e.key&&o===e.type)return r;r--;}if(f2&&(e.children=arguments.length>3?n.call(arguments,2):t),d$1(l.type,e,i||l.key,o||l.ref,null)}function G$1(n,l){var u={__c:l="__cC"+e$1++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,t;return this.getChildContext||(u=[],(t={})[l]=this,this.getChildContext=function(){return t},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(function(n){n.__e=!0,w$2(n);});},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n);};}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=s$1.slice,l$1={__e:function(n,l,u,t){for(var i,o,r;l=l.__;)if((i=l.__c)&&!i.__)try{if((o=i.constructor)&&null!=o.getDerivedStateFromError&&(i.setState(o.getDerivedStateFromError(n)),r=i.__d),null!=i.componentDidCatch&&(i.componentDidCatch(n,t||{}),r=i.__d),r)return i.__E=i}catch(l){n=l;}throw n}},u$1=0,b$1.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=v$1({},this.state),"function"==typeof n&&(n=n(v$1({},u),this.props)),n&&v$1(u,n),null!=n&&this.__v&&(l&&this._sb.push(l),w$2(this));},b$1.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),w$2(this));},b$1.prototype.render=k$2,i$1=[],r$1="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,f$1=function(n,l){return n.__v.__b-l.__v.__b},x$2.__r=0,e$1=0;
+
+var t,r,u,i,o=0,f=[],c=[],e=l$1.__b,a=l$1.__r,v=l$1.diffed,l=l$1.__c,m=l$1.unmount;function d(t,u){l$1.__h&&l$1.__h(r,t,o||u),o=0;var i=r.__H||(r.__H={__:[],__h:[]});return t>=i.__.length&&i.__.push({__V:c}),i.__[t]}function h(n){return o=1,s(B$1,n)}function s(n,u,i){var o=d(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):B$1(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}));}],o.__c=r,!r.u)){var f=function(n,t,r){if(!o.__c.__H)return !0;var u=o.__c.__H.__.filter(function(n){return n.__c});if(u.every(function(n){return !n.__N}))return !c||c.call(this,n,t,r);var i=!1;return u.forEach(function(n){if(n.__N){var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=!0);}}),!(!i&&o.__c.props===n)&&(!c||c.call(this,n,t,r))};r.u=!0;var c=r.shouldComponentUpdate,e=r.componentWillUpdate;r.componentWillUpdate=function(n,t,r){if(this.__e){var u=c;c=void 0,f(n,t,r),c=u;}e&&e.call(this,n,t,r);},r.shouldComponentUpdate=f;}return o.__N||o.__}function p(u,i){var o=d(t++,3);!l$1.__s&&z$1(o.__H,i)&&(o.__=u,o.i=i,r.__H.__h.push(o));}function y(u,i){var o=d(t++,4);!l$1.__s&&z$1(o.__H,i)&&(o.__=u,o.i=i,r.__h.push(o));}function _(n){return o=5,F$1(function(){return {current:n}},[])}function A$1(n,t,r){o=6,y(function(){return "function"==typeof n?(n(t()),function(){return n(null)}):n?(n.current=t(),function(){return n.current=null}):void 0},null==r?r:r.concat(n));}function F$1(n,r){var u=d(t++,7);return z$1(u.__H,r)?(u.__V=n(),u.i=r,u.__h=n,u.__V):u.__}function T$1(n,t){return o=8,F$1(function(){return n},t)}function q$1(n){var u=r.context[n.__c],i=d(t++,9);return i.c=n,u?(null==i.__&&(i.__=!0,u.sub(r)),u.props.value):n.__}function x$1(t,r){l$1.useDebugValue&&l$1.useDebugValue(r?r(t):t);}function V$1(){var n=d(t++,11);if(!n.__){for(var u=r.__v;null!==u&&!u.__m&&null!==u.__;)u=u.__;var i=u.__m||(u.__m=[0,0]);n.__="P"+i[0]+"-"+i[1]++;}return n.__}function b(){for(var t;t=f.shift();)if(t.__P&&t.__H)try{t.__H.__h.forEach(k$1),t.__H.__h.forEach(w$1),t.__H.__h=[];}catch(r){t.__H.__h=[],l$1.__e(r,t.__v);}}l$1.__b=function(n){r=null,e&&e(n);},l$1.__r=function(n){a&&a(n),t=0;var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.forEach(function(n){n.__N&&(n.__=n.__N),n.__V=c,n.__N=n.i=void 0;})):(i.__h.forEach(k$1),i.__h.forEach(w$1),i.__h=[],t=0)),u=r;},l$1.diffed=function(t){v&&v(t);var o=t.__c;o&&o.__H&&(o.__H.__h.length&&(1!==f.push(o)&&i===l$1.requestAnimationFrame||((i=l$1.requestAnimationFrame)||j$1)(b)),o.__H.__.forEach(function(n){n.i&&(n.__H=n.i),n.__V!==c&&(n.__=n.__V),n.i=void 0,n.__V=c;})),u=r=null;},l$1.__c=function(t,r){r.some(function(t){try{t.__h.forEach(k$1),t.__h=t.__h.filter(function(n){return !n.__||w$1(n)});}catch(u){r.some(function(n){n.__h&&(n.__h=[]);}),r=[],l$1.__e(u,t.__v);}}),l&&l(t,r);},l$1.unmount=function(t){m&&m(t);var r,u=t.__c;u&&u.__H&&(u.__H.__.forEach(function(n){try{k$1(n);}catch(n){r=n;}}),u.__H=void 0,r&&l$1.__e(r,u.__v));};var g$1="function"==typeof requestAnimationFrame;function j$1(n){var t,r=function(){clearTimeout(u),g$1&&cancelAnimationFrame(t),setTimeout(n);},u=setTimeout(r,100);g$1&&(t=requestAnimationFrame(r));}function k$1(n){var t=r,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r=t;}function w$1(n){var t=r;n.__c=n.__(),r=t;}function z$1(n,t){return !n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function B$1(n,t){return "function"==typeof t?t(n):t}
+
+function g(n,t){for(var e in t)n[e]=t[e];return n}function C(n,t){for(var e in n)if("__source"!==e&&!(e in t))return !0;for(var r in t)if("__source"!==r&&n[r]!==t[r])return !0;return !1}function E(n,t){return n===t&&(0!==n||1/n==1/t)||n!=n&&t!=t}function w(n){this.props=n;}function x(n,e){function r(n){var t=this.props.ref,r=t==n.ref;return !r&&t&&(t.call?t(null):t.current=null),e?!e(this.props,n)||!r:C(this.props,n)}function u(e){return this.shouldComponentUpdate=r,y$1(n,e)}return u.displayName="Memo("+(n.displayName||n.name)+")",u.prototype.isReactComponent=!0,u.__f=!0,u}(w.prototype=new b$1).isPureReactComponent=!0,w.prototype.shouldComponentUpdate=function(n,t){return C(this.props,n)||C(this.state,t)};var R=l$1.__b;l$1.__b=function(n){n.type&&n.type.__f&&n.ref&&(n.props.ref=n.ref,n.ref=null),R&&R(n);};var N="undefined"!=typeof Symbol&&Symbol.for&&Symbol.for("react.forward_ref")||3911;function k(n){function t(t){var e=g({},t);return delete e.ref,n(e,t.ref||null)}return t.$$typeof=N,t.render=t,t.prototype.isReactComponent=t.__f=!0,t.displayName="ForwardRef("+(n.displayName||n.name)+")",t}var A=function(n,t){return null==n?null:C$1(C$1(n).map(t))},O={map:A,forEach:A,count:function(n){return n?C$1(n).length:0},only:function(n){var t=C$1(n);if(1!==t.length)throw "Children.only";return t[0]},toArray:C$1},T=l$1.__e;l$1.__e=function(n,t,e,r){if(n.then)for(var u,o=t;o=o.__;)if((u=o.__c)&&u.__c)return null==t.__e&&(t.__e=e.__e,t.__k=e.__k),u.__c(n,t);T(n,t,e,r);};var F=l$1.unmount;function I(n,t,e){return n&&(n.__c&&n.__c.__H&&(n.__c.__H.__.forEach(function(n){"function"==typeof n.__c&&n.__c();}),n.__c.__H=null),null!=(n=g({},n)).__c&&(n.__c.__P===e&&(n.__c.__P=t),n.__c=null),n.__k=n.__k&&n.__k.map(function(n){return I(n,t,e)})),n}function L(n,t,e){return n&&(n.__v=null,n.__k=n.__k&&n.__k.map(function(n){return L(n,t,e)}),n.__c&&n.__c.__P===t&&(n.__e&&e.insertBefore(n.__e,n.__d),n.__c.__e=!0,n.__c.__P=e)),n}function U(){this.__u=0,this.t=null,this.__b=null;}function D(n){var t=n.__.__c;return t&&t.__a&&t.__a(n)}function M(n){var e,r,u;function o(o){if(e||(e=n()).then(function(n){r=n.default||n;},function(n){u=n;}),u)throw u;if(!r)throw e;return y$1(r,o)}return o.displayName="Lazy",o.__f=!0,o}function V(){this.u=null,this.o=null;}l$1.unmount=function(n){var t=n.__c;t&&t.__R&&t.__R(),t&&!0===n.__h&&(n.type=null),F&&F(n);},(U.prototype=new b$1).__c=function(n,t){var e=t.__c,r=this;null==r.t&&(r.t=[]),r.t.push(e);var u=D(r.__v),o=!1,i=function(){o||(o=!0,e.__R=null,u?u(l):l());};e.__R=i;var l=function(){if(!--r.__u){if(r.state.__a){var n=r.state.__a;r.__v.__k[0]=L(n,n.__c.__P,n.__c.__O);}var t;for(r.setState({__a:r.__b=null});t=r.t.pop();)t.forceUpdate();}},c=!0===t.__h;r.__u++||c||r.setState({__a:r.__b=r.__v.__k[0]}),n.then(i,i);},U.prototype.componentWillUnmount=function(){this.t=[];},U.prototype.render=function(n,e){if(this.__b){if(this.__v.__k){var r=document.createElement("div"),o=this.__v.__k[0].__c;this.__v.__k[0]=I(this.__b,r,o.__O=o.__P);}this.__b=null;}var i=e.__a&&y$1(k$2,null,n.fallback);return i&&(i.__h=null),[y$1(k$2,null,e.__a?null:n.children),i]};var W=function(n,t,e){if(++e[1]===e[0]&&n.o.delete(t),n.props.revealOrder&&("t"!==n.props.revealOrder[0]||!n.o.size))for(e=n.u;e;){for(;e.length>3;)e.pop()();if(e[1]>>1,1),e.i.removeChild(n);}}),D$1(y$1(P,{context:e.context},n.__v),e.l);}function z(n,e){var r=y$1(j,{__v:n,i:e});return r.containerInfo=e,r}(V.prototype=new b$1).__a=function(n){var t=this,e=D(t.__v),r=t.o.get(n);return r[0]++,function(u){var o=function(){t.props.revealOrder?(r.push(u),W(t,n,r)):u();};e?e(o):o();}},V.prototype.render=function(n){this.u=null,this.o=new Map;var t=C$1(n.children);n.revealOrder&&"b"===n.revealOrder[0]&&t.reverse();for(var e=t.length;e--;)this.o.set(t[e],this.u=[1,0,this.u]);return n.children},V.prototype.componentDidUpdate=V.prototype.componentDidMount=function(){var n=this;this.o.forEach(function(t,e){W(n,e,t);});};var B="undefined"!=typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103,H=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,Z=/^on(Ani|Tra|Tou|BeforeInp|Compo)/,Y=/[A-Z0-9]/g,$="undefined"!=typeof document,q=function(n){return ("undefined"!=typeof Symbol&&"symbol"==typeof Symbol()?/fil|che|rad/:/fil|che|ra/).test(n)};function G(n,t,e){return null==t.__k&&(t.textContent=""),D$1(n,t),"function"==typeof e&&e(),n?n.__c:null}function J(n,t,e){return E$1(n,t),"function"==typeof e&&e(),n?n.__c:null}b$1.prototype.isReactComponent={},["componentWillMount","componentWillReceiveProps","componentWillUpdate"].forEach(function(t){Object.defineProperty(b$1.prototype,t,{configurable:!0,get:function(){return this["UNSAFE_"+t]},set:function(n){Object.defineProperty(this,t,{configurable:!0,writable:!0,value:n});}});});var K=l$1.event;function Q(){}function X(){return this.cancelBubble}function nn(){return this.defaultPrevented}l$1.event=function(n){return K&&(n=K(n)),n.persist=Q,n.isPropagationStopped=X,n.isDefaultPrevented=nn,n.nativeEvent=n};var tn,en={enumerable:!1,configurable:!0,get:function(){return this.class}},rn=l$1.vnode;l$1.vnode=function(n){"string"==typeof n.type&&function(n){var t=n.props,e=n.type,u={};for(var o in t){var i=t[o];if(!("value"===o&&"defaultValue"in t&&null==i||$&&"children"===o&&"noscript"===e||"class"===o||"className"===o)){var l=o.toLowerCase();"defaultValue"===o&&"value"in t&&null==t.value?o="value":"download"===o&&!0===i?i="":"ondoubleclick"===l?o="ondblclick":"onchange"!==l||"input"!==e&&"textarea"!==e||q(t.type)?"onfocus"===l?o="onfocusin":"onblur"===l?o="onfocusout":Z.test(o)?o=l:-1===e.indexOf("-")&&H.test(o)?o=o.replace(Y,"-$&").toLowerCase():null===i&&(i=void 0):l=o="oninput","oninput"===l&&u[o=l]&&(o="oninputCapture"),u[o]=i;}}"select"==e&&u.multiple&&Array.isArray(u.value)&&(u.value=C$1(t.children).forEach(function(n){n.props.selected=-1!=u.value.indexOf(n.props.value);})),"select"==e&&null!=u.defaultValue&&(u.value=C$1(t.children).forEach(function(n){n.props.selected=u.multiple?-1!=u.defaultValue.indexOf(n.props.value):u.defaultValue==n.props.value;})),t.class&&!t.className?(u.class=t.class,Object.defineProperty(u,"className",en)):(t.className&&!t.class||t.class&&t.className)&&(u.class=u.className=t.className),n.props=u;}(n),n.$$typeof=B,rn&&rn(n);};var un=l$1.__r;l$1.__r=function(n){un&&un(n),tn=n.__c;};var on=l$1.diffed;l$1.diffed=function(n){on&&on(n);var t=n.props,e=n.__e;null!=e&&"textarea"===n.type&&"value"in t&&t.value!==e.value&&(e.value=null==t.value?"":t.value),tn=null;};var ln={ReactCurrentDispatcher:{current:{readContext:function(n){return tn.__n[n.__c].props.value}}}};function fn(n){return y$1.bind(null,n)}function an(n){return !!n&&n.$$typeof===B}function sn(n){return an(n)&&n.type===k$2}function hn(n){return an(n)?F$2.apply(null,arguments):n}function vn(n){return !!n.__k&&(D$1(null,n),!0)}function dn(n){return n&&(n.base||1===n.nodeType&&n)||null}var pn=function(n,t){return n(t)},mn=function(n,t){return n(t)},yn=k$2;function _n(n){n();}function bn(n){return n}function Sn(){return [!1,_n]}var gn=y,Cn=an;function En(n,t){var e=t(),r=h({h:{__:e,v:t}}),u=r[0].h,o=r[1];return y(function(){u.__=e,u.v=t,E(u.__,t())||o({h:u});},[n,e,t]),p(function(){return E(u.__,u.v())||o({h:u}),n(function(){E(u.__,u.v())||o({h:u});})},[n]),e}var wn={useState:h,useId:V$1,useReducer:s,useEffect:p,useLayoutEffect:y,useInsertionEffect:gn,useTransition:Sn,useDeferredValue:bn,useSyncExternalStore:En,startTransition:_n,useRef:_,useImperativeHandle:A$1,useMemo:F$1,useCallback:T$1,useContext:q$1,useDebugValue:x$1,version:"17.0.2",Children:O,render:G,hydrate:J,unmountComponentAtNode:vn,createPortal:z,createElement:y$1,createContext:G$1,createFactory:fn,cloneElement:hn,createRef:_$1,Fragment:k$2,isValidElement:an,isElement:Cn,isFragment:sn,findDOMNode:dn,Component:b$1,PureComponent:w,memo:x,forwardRef:k,flushSync:mn,unstable_batchedUpdates:pn,StrictMode:yn,Suspense:U,SuspenseList:V,lazy:M,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:ln};
+
+const IMAGE_EXTENSIONS = Object.freeze(new Set([
+ ".tif",
+ ".tiff",
+ ".gif",
+ ".png",
+ ".apng",
+ ".avif",
+ ".jpg",
+ ".jpeg",
+ ".jfif",
+ ".pjepg",
+ ".pjp",
+ ".svg",
+ ".webp",
+ ".bmp",
+ ".ico",
+ ".cur",
+]));
+/** Determines if the given link points to an embedded image. */
+function isImageEmbed(link) {
+ if (!link.path.contains("."))
+ return false;
+ let extension = link.path.substring(link.path.lastIndexOf("."));
+ return link.type == "file" && link.embed && IMAGE_EXTENSIONS.has(extension);
+}
+/** Extract text of the form 'WxH' or 'W' from the display of a link. */
+function extractImageDimensions(link) {
+ if (!link.display)
+ return undefined;
+ let match = /^(\d+)x(\d+)$/iu.exec(link.display);
+ if (match)
+ return [parseInt(match[1]), parseInt(match[2])];
+ let match2 = /^(\d+)/.exec(link.display);
+ if (match2)
+ return [parseInt(match2[1])];
+ // No match.
+ return undefined;
+}
+
+/** Provides core preact / rendering utilities for all view types. */
+const DataviewContext = G$1(undefined);
+/** Hacky preact component which wraps Obsidian's markdown renderer into a neat component. */
+function RawMarkdown({ content, sourcePath, inline = true, style, cls, onClick, }) {
+ const container = _(null);
+ const component = q$1(DataviewContext).component;
+ p(() => {
+ if (!container.current)
+ return;
+ container.current.innerHTML = "";
+ obsidian.MarkdownRenderer.renderMarkdown(content, container.current, sourcePath, component).then(() => {
+ if (!container.current || !inline)
+ return;
+ // Unwrap any created paragraph elements if we are inline.
+ let paragraph = container.current.querySelector("p");
+ while (paragraph) {
+ let children = paragraph.childNodes;
+ paragraph.replaceWith(...Array.from(children));
+ paragraph = container.current.querySelector("p");
+ }
+ });
+ }, [content, sourcePath, container.current]);
+ return y$1("span", { ref: container, style: style, class: cls, onClick: onClick });
+}
+/** Hacky preact component which wraps Obsidian's markdown renderer into a neat component. */
+const Markdown = wn.memo(RawMarkdown);
+/** Embeds an HTML element in the react DOM. */
+function RawEmbedHtml({ element }) {
+ const container = _(null);
+ p(() => {
+ if (!container.current)
+ return;
+ container.current.innerHTML = "";
+ container.current.appendChild(element);
+ }, [container.current, element]);
+ return y$1("span", { ref: container });
+}
+/** Embeds an HTML element in the react DOM. */
+const EmbedHtml = wn.memo(RawEmbedHtml);
+/** Intelligently render an arbitrary literal value. */
+function RawLit({ value, sourcePath, inline = false, depth = 0, }) {
+ const context = q$1(DataviewContext);
+ // Short-circuit if beyond the maximum render depth.
+ if (depth >= context.settings.maxRecursiveRenderDepth)
+ return y$1(k$2, null, "...");
+ if (Values.isNull(value) || value === undefined) {
+ return y$1(Markdown, { content: context.settings.renderNullAs, sourcePath: sourcePath });
+ }
+ else if (Values.isString(value)) {
+ return y$1(Markdown, { content: value, sourcePath: sourcePath });
+ }
+ else if (Values.isNumber(value)) {
+ return y$1(k$2, null, "" + value);
+ }
+ else if (Values.isBoolean(value)) {
+ return y$1(k$2, null, "" + value);
+ }
+ else if (Values.isDate(value)) {
+ return y$1(k$2, null, renderMinimalDate(value, context.settings, currentLocale()));
+ }
+ else if (Values.isDuration(value)) {
+ return y$1(k$2, null, renderMinimalDuration(value));
+ }
+ else if (Values.isLink(value)) {
+ // Special case handling of image/video/etc embeddings to bypass the Obsidian API not working.
+ if (isImageEmbed(value)) {
+ let realFile = context.app.metadataCache.getFirstLinkpathDest(value.path, sourcePath);
+ if (!realFile)
+ return y$1(Markdown, { content: value.markdown(), sourcePath: sourcePath });
+ let dimensions = extractImageDimensions(value);
+ let resourcePath = context.app.vault.getResourcePath(realFile);
+ if (dimensions && dimensions.length == 2)
+ return y$1("img", { alt: value.path, src: resourcePath, width: dimensions[0], height: dimensions[1] });
+ else if (dimensions && dimensions.length == 1)
+ return y$1("img", { alt: value.path, src: resourcePath, width: dimensions[0] });
+ else
+ return y$1("img", { alt: value.path, src: resourcePath });
+ }
+ return y$1(Markdown, { content: value.markdown(), sourcePath: sourcePath });
+ }
+ else if (Values.isHtml(value)) {
+ return y$1(EmbedHtml, { element: value });
+ }
+ else if (Values.isWidget(value)) {
+ if (Widgets.isListPair(value)) {
+ return (y$1(k$2, null,
+ y$1(Lit, { value: value.key, sourcePath: sourcePath }),
+ ":",
+ " ",
+ y$1(Lit, { value: value.value, sourcePath: sourcePath })));
+ }
+ else if (Widgets.isExternalLink(value)) {
+ return (y$1("a", { href: value.url, rel: "noopener", target: "_blank", class: "external-link" }, value.display ?? value.url));
+ }
+ else {
+ return y$1("b", null,
+ "");
+ }
+ }
+ else if (Values.isFunction(value)) {
+ return y$1(k$2, null, "