diff --git a/app/fts5_scheduler.go b/app/fts5_scheduler.go new file mode 100644 index 0000000..b6b5334 --- /dev/null +++ b/app/fts5_scheduler.go @@ -0,0 +1,40 @@ +package app + +import ( + "time" + + "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/imports" + "github.com/pocketbase/pocketbase/core" +) + +func StartWeeklyFTS5Rebuild(app core.App) { + go func() { + for { + next := nextSundayMidnight(time.Now()) + timer := time.NewTimer(time.Until(next)) + <-timer.C + timer.Stop() + + app.Logger().Info("Starting scheduled FTS5 rebuild", "scheduled_for", next.Format(time.RFC3339)) + status, err := imports.StartFTS5Rebuild(app, false) + if err != nil { + app.Logger().Error("Scheduled FTS5 rebuild failed", "error", err) + } else if status == "running" { + app.Logger().Info("Scheduled FTS5 rebuild skipped (already running)") + } else { + app.Logger().Info("Scheduled FTS5 rebuild started") + } + } + }() +} + +func nextSundayMidnight(now time.Time) time.Time { + local := now.In(time.Local) + daysUntil := (7 - int(local.Weekday())) % 7 + base := time.Date(local.Year(), local.Month(), local.Day(), 0, 0, 0, 0, local.Location()) + next := base.AddDate(0, 0, daysUntil) + if !next.After(local) { + next = next.AddDate(0, 0, 7) + } + return next +} diff --git a/app/pb.go b/app/pb.go index fb5fe99..47cc8f6 100644 --- a/app/pb.go +++ b/app/pb.go @@ -747,6 +747,8 @@ func (app *App) bindPages(engine *templating.Engine) ServeFunc { page.Setup(e.Router, app, engine) } + StartWeeklyFTS5Rebuild(e.App) + return e.Next() } } diff --git a/controllers/exports_admin.go b/controllers/exports_admin.go index c524b6f..194b9f0 100644 --- a/controllers/exports_admin.go +++ b/controllers/exports_admin.go @@ -5,12 +5,14 @@ import ( "net/http" "os" "path/filepath" + "strconv" "strings" "time" "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/exports" + "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/imports" "github.com/Theodor-Springmann-Stiftung/musenalm/middleware" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" "github.com/Theodor-Springmann-Stiftung/musenalm/templating" @@ -20,14 +22,17 @@ import ( ) const ( - URL_EXPORTS_ADMIN = "/redaktion/exports/" - URL_EXPORTS_RUN = "run/" - URL_EXPORTS_LIST = "list/" - URL_EXPORTS_DOWNLOAD = "download/" - URL_EXPORTS_DELETE = "delete/" - TEMPLATE_EXPORTS = "/redaktion/exports/" - TEMPLATE_EXPORTS_LIST = "/redaktion/exports/list/" - LAYOUT_EXPORTS_FRAGMENT = "fragment" + URL_EXPORTS_ADMIN = "/redaktion/exports/" + URL_EXPORTS_RUN = "run/" + URL_EXPORTS_LIST = "list/" + URL_EXPORTS_DOWNLOAD = "download/" + URL_EXPORTS_DELETE = "delete/" + URL_EXPORTS_SETTINGS = "settings/" + URL_EXPORTS_FTS5_REBUILD = "fts5/rebuild/" + URL_EXPORTS_FTS5_STATUS = "fts5/status/" + TEMPLATE_EXPORTS = "/redaktion/exports/" + TEMPLATE_EXPORTS_LIST = "/redaktion/exports/list/" + LAYOUT_EXPORTS_FRAGMENT = "fragment" ) func init() { @@ -56,6 +61,10 @@ func (p *ExportsAdmin) Setup(router *router.Router[*core.RequestEvent], ia pagem rg.POST(URL_EXPORTS_RUN, p.runHandler(appInstance)) rg.GET(URL_EXPORTS_DOWNLOAD+"{id}", p.downloadHandler(appInstance)) rg.POST(URL_EXPORTS_DELETE+"{id}", p.deleteHandler(appInstance)) + rg.POST(URL_EXPORTS_SETTINGS+"save/", handleSettingSave(appInstance, URL_EXPORTS_ADMIN)) + rg.POST(URL_EXPORTS_SETTINGS+"delete/", handleSettingDelete(appInstance, URL_EXPORTS_ADMIN)) + rg.POST(URL_EXPORTS_FTS5_REBUILD, p.fts5RunHandler(appInstance)) + rg.GET(URL_EXPORTS_FTS5_STATUS, p.fts5StatusHandler(appInstance)) return nil } @@ -82,6 +91,12 @@ func (p *ExportsAdmin) pageHandler(engine *templating.Engine, app core.App) Hand if err != nil { return engine.Response500(e, err, nil) } + if msg := popFlashSuccess(e); msg != "" { + data["success"] = msg + } + if errMsg := strings.TrimSpace(e.Request.URL.Query().Get("error")); errMsg != "" { + data["error"] = errMsg + } return engine.Response200(e, TEMPLATE_EXPORTS, data, pagemodels.LAYOUT_LOGIN_PAGES) } } @@ -96,6 +111,61 @@ func (p *ExportsAdmin) listHandler(engine *templating.Engine, app core.App) Hand } } +func (p *ExportsAdmin) fts5RunHandler(app core.App) HandleFunc { + return func(e *core.RequestEvent) error { + req := templating.NewRequest(e) + if err := e.Request.ParseForm(); err != nil { + return e.JSON(http.StatusBadRequest, map[string]any{"error": "Formulardaten ungueltig."}) + } + if err := req.CheckCSRF(e.Request.FormValue("csrf_token")); err != nil { + return e.JSON(http.StatusUnauthorized, map[string]any{"error": err.Error()}) + } + + status, err := imports.StartFTS5Rebuild(app, true) + if err != nil { + return e.JSON(http.StatusInternalServerError, map[string]any{"error": err.Error()}) + } + + if status == "running" { + return e.JSON(http.StatusConflict, map[string]any{"error": "FTS5-Neuaufbau läuft bereits."}) + } + if status == "restarting" { + return e.JSON(http.StatusOK, map[string]any{"success": true, "status": "restarting"}) + } + + return e.JSON(http.StatusOK, map[string]any{"success": true, "status": "started"}) + } +} + +func (p *ExportsAdmin) fts5StatusHandler(app core.App) HandleFunc { + return func(e *core.RequestEvent) error { + status := settingString(app, "fts5_rebuild_status") + if status == "" { + status = "idle" + } + message := normalizeGermanMessage(settingString(app, "fts5_rebuild_message")) + errMsg := normalizeGermanMessage(settingString(app, "fts5_rebuild_error")) + done := 0 + total := 0 + if setting, err := dbmodels.Settings_Key(app, "fts5_rebuild_done"); err == nil && setting != nil { + done = parseSettingInt(setting.Value()) + } + if setting, err := dbmodels.Settings_Key(app, "fts5_rebuild_total"); err == nil && setting != nil { + total = parseSettingInt(setting.Value()) + } + lastRebuild := formatLastRebuild(app) + + return e.JSON(http.StatusOK, map[string]any{ + "status": status, + "message": message, + "error": errMsg, + "done": done, + "total": total, + "last_rebuild": lastRebuild, + }) + } +} + func (p *ExportsAdmin) runHandler(app core.App) HandleFunc { return func(e *core.RequestEvent) error { req := templating.NewRequest(e) @@ -234,7 +304,7 @@ func (p *ExportsAdmin) deleteHandler(app core.App) HandleFunc { status := record.GetString(dbmodels.EXPORT_STATUS_FIELD) if status == dbmodels.EXPORT_STATUS_RUNNING || status == dbmodels.EXPORT_STATUS_QUEUED { - return e.JSON(http.StatusConflict, map[string]any{"error": "Export laeuft noch."}) + return e.JSON(http.StatusConflict, map[string]any{"error": "Export läuft noch."}) } exportDir, err := exports.ExportDir(app) @@ -264,6 +334,10 @@ func exportsData(e *core.RequestEvent, app core.App) (map[string]any, error) { if err != nil { return nil, err } + settings, err := settingsData(app) + if err != nil { + return nil, err + } data["exports"] = exportsList data["has_running"] = hasRunning @@ -271,6 +345,9 @@ func exportsData(e *core.RequestEvent, app core.App) (map[string]any, error) { if req.Session() != nil { data["csrf_token"] = req.Session().Token } + for key, value := range settings { + data[key] = value + } return data, nil } @@ -335,3 +412,73 @@ func formatBytes(size int64) string { } return fmt.Sprintf("%.2f %s", value, units[unitIdx]) } + +func settingString(app core.App, key string) string { + setting, err := dbmodels.Settings_Key(app, key) + if err != nil || setting == nil { + return "" + } + if value, ok := setting.Value().(string); ok { + return normalizeSettingString(value) + } + return normalizeSettingString(fmt.Sprintf("%v", setting.Value())) +} + +func formatLastRebuild(app core.App) string { + setting, err := dbmodels.Settings_Key(app, "fts5_last_rebuild") + if err != nil || setting == nil { + return "" + } + if formatted, ok := formatSettingGermanDateTime(setting.Value()); ok { + return normalizeSettingString(formatted) + } + if value, ok := parseSettingString(setting.Value()); ok { + return normalizeSettingString(value) + } + return normalizeSettingString(fmt.Sprintf("%v", setting.Value())) +} + +func parseSettingInt(value any) int { + switch v := value.(type) { + case float64: + return int(v) + case int: + return v + case int64: + return int(v) + case string: + if parsed, err := strconv.Atoi(v); err == nil { + return parsed + } + default: + if parsed, err := strconv.Atoi(fmt.Sprintf("%v", value)); err == nil { + return parsed + } + } + return 0 +} + +func normalizeSettingString(value string) string { + trimmed := strings.TrimSpace(value) + if len(trimmed) >= 2 && strings.HasPrefix(trimmed, "\"") && strings.HasSuffix(trimmed, "\"") { + if unquoted, err := strconv.Unquote(trimmed); err == nil { + return unquoted + } + return strings.Trim(trimmed, "\"") + } + return trimmed +} + +func normalizeGermanMessage(value string) string { + replacer := strings.NewReplacer( + "laeuft", "läuft", + "Laeuft", "Läuft", + "Loescht", "Löscht", + "loescht", "löscht", + "fuellt", "füllt", + "Fuellt", "Füllt", + "Eintraegen", "Einträgen", + "eintraegen", "einträgen", + ) + return replacer.Replace(value) +} diff --git a/controllers/settings_admin.go b/controllers/settings_admin.go new file mode 100644 index 0000000..757f445 --- /dev/null +++ b/controllers/settings_admin.go @@ -0,0 +1,255 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "sort" + "strings" + + "github.com/Theodor-Springmann-Stiftung/musenalm/app" + "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" + "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/imports" + "github.com/Theodor-Springmann-Stiftung/musenalm/middleware" + "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" + "github.com/Theodor-Springmann-Stiftung/musenalm/templating" + "github.com/pocketbase/pocketbase/core" + "github.com/pocketbase/pocketbase/tools/router" + "github.com/pocketbase/pocketbase/tools/types" +) + +const ( + URL_SETTINGS_ADMIN = "/redaktion/settings/" + URL_SETTINGS_SAVE = "save/" + URL_SETTINGS_DELETE = "delete/" + URL_SETTINGS_FTS5_REBUILD = "fts5/rebuild/" + TEMPLATE_SETTINGS = "/redaktion/settings/" +) + +func init() { + app.Register(&SettingsAdmin{}) +} + +type SettingsAdmin struct{} + +func (p *SettingsAdmin) Up(ia pagemodels.IApp, engine *templating.Engine) error { + return nil +} + +func (p *SettingsAdmin) Down(ia pagemodels.IApp, engine *templating.Engine) error { + return nil +} + +func (p *SettingsAdmin) Setup(router *router.Router[*core.RequestEvent], ia pagemodels.IApp, engine *templating.Engine) error { + appInstance := ia.Core() + rg := router.Group(URL_SETTINGS_ADMIN) + rg.BindFunc(middleware.Authenticated(appInstance)) + rg.BindFunc(middleware.IsAdmin()) + rg.GET("", p.redirectHandler()) + rg.POST(URL_SETTINGS_SAVE, handleSettingSave(appInstance, URL_SETTINGS_ADMIN)) + rg.POST(URL_SETTINGS_DELETE, handleSettingDelete(appInstance, URL_SETTINGS_ADMIN)) + rg.POST(URL_SETTINGS_FTS5_REBUILD, handleFTS5Rebuild(appInstance, URL_SETTINGS_ADMIN)) + return nil +} + +type settingView struct { + Key string + Value string + Updated types.DateTime +} + +func (p *SettingsAdmin) redirectHandler() HandleFunc { + return func(e *core.RequestEvent) error { + return e.Redirect(http.StatusSeeOther, URL_EXPORTS_ADMIN) + } +} + +func handleSettingSave(app core.App, redirectBase string) HandleFunc { + return func(e *core.RequestEvent) error { + req := templating.NewRequest(e) + if err := e.Request.ParseForm(); err != nil { + return redirectSettingsError(e, redirectBase, "Formulardaten ungueltig.") + } + if err := req.CheckCSRF(e.Request.FormValue("csrf_token")); err != nil { + return redirectSettingsError(e, redirectBase, err.Error()) + } + + key := strings.TrimSpace(e.Request.FormValue("key")) + if key == "" { + return redirectSettingsError(e, redirectBase, "Schluessel darf nicht leer sein.") + } + + valueRaw := strings.TrimSpace(e.Request.FormValue("value")) + value := parseSettingValue(valueRaw) + + collection, err := app.FindCollectionByNameOrId(dbmodels.SETTINGS_TABLE) + if err != nil { + return redirectSettingsError(e, redirectBase, "Einstellungen-Tabelle nicht verfuegbar.") + } + + var record *core.Record + existing, err := dbmodels.Settings_Key(app, key) + if err != nil { + if !isRecordNotFound(err) { + return redirectSettingsError(e, redirectBase, "Konnte Einstellung nicht laden.") + } + } else if existing != nil { + record = existing.ProxyRecord() + } + + if record == nil { + record = core.NewRecord(collection) + } + + record.Set(dbmodels.KEY_FIELD, key) + record.Set(dbmodels.VALUE_FIELD, value) + if err := app.Save(record); err != nil { + return redirectSettingsError(e, redirectBase, "Einstellung konnte nicht gespeichert werden.") + } + + setFlashSuccess(e, "Einstellung gespeichert.") + return e.Redirect(http.StatusSeeOther, redirectBase) + } +} + +func handleSettingDelete(app core.App, redirectBase string) HandleFunc { + return func(e *core.RequestEvent) error { + req := templating.NewRequest(e) + if err := e.Request.ParseForm(); err != nil { + return redirectSettingsError(e, redirectBase, "Formulardaten ungueltig.") + } + if err := req.CheckCSRF(e.Request.FormValue("csrf_token")); err != nil { + return redirectSettingsError(e, redirectBase, err.Error()) + } + + key := strings.TrimSpace(e.Request.FormValue("key")) + if key == "" { + return redirectSettingsError(e, redirectBase, "Schluessel darf nicht leer sein.") + } + + record, err := dbmodels.Settings_Key(app, key) + if err != nil { + if isRecordNotFound(err) { + setFlashSuccess(e, "Einstellung entfernt.") + return e.Redirect(http.StatusSeeOther, redirectBase) + } + return redirectSettingsError(e, redirectBase, "Einstellung konnte nicht geladen werden.") + } + + if err := app.Delete(record.ProxyRecord()); err != nil { + return redirectSettingsError(e, redirectBase, "Einstellung konnte nicht entfernt werden.") + } + + setFlashSuccess(e, "Einstellung entfernt.") + return e.Redirect(http.StatusSeeOther, redirectBase) + } +} + +func handleFTS5Rebuild(app core.App, redirectBase string) HandleFunc { + return func(e *core.RequestEvent) error { + req := templating.NewRequest(e) + if err := e.Request.ParseForm(); err != nil { + return redirectSettingsError(e, redirectBase, "Formulardaten ungueltig.") + } + if err := req.CheckCSRF(e.Request.FormValue("csrf_token")); err != nil { + return redirectSettingsError(e, redirectBase, err.Error()) + } + + status, err := imports.StartFTS5Rebuild(app, true) + if err != nil { + return redirectSettingsError(e, redirectBase, err.Error()) + } + if status == "running" { + return redirectSettingsError(e, redirectBase, "FTS5-Neuaufbau läuft bereits.") + } + if status == "restarting" { + setFlashSuccess(e, "FTS5-Neuaufbau wird neu gestartet.") + return e.Redirect(http.StatusSeeOther, redirectBase) + } + + setFlashSuccess(e, "FTS5-Neuaufbau gestartet.") + return e.Redirect(http.StatusSeeOther, redirectBase) + } +} + +func settingsData(app core.App) (map[string]any, error) { + settings, err := dbmodels.Settings_All(app) + if err != nil { + return nil, err + } + + list := make([]settingView, 0, len(settings)) + for _, setting := range settings { + if setting == nil { + continue + } + list = append(list, settingView{ + Key: setting.Key(), + Value: formatSettingValue(setting.Value()), + Updated: setting.GetDateTime(dbmodels.UPDATED_FIELD), + }) + } + + sort.Slice(list, func(i, j int) bool { + return list[i].Key < list[j].Key + }) + + var lastRebuild string + var lastRebuildDT types.DateTime + if setting, err := dbmodels.Settings_Key(app, "fts5_last_rebuild"); err == nil && setting != nil { + if dt, ok := parseSettingDateTime(setting.Value()); ok { + lastRebuildDT = dt + lastRebuild = formatSettingValue(dt) + } else { + lastRebuild = formatSettingValue(setting.Value()) + } + } + + return map[string]any{ + "settings": list, + "fts5_last_rebuild": lastRebuild, + "fts5_last_rebuild_dt": lastRebuildDT, + }, nil +} + +func parseSettingValue(valueRaw string) any { + if valueRaw == "" { + return "" + } + var parsed any + if err := json.Unmarshal([]byte(valueRaw), &parsed); err == nil { + return parsed + } + return valueRaw +} + +func formatSettingValue(value any) string { + if value == nil { + return "" + } + if formatted, ok := formatSettingDateTime(value); ok { + return formatted + } + if str, ok := parseSettingString(value); ok && str != "" { + return str + } + data, err := json.Marshal(value) + if err != nil { + return fmt.Sprintf("%v", value) + } + return string(data) +} + +func isRecordNotFound(err error) bool { + if err == nil { + return false + } + msg := err.Error() + return strings.Contains(msg, "no rows in result set") || strings.Contains(msg, "not found") +} + +func redirectSettingsError(e *core.RequestEvent, baseURL, message string) error { + redirect := fmt.Sprintf("%s?error=%s", baseURL, url.QueryEscape(message)) + return e.Redirect(http.StatusSeeOther, redirect) +} diff --git a/controllers/settings_helpers.go b/controllers/settings_helpers.go new file mode 100644 index 0000000..fc0f04d --- /dev/null +++ b/controllers/settings_helpers.go @@ -0,0 +1,78 @@ +package controllers + +import ( + "encoding/json" + "strings" + + "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/functions" + "github.com/pocketbase/pocketbase/tools/types" +) + +func parseSettingString(value any) (string, bool) { + switch v := value.(type) { + case string: + return strings.TrimSpace(v), true + case []byte: + if len(v) == 0 { + return "", false + } + var parsed string + if err := json.Unmarshal(v, &parsed); err == nil { + return strings.TrimSpace(parsed), true + } + return strings.TrimSpace(string(v)), true + case json.RawMessage: + if len(v) == 0 { + return "", false + } + var parsed string + if err := json.Unmarshal(v, &parsed); err == nil { + return strings.TrimSpace(parsed), true + } + return strings.TrimSpace(string(v)), true + default: + return "", false + } +} + +func formatSettingDateTime(value any) (string, bool) { + if value == nil { + return "", false + } + if dt, err := types.ParseDateTime(value); err == nil && !dt.IsZero() { + return functions.GermanShortDateTime(dt), true + } + if raw, ok := parseSettingString(value); ok { + clean := strings.Trim(raw, "\"") + if dt, err := types.ParseDateTime(clean); err == nil && !dt.IsZero() { + return functions.GermanShortDateTime(dt), true + } + if clean != "" { + return clean, true + } + } + return "", false +} + +func parseSettingDateTime(value any) (types.DateTime, bool) { + if value == nil { + return types.DateTime{}, false + } + if dt, err := types.ParseDateTime(value); err == nil && !dt.IsZero() { + return dt, true + } + if raw, ok := parseSettingString(value); ok { + clean := strings.Trim(raw, "\"") + if dt, err := types.ParseDateTime(clean); err == nil && !dt.IsZero() { + return dt, true + } + } + return types.DateTime{}, false +} + +func formatSettingGermanDateTime(value any) (string, bool) { + if dt, ok := parseSettingDateTime(value); ok { + return functions.GermanDate(dt) + " " + functions.GermanTime(dt), true + } + return "", false +} diff --git a/dbmodels/db_data.go b/dbmodels/db_data.go index f588cb9..102219d 100644 --- a/dbmodels/db_data.go +++ b/dbmodels/db_data.go @@ -440,6 +440,7 @@ const ( SESSIONS_TABLE = "sessions" ACCESS_TOKENS_TABLE = "access_tokens" DATA_TABLE = "data" + SETTINGS_TABLE = "settings" IMAGES_TABLE = "images" FILES_TABLE = "files" HTML_TABLE = "html" diff --git a/dbmodels/fts5.go b/dbmodels/fts5.go index f0e5a88..21f6de2 100644 --- a/dbmodels/fts5.go +++ b/dbmodels/fts5.go @@ -269,10 +269,42 @@ func CreateFTS5TableQuery(tablename string, fields ...string) string { return str } +func CreateFTS5Tables(app core.App) error { + err1 := createFTS5Table(app, AGENTS_TABLE, AGENTS_FTS5_FIELDS) + err2 := createFTS5Table(app, PLACES_TABLE, PLACES_FTS5_FIELDS) + err3 := createFTS5Table(app, SERIES_TABLE, SERIES_FTS5_FIELDS) + err4 := createFTS5Table(app, ITEMS_TABLE, ITEMS_FTS5_FIELDS) + err5 := createFTS5Table(app, ENTRIES_TABLE, ENTRIES_FTS5_FIELDS) + err6 := createFTS5Table(app, CONTENTS_TABLE, CONTENTS_FTS5_FIELDS) + return errors.Join(err1, err2, err3, err4, err5, err6) +} + +func DropFTS5Tables(app core.App) error { + err1 := dropFTS5Table(app, FTS5TableName(AGENTS_TABLE)) + err2 := dropFTS5Table(app, FTS5TableName(PLACES_TABLE)) + err3 := dropFTS5Table(app, FTS5TableName(SERIES_TABLE)) + err4 := dropFTS5Table(app, FTS5TableName(ITEMS_TABLE)) + err5 := dropFTS5Table(app, FTS5TableName(ENTRIES_TABLE)) + err6 := dropFTS5Table(app, FTS5TableName(CONTENTS_TABLE)) + return errors.Join(err1, err2, err3, err4, err5, err6) +} + func FTS5TableName(table string) string { return FTS5_PREFIX + table } +func createFTS5Table(app core.App, table string, fields []string) error { + query := CreateFTS5TableQuery(table, fields...) + _, err := app.DB().NewQuery(query).Execute() + return err +} + +func dropFTS5Table(app core.App, table string) error { + query := "DROP TABLE IF EXISTS " + table + _, err := app.DB().NewQuery(query).Execute() + return err +} + func InsertFTS5Agent(app core.App, agent *Agent) error { query := FTS5InsertQuery(app, AGENTS_TABLE, AGENTS_FTS5_FIELDS) return BulkInsertFTS5Agent(query, agent) diff --git a/dbmodels/queries.go b/dbmodels/queries.go index 5fc7ada..bfd0f98 100644 --- a/dbmodels/queries.go +++ b/dbmodels/queries.go @@ -159,6 +159,17 @@ func Data_All(app core.App) ([]*Data, error) { return data, err } +func Settings_Key(app core.App, key string) (*Setting, error) { + ret, err := TableByField[Setting](app, SETTINGS_TABLE, KEY_FIELD, key) + return &ret, err +} + +func Settings_All(app core.App) ([]*Setting, error) { + settings := make([]*Setting, 0) + err := app.RecordQuery(SETTINGS_TABLE).All(&settings) + return settings, err +} + func Pages_All(app core.App) ([]*Page, error) { pages := make([]*Page, 0) err := app.RecordQuery(PAGES_TABLE).All(&pages) diff --git a/dbmodels/settings.go b/dbmodels/settings.go new file mode 100644 index 0000000..0b8a537 --- /dev/null +++ b/dbmodels/settings.go @@ -0,0 +1,23 @@ +package dbmodels + +import "github.com/pocketbase/pocketbase/core" + +type Setting struct { + core.BaseRecordProxy +} + +func (s *Setting) Key() string { + return s.GetString(KEY_FIELD) +} + +func (s *Setting) SetKey(key string) { + s.Set(KEY_FIELD, key) +} + +func (s *Setting) Value() any { + return s.GetRaw(VALUE_FIELD) +} + +func (s *Setting) SetValue(value any) { + s.Set(VALUE_FIELD, value) +} diff --git a/helpers/imports/fts.go b/helpers/imports/fts.go index d2801b4..8ed0691 100644 --- a/helpers/imports/fts.go +++ b/helpers/imports/fts.go @@ -1,50 +1,156 @@ package imports import ( + "context" + "errors" + "fmt" + "strconv" + "strings" + "sync" + "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/pocketbase/pocketbase/core" + "github.com/pocketbase/pocketbase/tools/types" ) +var ( + fts5Mu sync.Mutex + fts5Running bool + fts5Cancel context.CancelFunc + fts5RestartRequested bool +) + +func StartFTS5Rebuild(app core.App, allowRestart bool) (string, error) { + fts5Mu.Lock() + if fts5Running { + if allowRestart { + fts5RestartRequested = true + if fts5Cancel != nil { + fts5Cancel() + } + done := getSettingInt(app, "fts5_rebuild_done") + total := getSettingInt(app, "fts5_rebuild_total") + setFTS5RebuildState(app, "running", "Neuaufbau wird neu gestartet.", done, total, "") + fts5Mu.Unlock() + return "restarting", nil + } + fts5Mu.Unlock() + return "running", nil + } + ctx, cancel := context.WithCancel(context.Background()) + fts5Running = true + fts5Cancel = cancel + fts5RestartRequested = false + fts5Mu.Unlock() + + go func() { + err := rebuildFTSFromScratchWithContext(app, ctx) + fts5Mu.Lock() + restart := fts5RestartRequested + fts5Running = false + fts5Cancel = nil + fts5RestartRequested = false + fts5Mu.Unlock() + + if restart { + _, _ = StartFTS5Rebuild(app, false) + return + } + + if errors.Is(err, context.Canceled) { + return + } + }() + + return "started", nil +} + func RebuildFTS(app core.App) error { - if err := dbmodels.DeleteFTS5Data(app); err != nil { + return rebuildFTSWithContext(app, true, context.Background()) +} + +func RebuildFTSFromScratch(app core.App) error { + return rebuildFTSFromScratchWithContext(app, context.Background()) +} + +func rebuildFTSFromScratchWithContext(app core.App, ctx context.Context) error { + setFTS5RebuildState(app, "running", "Neuaufbau wird vorbereitet...", 0, 0, "") + if err := checkFTS5Canceled(app, ctx, 0, 0); err != nil { return err } + if err := dbmodels.DropFTS5Tables(app); err != nil { + setFTS5RebuildState(app, "error", "Neuaufbau fehlgeschlagen.", 0, 0, err.Error()) + return err + } + if err := checkFTS5Canceled(app, ctx, 0, 0); err != nil { + return err + } + if err := dbmodels.CreateFTS5Tables(app); err != nil { + setFTS5RebuildState(app, "error", "Neuaufbau fehlgeschlagen.", 0, 0, err.Error()) + return err + } + + if err := rebuildFTSWithContext(app, false, ctx); err != nil { + return err + } + updateFTS5RebuildTimestamp(app) + return nil +} + +func rebuildFTSWithContext(app core.App, clearExisting bool, ctx context.Context) error { + fail := func(err error, done, total int) error { + setFTS5RebuildState(app, "error", "Neuaufbau fehlgeschlagen.", done, total, err.Error()) + return err + } + + if clearExisting { + if err := dbmodels.DeleteFTS5Data(app); err != nil { + return fail(err, 0, 0) + } + } places := []*dbmodels.Place{} if err := app.RecordQuery(dbmodels.PLACES_TABLE).All(&places); err != nil { - return err + return fail(err, 0, 0) } agents := []*dbmodels.Agent{} if err := app.RecordQuery(dbmodels.AGENTS_TABLE).All(&agents); err != nil { - return err + return fail(err, 0, 0) } series := []*dbmodels.Series{} if err := app.RecordQuery(dbmodels.SERIES_TABLE).All(&series); err != nil { - return err + return fail(err, 0, 0) } items := []*dbmodels.Item{} if err := app.RecordQuery(dbmodels.ITEMS_TABLE).All(&items); err != nil { - return err + return fail(err, 0, 0) } entries := []*dbmodels.Entry{} if err := app.RecordQuery(dbmodels.ENTRIES_TABLE).All(&entries); err != nil { - return err + return fail(err, 0, 0) } contents := []*dbmodels.Content{} if err := app.RecordQuery(dbmodels.CONTENTS_TABLE).All(&contents); err != nil { - return err + return fail(err, 0, 0) } entriesSeries := []*dbmodels.REntriesSeries{} if err := app.RecordQuery(dbmodels.RelationTableName(dbmodels.ENTRIES_TABLE, dbmodels.SERIES_TABLE)).All(&entriesSeries); err != nil { - return err + return fail(err, 0, 0) } entriesAgents := []*dbmodels.REntriesAgents{} if err := app.RecordQuery(dbmodels.RelationTableName(dbmodels.ENTRIES_TABLE, dbmodels.AGENTS_TABLE)).All(&entriesAgents); err != nil { - return err + return fail(err, 0, 0) } contentsAgents := []*dbmodels.RContentsAgents{} if err := app.RecordQuery(dbmodels.RelationTableName(dbmodels.CONTENTS_TABLE, dbmodels.AGENTS_TABLE)).All(&contentsAgents); err != nil { + return fail(err, 0, 0) + } + + total := len(places) + len(agents) + len(series) + len(items) + len(entries) + len(contents) + done := 0 + setFTS5RebuildState(app, "running", "FTS5-Neuaufbau läuft.", done, total, "") + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { return err } @@ -92,27 +198,50 @@ func RebuildFTS(app core.App) error { qc := dbmodels.FTS5InsertQuery(app, dbmodels.CONTENTS_TABLE, dbmodels.CONTENTS_FTS5_FIELDS) for _, place := range places { - if err := dbmodels.BulkInsertFTS5Place(qp, place); err != nil { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { return err } + if err := dbmodels.BulkInsertFTS5Place(qp, place); err != nil { + return fail(err, done, total) + } + done++ + maybeUpdateFTS5Progress(app, ctx, done, total) } for _, agent := range agents { - if err := dbmodels.BulkInsertFTS5Agent(qa, agent); err != nil { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { return err } + if err := dbmodels.BulkInsertFTS5Agent(qa, agent); err != nil { + return fail(err, done, total) + } + done++ + maybeUpdateFTS5Progress(app, ctx, done, total) } for _, s := range series { - if err := dbmodels.BulkInsertFTS5Series(qs, s); err != nil { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { return err } + if err := dbmodels.BulkInsertFTS5Series(qs, s); err != nil { + return fail(err, done, total) + } + done++ + maybeUpdateFTS5Progress(app, ctx, done, total) } for _, item := range items { - if err := dbmodels.BulkInsertFTS5Item(qi, item); err != nil { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { return err } + if err := dbmodels.BulkInsertFTS5Item(qi, item); err != nil { + return fail(err, done, total) + } + done++ + maybeUpdateFTS5Progress(app, ctx, done, total) } for _, entry := range entries { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { + return err + } entryPlaces := []*dbmodels.Place{} for _, placeId := range entry.Places() { if place := placesById[placeId]; place != nil { @@ -122,17 +251,145 @@ func RebuildFTS(app core.App) error { entryAgents := entriesAgentsMap[entry.Id] entrySeries := entriesSeriesMap[entry.Id] if err := dbmodels.BulkInsertFTS5Entry(qe, entry, entryPlaces, entryAgents, entrySeries); err != nil { - return err + return fail(err, done, total) } + done++ + maybeUpdateFTS5Progress(app, ctx, done, total) } for _, content := range contents { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { + return err + } entry := entriesById[content.Entry()] contentAgents := contentsAgentsMap[content.Id] if err := dbmodels.BulkInsertFTS5Content(qc, content, entry, contentAgents); err != nil { + return fail(err, done, total) + } + done++ + maybeUpdateFTS5Progress(app, ctx, done, total) + } + + setFTS5RebuildState(app, "complete", "FTS5-Neuaufbau abgeschlossen.", done, total, "") + return nil +} + +func maybeUpdateFTS5Progress(app core.App, ctx context.Context, done, total int) { + if total <= 0 { + return + } + if done%100 == 0 || done == total { + if err := checkFTS5Canceled(app, ctx, done, total); err != nil { + return + } + setFTS5RebuildState(app, "running", "FTS5-Neuaufbau läuft.", done, total, "") + } +} + +func checkFTS5Canceled(app core.App, ctx context.Context, done, total int) error { + if ctx == nil { + return nil + } + select { + case <-ctx.Done(): + setFTS5RebuildState(app, "aborted", "Neuaufbau abgebrochen.", done, total, "") + return ctx.Err() + default: + return nil + } +} + +func updateFTS5RebuildTimestamp(app core.App) { + collection, err := app.FindCollectionByNameOrId(dbmodels.SETTINGS_TABLE) + if err != nil { + app.Logger().Error("Failed to load settings collection for FTS5 timestamp", "error", err) + return + } + + var record *core.Record + existing, err := dbmodels.Settings_Key(app, "fts5_last_rebuild") + if err == nil && existing != nil { + record = existing.ProxyRecord() + } else if err != nil { + if !isRecordNotFound(err) { + app.Logger().Error("Failed to load FTS5 timestamp setting", "error", err) + return + } + } + if record == nil { + record = core.NewRecord(collection) + } + + record.Set(dbmodels.KEY_FIELD, "fts5_last_rebuild") + record.Set(dbmodels.VALUE_FIELD, types.NowDateTime()) + if err := app.Save(record); err != nil { + app.Logger().Error("Failed to save FTS5 timestamp", "error", err) + } +} + +func isRecordNotFound(err error) bool { + if err == nil { + return false + } + msg := err.Error() + return strings.Contains(msg, "no rows in result set") || strings.Contains(msg, "not found") +} + +func setFTS5RebuildState(app core.App, status, message string, done, total int, errMsg string) { + _ = upsertSetting(app, "fts5_rebuild_status", status) + _ = upsertSetting(app, "fts5_rebuild_message", message) + _ = upsertSetting(app, "fts5_rebuild_done", done) + _ = upsertSetting(app, "fts5_rebuild_total", total) + if errMsg != "" { + _ = upsertSetting(app, "fts5_rebuild_error", errMsg) + } else { + _ = upsertSetting(app, "fts5_rebuild_error", "") + } +} + +func upsertSetting(app core.App, key string, value any) error { + collection, err := app.FindCollectionByNameOrId(dbmodels.SETTINGS_TABLE) + if err != nil { + return err + } + + var record *core.Record + existing, err := dbmodels.Settings_Key(app, key) + if err == nil && existing != nil { + record = existing.ProxyRecord() + } else if err != nil { + if !isRecordNotFound(err) { return err } } - - return nil + if record == nil { + record = core.NewRecord(collection) + } + record.Set(dbmodels.KEY_FIELD, key) + record.Set(dbmodels.VALUE_FIELD, value) + return app.Save(record) +} + +func getSettingInt(app core.App, key string) int { + setting, err := dbmodels.Settings_Key(app, key) + if err != nil || setting == nil { + return 0 + } + switch v := setting.Value().(type) { + case float64: + return int(v) + case int: + return v + case int64: + return int(v) + case string: + if parsed, err := strconv.Atoi(v); err == nil { + return parsed + } + default: + if parsed, err := strconv.Atoi(fmt.Sprintf("%v", v)); err == nil { + return parsed + } + } + return 0 } diff --git a/migrations/1769000002_settings.go b/migrations/1769000002_settings.go new file mode 100644 index 0000000..ee7416b --- /dev/null +++ b/migrations/1769000002_settings.go @@ -0,0 +1,36 @@ +package migrations + +import ( + "strings" + + "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + collection := core.NewBaseCollection(dbmodels.SETTINGS_TABLE) + dbmodels.SetBasicPublicRules(collection) + fields := core.NewFieldsList( + &core.TextField{Name: dbmodels.KEY_FIELD, Required: true, Presentable: true}, + &core.JSONField{Name: dbmodels.VALUE_FIELD, Required: false}, + ) + dbmodels.SetCreatedUpdatedFields(&fields) + collection.Fields = fields + dbmodels.AddIndex(collection, dbmodels.KEY_FIELD, true) + + return app.Save(collection) + }, func(app core.App) error { + collection, err := app.FindCollectionByNameOrId(dbmodels.SETTINGS_TABLE) + if err != nil { + if strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "no rows in result set") { + return nil + } + app.Logger().Error("Failed to find collection for deletion", "collection", dbmodels.SETTINGS_TABLE, "error", err) + return err + } + + return app.Delete(collection) + }) +} diff --git a/views/assets/scripts.js b/views/assets/scripts.js index 1def8e9..f899dff 100644 --- a/views/assets/scripts.js +++ b/views/assets/scripts.js @@ -65,7 +65,7 @@ const ui = "\uFEFF", St = " ", Yr = function(s) { s = s.parentNode; } } -}, Hn = (s) => document.activeElement !== s && Ot(s, document.activeElement), Ot = function(s, t) { +}, qn = (s) => document.activeElement !== s && Ot(s, document.activeElement), Ot = function(s, t) { if (s && t) for (; t; ) { if (t === s) return !0; t = t.parentNode; @@ -127,7 +127,7 @@ const Ee = function() { }, ee = (s) => ro(s) && (s == null ? void 0 : s.data) === "block", ro = (s) => (s == null ? void 0 : s.nodeType) === Node.COMMENT_NODE, ie = function(s) { let { name: t } = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; if (s) return xe(s) ? s.data === ui ? !t || s.parentNode.dataset.trixCursorTarget === t : void 0 : ie(s.firstChild); -}, Nt = (s) => Qr(s, Mt), Zr = (s) => xe(s) && (s == null ? void 0 : s.data) === "", xe = (s) => (s == null ? void 0 : s.nodeType) === Node.TEXT_NODE, qn = { level2Enabled: !0, getLevel() { +}, Nt = (s) => Qr(s, Mt), Zr = (s) => xe(s) && (s == null ? void 0 : s.data) === "", xe = (s) => (s == null ? void 0 : s.nodeType) === Node.TEXT_NODE, Hn = { level2Enabled: !0, getLevel() { return this.level2Enabled && Oe.supportsInputEvents ? 2 : 0; }, pickFiles(s) { const t = x("input", { type: "file", multiple: !0, hidden: !0, id: this.fileInputId }); @@ -184,8 +184,8 @@ var ii = { removeBlankTableCells: !1, tableCellSeparator: " | ", tableRowSeparat `) }; const An = { interval: 5e3 }; -var Be = Object.freeze({ __proto__: null, attachments: Pn, blockAttributes: X, browser: Oe, css: { attachment: "attachment", attachmentCaption: "attachment__caption", attachmentCaptionEditor: "attachment__caption-editor", attachmentMetadata: "attachment__metadata", attachmentMetadataContainer: "attachment__metadata-container", attachmentName: "attachment__name", attachmentProgress: "attachment__progress", attachmentSize: "attachment__size", attachmentToolbar: "attachment__toolbar", attachmentGallery: "attachment-gallery" }, dompurify: Gr, fileSize: Jr, input: qn, keyNames: { 8: "backspace", 9: "tab", 13: "return", 27: "escape", 37: "left", 39: "right", 46: "delete", 68: "d", 72: "h", 79: "o" }, lang: v, parser: ii, textAttributes: Ft, toolbar: ta, undo: An }); -class q { +var Be = Object.freeze({ __proto__: null, attachments: Pn, blockAttributes: X, browser: Oe, css: { attachment: "attachment", attachmentCaption: "attachment__caption", attachmentCaptionEditor: "attachment__caption-editor", attachmentMetadata: "attachment__metadata", attachmentMetadataContainer: "attachment__metadata-container", attachmentName: "attachment__name", attachmentProgress: "attachment__progress", attachmentSize: "attachment__size", attachmentToolbar: "attachment__toolbar", attachmentGallery: "attachment-gallery" }, dompurify: Gr, fileSize: Jr, input: Hn, keyNames: { 8: "backspace", 9: "tab", 13: "return", 27: "escape", 37: "left", 39: "right", 46: "delete", 68: "d", 72: "h", 79: "o" }, lang: v, parser: ii, textAttributes: Ft, toolbar: ta, undo: An }); +class H { static proxyMethod(t) { const { name: e, toMethod: i, toProperty: n, optional: r } = ao(t); this.prototype[e] = function() { @@ -201,8 +201,8 @@ const ao = function(s) { const e = { name: t[4] }; return t[2] != null ? e.toMethod = t[1] : e.toProperty = t[1], t[3] != null && (e.optional = !0), e; }, { apply: ys } = Function.prototype, oo = new RegExp("^(.+?)(\\(\\))?(\\?)?\\.(.+?)$"); -var Fi, Hi, qi; -class ke extends q { +var Fi, qi, Hi; +class ke extends H { static box() { let t = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : ""; return t instanceof this ? t : this.fromUCS2String(t == null ? void 0 : t.toString()); @@ -241,7 +241,7 @@ class ke extends q { return this.ucs2String; } } -const lo = ((Fi = Array.from) === null || Fi === void 0 ? void 0 : Fi.call(Array, "👼").length) === 1, co = ((Hi = " ".codePointAt) === null || Hi === void 0 ? void 0 : Hi.call(" ", 0)) != null, ho = ((qi = String.fromCodePoint) === null || qi === void 0 ? void 0 : qi.call(String, 32, 128124)) === " 👼"; +const lo = ((Fi = Array.from) === null || Fi === void 0 ? void 0 : Fi.call(Array, "👼").length) === 1, co = ((qi = " ".codePointAt) === null || qi === void 0 ? void 0 : qi.call(" ", 0)) != null, ho = ((Hi = String.fromCodePoint) === null || Hi === void 0 ? void 0 : Hi.call(String, 32, 128124)) === " 👼"; let En, xn; En = lo && co ? (s) => Array.from(s).map((t) => t.codePointAt(0)) : function(s) { const t = []; @@ -266,7 +266,7 @@ En = lo && co ? (s) => Array.from(s).map((t) => t.codePointAt(0)) : function(s) })().join(""); }; let uo = 0; -class $t extends q { +class $t extends H { static fromJSONString(t) { return this.fromJSON(JSON.parse(t)); } @@ -299,7 +299,7 @@ class $t extends q { return this.id.toString(); } } -const Ht = function() { +const qt = function() { let s = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : [], t = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : []; if (s.length !== t.length) return !1; for (let e = 0; e < s.length; e++) @@ -384,7 +384,7 @@ const Sn = () => ($i || ($i = fo().concat(po())), $i), F = (s) => X[s], po = () }, Cn = function(s, t) { return typeof s == "number" ? s === t : re(s, t); }; -class ra extends q { +class ra extends H { constructor() { super(...arguments), this.update = this.update.bind(this), this.selectionManagers = []; } @@ -410,7 +410,7 @@ class ra extends q { this.update(); } } -const qt = new ra(), aa = function() { +const Ht = new ra(), aa = function() { const s = window.getSelection(); if (s.rangeCount > 0) return s; }, Se = function() { @@ -419,7 +419,7 @@ const qt = new ra(), aa = function() { if (t && !vo(t)) return t; }, oa = function(s) { const t = window.getSelection(); - return t.removeAllRanges(), t.addRange(s), qt.update(); + return t.removeAllRanges(), t.addRange(s), Ht.update(); }, vo = (s) => Ss(s.startContainer) || Ss(s.endContainer), Ss = (s) => !Object.getPrototypeOf(s), ye = (s) => s.replace(new RegExp("".concat(ui), "g"), "").replace(new RegExp("".concat(St), "g"), " "), Vn = new RegExp("[^\\S".concat(St, "]")), jn = (s) => s.replace(new RegExp("".concat(Vn.source), "g"), " ").replace(/\ {2,}/g, " "), Ls = function(s, t) { if (s.isEqualTo(t)) return ["", ""]; const e = ji(s, t), { length: i } = e.utf16String; @@ -479,7 +479,7 @@ class et extends $t { return t = me(t), this.getKeys().filter((e) => this.values[e] === t.values[e]); } isEqualTo(t) { - return Ht(this.toArray(), me(t).toArray()); + return qt(this.toArray(), me(t).toArray()); } isEmpty() { return this.getKeys().length === 0; @@ -557,7 +557,7 @@ class Wn { }), t.join("/"); } } -class xo extends q { +class xo extends H { constructor() { let t = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : []; super(...arguments), this.objects = {}, Array.from(t).forEach((e) => { @@ -590,7 +590,7 @@ class So { } } const Cs = (s) => s.dataset.trixStoreKey; -class oi extends q { +class oi extends H { isPerforming() { return this.performing === !0; } @@ -617,7 +617,7 @@ class oi extends q { } } oi.proxyMethod("getPromise().then"), oi.proxyMethod("getPromise().catch"); -class Ut extends q { +class Ut extends H { constructor(t) { let e = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; super(...arguments), this.object = t, this.options = e, this.childViews = [], this.rootView = this; @@ -776,8 +776,8 @@ function be(s, t) { return null; }; } -const Is = Q(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]), Ki = Q(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "slot", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]), Gi = Q(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]), Bo = Q(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]), Ji = Q(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]), Mo = Q(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]), Rs = Q(["#text"]), Ds = Q(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns", "slot"]), Yi = Q(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]), Os = Q(["accent", "accentunder", "align", "bevelled", "close", "columnsalign", "columnlines", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lspace", "lquote", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]), Ke = Q(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]), No = st(/\{\{[\w\W]*|[\w\W]*\}\}/gm), Po = st(/<%[\w\W]*|[\w\W]*%>/gm), Fo = st(/\$\{[\w\W]*/gm), Ho = st(/^data-[\-\w.\u00B7-\uFFFF]+$/), qo = st(/^aria-[\-\w]+$/), ca = st(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i), $o = st(/^(?:\w+script|data):/i), Uo = st(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g), ha = st(/^html$/i), Vo = st(/^[a-z][.\w]*(-[.\w]+)+$/i); -var Bs = Object.freeze({ __proto__: null, ARIA_ATTR: qo, ATTR_WHITESPACE: Uo, CUSTOM_ELEMENT: Vo, DATA_ATTR: Ho, DOCTYPE_NAME: ha, ERB_EXPR: Po, IS_ALLOWED_URI: ca, IS_SCRIPT_OR_DATA: $o, MUSTACHE_EXPR: No, TMPLIT_EXPR: Fo }); +const Is = Q(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]), Ki = Q(["svg", "a", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "enterkeyhint", "exportparts", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "inputmode", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "part", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "slot", "stop", "style", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "view", "vkern"]), Gi = Q(["feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence"]), Bo = Q(["animate", "color-profile", "cursor", "discard", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri", "foreignobject", "hatch", "hatchpath", "mesh", "meshgradient", "meshpatch", "meshrow", "missing-glyph", "script", "set", "solidcolor", "unknown", "use"]), Ji = Q(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]), Mo = Q(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]), Rs = Q(["#text"]), Ds = Q(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns", "slot"]), Yi = Q(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]), Os = Q(["accent", "accentunder", "align", "bevelled", "close", "columnsalign", "columnlines", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lspace", "lquote", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]), Ke = Q(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]), No = st(/\{\{[\w\W]*|[\w\W]*\}\}/gm), Po = st(/<%[\w\W]*|[\w\W]*%>/gm), Fo = st(/\$\{[\w\W]*/gm), qo = st(/^data-[\-\w.\u00B7-\uFFFF]+$/), Ho = st(/^aria-[\-\w]+$/), ca = st(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i), $o = st(/^(?:\w+script|data):/i), Uo = st(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g), ha = st(/^html$/i), Vo = st(/^[a-z][.\w]*(-[.\w]+)+$/i); +var Bs = Object.freeze({ __proto__: null, ARIA_ATTR: Ho, ATTR_WHITESPACE: Uo, CUSTOM_ELEMENT: Vo, DATA_ATTR: qo, DOCTYPE_NAME: ha, ERB_EXPR: Po, IS_ALLOWED_URI: ca, IS_SCRIPT_OR_DATA: $o, MUSTACHE_EXPR: No, TMPLIT_EXPR: Fo }); const jo = 1, Wo = 3, zo = 7, Ko = 8, Go = 9, Jo = function() { return typeof window > "u" ? null : window; }; @@ -797,7 +797,7 @@ var Ie = function s() { e.isSupported = typeof la == "function" && typeof _ == "function" && z && z.createHTMLDocument !== void 0; const { MUSTACHE_EXPR: Tt, ERB_EXPR: jt, TMPLIT_EXPR: tt, DATA_ATTR: _i, ARIA_ATTR: vi, IS_SCRIPT_OR_DATA: yi, ATTR_WHITESPACE: Me, CUSTOM_ELEMENT: Ai } = Bs; let { IS_ALLOWED_URI: oe } = Bs, M = null; - const H = L({}, [...Is, ...Ki, ...Gi, ...Ji, ...Rs]); + const q = L({}, [...Is, ...Ki, ...Gi, ...Ji, ...Rs]); let D = null; const Gn = L({}, [...Ds, ...Yi, ...Os, ...Ke]); let N = Object.seal(da(null, { tagNameCheck: { writable: !0, configurable: !1, enumerable: !0, value: null }, attributeNameCheck: { writable: !0, configurable: !1, enumerable: !0, value: null }, allowCustomizedBuiltInElements: { writable: !0, configurable: !1, enumerable: !0, value: !1 } })), le = null, Ei = null, Jn = !0, xi = !0, Yn = !1, Xn = !0, Wt = !1, Ne = !0, kt = !1, Si = !1, Li = !1, zt = !1, Pe = !1, Fe = !1, Qn = !0, Zn = !1, Ci = !0, de = !1, Kt = {}, Gt = null; @@ -805,9 +805,9 @@ var Ie = function s() { let es = null; const is = L({}, ["audio", "video", "img", "source", "image", "track"]); let wi = null; - const ns = L({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]), He = "http://www.w3.org/1998/Math/MathML", qe = "http://www.w3.org/2000/svg", gt = "http://www.w3.org/1999/xhtml"; + const ns = L({}, ["alt", "class", "for", "id", "label", "name", "pattern", "placeholder", "role", "summary", "title", "value", "style", "xmlns"]), qe = "http://www.w3.org/1998/Math/MathML", He = "http://www.w3.org/2000/svg", gt = "http://www.w3.org/1999/xhtml"; let Jt = gt, Ti = !1, ki = null; - const Ja = L({}, [He, qe, gt], Wi); + const Ja = L({}, [qe, He, gt], Wi); let $e = L({}, ["mi", "mo", "mn", "ms", "mtext"]), Ue = L({}, ["annotation-xml"]); const Ya = L({}, ["title", "style", "font", "a", "script"]); let ce = null; @@ -818,7 +818,7 @@ var Ie = function s() { }, Ii = function() { let d = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}; if (!Yt || Yt !== d) { - if (d && typeof d == "object" || (d = {}), d = ft(d), ce = Xa.indexOf(d.PARSER_MEDIA_TYPE) === -1 ? "text/html" : d.PARSER_MEDIA_TYPE, W = ce === "application/xhtml+xml" ? Wi : si, M = lt(d, "ALLOWED_TAGS") ? L({}, d.ALLOWED_TAGS, W) : H, D = lt(d, "ALLOWED_ATTR") ? L({}, d.ALLOWED_ATTR, W) : Gn, ki = lt(d, "ALLOWED_NAMESPACES") ? L({}, d.ALLOWED_NAMESPACES, Wi) : Ja, wi = lt(d, "ADD_URI_SAFE_ATTR") ? L(ft(ns), d.ADD_URI_SAFE_ATTR, W) : ns, es = lt(d, "ADD_DATA_URI_TAGS") ? L(ft(is), d.ADD_DATA_URI_TAGS, W) : is, Gt = lt(d, "FORBID_CONTENTS") ? L({}, d.FORBID_CONTENTS, W) : ts, le = lt(d, "FORBID_TAGS") ? L({}, d.FORBID_TAGS, W) : ft({}), Ei = lt(d, "FORBID_ATTR") ? L({}, d.FORBID_ATTR, W) : ft({}), Kt = !!lt(d, "USE_PROFILES") && d.USE_PROFILES, Jn = d.ALLOW_ARIA_ATTR !== !1, xi = d.ALLOW_DATA_ATTR !== !1, Yn = d.ALLOW_UNKNOWN_PROTOCOLS || !1, Xn = d.ALLOW_SELF_CLOSE_IN_ATTR !== !1, Wt = d.SAFE_FOR_TEMPLATES || !1, Ne = d.SAFE_FOR_XML !== !1, kt = d.WHOLE_DOCUMENT || !1, zt = d.RETURN_DOM || !1, Pe = d.RETURN_DOM_FRAGMENT || !1, Fe = d.RETURN_TRUSTED_TYPE || !1, Li = d.FORCE_BODY || !1, Qn = d.SANITIZE_DOM !== !1, Zn = d.SANITIZE_NAMED_PROPS || !1, Ci = d.KEEP_CONTENT !== !1, de = d.IN_PLACE || !1, oe = d.ALLOWED_URI_REGEXP || ca, Jt = d.NAMESPACE || gt, $e = d.MATHML_TEXT_INTEGRATION_POINTS || $e, Ue = d.HTML_INTEGRATION_POINTS || Ue, N = d.CUSTOM_ELEMENT_HANDLING || {}, d.CUSTOM_ELEMENT_HANDLING && ss(d.CUSTOM_ELEMENT_HANDLING.tagNameCheck) && (N.tagNameCheck = d.CUSTOM_ELEMENT_HANDLING.tagNameCheck), d.CUSTOM_ELEMENT_HANDLING && ss(d.CUSTOM_ELEMENT_HANDLING.attributeNameCheck) && (N.attributeNameCheck = d.CUSTOM_ELEMENT_HANDLING.attributeNameCheck), d.CUSTOM_ELEMENT_HANDLING && typeof d.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements == "boolean" && (N.allowCustomizedBuiltInElements = d.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements), Wt && (xi = !1), Pe && (zt = !0), Kt && (M = L({}, Rs), D = [], Kt.html === !0 && (L(M, Is), L(D, Ds)), Kt.svg === !0 && (L(M, Ki), L(D, Yi), L(D, Ke)), Kt.svgFilters === !0 && (L(M, Gi), L(D, Yi), L(D, Ke)), Kt.mathMl === !0 && (L(M, Ji), L(D, Os), L(D, Ke))), d.ADD_TAGS && (M === H && (M = ft(M)), L(M, d.ADD_TAGS, W)), d.ADD_ATTR && (D === Gn && (D = ft(D)), L(D, d.ADD_ATTR, W)), d.ADD_URI_SAFE_ATTR && L(wi, d.ADD_URI_SAFE_ATTR, W), d.FORBID_CONTENTS && (Gt === ts && (Gt = ft(Gt)), L(Gt, d.FORBID_CONTENTS, W)), Ci && (M["#text"] = !0), kt && L(M, ["html", "head", "body"]), M.table && (L(M, ["tbody"]), delete le.tbody), d.TRUSTED_TYPES_POLICY) { + if (d && typeof d == "object" || (d = {}), d = ft(d), ce = Xa.indexOf(d.PARSER_MEDIA_TYPE) === -1 ? "text/html" : d.PARSER_MEDIA_TYPE, W = ce === "application/xhtml+xml" ? Wi : si, M = lt(d, "ALLOWED_TAGS") ? L({}, d.ALLOWED_TAGS, W) : q, D = lt(d, "ALLOWED_ATTR") ? L({}, d.ALLOWED_ATTR, W) : Gn, ki = lt(d, "ALLOWED_NAMESPACES") ? L({}, d.ALLOWED_NAMESPACES, Wi) : Ja, wi = lt(d, "ADD_URI_SAFE_ATTR") ? L(ft(ns), d.ADD_URI_SAFE_ATTR, W) : ns, es = lt(d, "ADD_DATA_URI_TAGS") ? L(ft(is), d.ADD_DATA_URI_TAGS, W) : is, Gt = lt(d, "FORBID_CONTENTS") ? L({}, d.FORBID_CONTENTS, W) : ts, le = lt(d, "FORBID_TAGS") ? L({}, d.FORBID_TAGS, W) : ft({}), Ei = lt(d, "FORBID_ATTR") ? L({}, d.FORBID_ATTR, W) : ft({}), Kt = !!lt(d, "USE_PROFILES") && d.USE_PROFILES, Jn = d.ALLOW_ARIA_ATTR !== !1, xi = d.ALLOW_DATA_ATTR !== !1, Yn = d.ALLOW_UNKNOWN_PROTOCOLS || !1, Xn = d.ALLOW_SELF_CLOSE_IN_ATTR !== !1, Wt = d.SAFE_FOR_TEMPLATES || !1, Ne = d.SAFE_FOR_XML !== !1, kt = d.WHOLE_DOCUMENT || !1, zt = d.RETURN_DOM || !1, Pe = d.RETURN_DOM_FRAGMENT || !1, Fe = d.RETURN_TRUSTED_TYPE || !1, Li = d.FORCE_BODY || !1, Qn = d.SANITIZE_DOM !== !1, Zn = d.SANITIZE_NAMED_PROPS || !1, Ci = d.KEEP_CONTENT !== !1, de = d.IN_PLACE || !1, oe = d.ALLOWED_URI_REGEXP || ca, Jt = d.NAMESPACE || gt, $e = d.MATHML_TEXT_INTEGRATION_POINTS || $e, Ue = d.HTML_INTEGRATION_POINTS || Ue, N = d.CUSTOM_ELEMENT_HANDLING || {}, d.CUSTOM_ELEMENT_HANDLING && ss(d.CUSTOM_ELEMENT_HANDLING.tagNameCheck) && (N.tagNameCheck = d.CUSTOM_ELEMENT_HANDLING.tagNameCheck), d.CUSTOM_ELEMENT_HANDLING && ss(d.CUSTOM_ELEMENT_HANDLING.attributeNameCheck) && (N.attributeNameCheck = d.CUSTOM_ELEMENT_HANDLING.attributeNameCheck), d.CUSTOM_ELEMENT_HANDLING && typeof d.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements == "boolean" && (N.allowCustomizedBuiltInElements = d.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements), Wt && (xi = !1), Pe && (zt = !0), Kt && (M = L({}, Rs), D = [], Kt.html === !0 && (L(M, Is), L(D, Ds)), Kt.svg === !0 && (L(M, Ki), L(D, Yi), L(D, Ke)), Kt.svgFilters === !0 && (L(M, Gi), L(D, Yi), L(D, Ke)), Kt.mathMl === !0 && (L(M, Ji), L(D, Os), L(D, Ke))), d.ADD_TAGS && (M === q && (M = ft(M)), L(M, d.ADD_TAGS, W)), d.ADD_ATTR && (D === Gn && (D = ft(D)), L(D, d.ADD_ATTR, W)), d.ADD_URI_SAFE_ATTR && L(wi, d.ADD_URI_SAFE_ATTR, W), d.FORBID_CONTENTS && (Gt === ts && (Gt = ft(Gt)), L(Gt, d.FORBID_CONTENTS, W)), Ci && (M["#text"] = !0), kt && L(M, ["html", "head", "body"]), M.table && (L(M, ["tbody"]), delete le.tbody), d.TRUSTED_TYPES_POLICY) { if (typeof d.TRUSTED_TYPES_POLICY.createHTML != "function") throw fe('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.'); if (typeof d.TRUSTED_TYPES_POLICY.createScriptURL != "function") throw fe('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.'); S = d.TRUSTED_TYPES_POLICY, E = S.createHTML(""); @@ -913,7 +913,7 @@ var Ie = function s() { let T = _(C); T && T.tagName || (T = { namespaceURI: Jt, tagName: "template" }); const y = si(C.tagName), j = si(T.tagName); - return !!ki[C.namespaceURI] && (C.namespaceURI === qe ? T.namespaceURI === gt ? y === "svg" : T.namespaceURI === He ? y === "svg" && (j === "annotation-xml" || $e[j]) : !!rs[y] : C.namespaceURI === He ? T.namespaceURI === gt ? y === "math" : T.namespaceURI === qe ? y === "math" && Ue[j] : !!as[y] : C.namespaceURI === gt ? !(T.namespaceURI === qe && !Ue[j]) && !(T.namespaceURI === He && !$e[j]) && !as[y] && (Ya[y] || !rs[y]) : !(ce !== "application/xhtml+xml" || !ki[C.namespaceURI])); + return !!ki[C.namespaceURI] && (C.namespaceURI === He ? T.namespaceURI === gt ? y === "svg" : T.namespaceURI === qe ? y === "svg" && (j === "annotation-xml" || $e[j]) : !!rs[y] : C.namespaceURI === qe ? T.namespaceURI === gt ? y === "math" : T.namespaceURI === He ? y === "math" && Ue[j] : !!as[y] : C.namespaceURI === gt ? !(T.namespaceURI === He && !Ue[j]) && !(T.namespaceURI === qe && !$e[j]) && !as[y] && (Ya[y] || !rs[y]) : !(ce !== "application/xhtml+xml" || !ki[C.namespaceURI])); }(d) ? (ht(d), !0) : g !== "noscript" && g !== "noembed" && g !== "noframes" || !J(/<\/no(script|embed|frames)/i, d.innerHTML) ? (Wt && d.nodeType === Wo && (b = d.textContent, ze([Tt, jt, tt], (C) => { b = pe(b, C, " "); }), d.textContent !== b && (ge(e.removed, { element: d.cloneNode() }), d.textContent = b)), pt(V.afterSanitizeElements, d, null), !1) : (ht(d), !0); @@ -1044,7 +1044,7 @@ Ie.addHook("uponSanitizeAttribute", function(s, t) { /^data-trix-/.test(t.attrName) && (t.forceKeepAttr = !0); }); const Yo = "style href src width height language class".split(" "), Xo = "javascript:".split(" "), Qo = "script iframe form noscript".split(" "); -class mi extends q { +class mi extends H { static setHTML(t, e, i) { const n = new this(e, i).sanitize(), r = n.getHTML ? n.getHTML() : n.outerHTML; t.innerHTML = r; @@ -2033,7 +2033,7 @@ class ut extends $t { return this.text.isBlockBreak(); } isEqualTo(t) { - return !!super.isEqualTo(t) || this.text.isEqualTo(t == null ? void 0 : t.text) && Ht(this.attributes, t == null ? void 0 : t.attributes) && re(this.htmlAttributes, t == null ? void 0 : t.htmlAttributes); + return !!super.isEqualTo(t) || this.text.isEqualTo(t == null ? void 0 : t.text) && qt(this.attributes, t == null ? void 0 : t.attributes) && re(this.htmlAttributes, t == null ? void 0 : t.htmlAttributes); } copyWithText(t) { return new ut(t, this.attributes, this.htmlAttributes); @@ -2052,7 +2052,7 @@ class ut extends $t { return e ? this.copyWithText(e) : this.copyWithText(this.text.copyUsingObjectMap(t)); } addAttribute(t) { - const e = this.attributes.concat(Hs(t)); + const e = this.attributes.concat(qs(t)); return this.copyWithAttributes(e); } addHTMLAttribute(t, e) { @@ -2067,7 +2067,7 @@ class ut extends $t { return this.removeAttribute(this.getLastAttribute()); } getLastAttribute() { - return qs(this.attributes); + return Hs(this.attributes); } getAttributes() { return this.attributes.slice(0); @@ -2085,7 +2085,7 @@ class ut extends $t { return this.getAttributeLevel() > 0; } getLastNestableAttribute() { - return qs(this.getNestableAttributes()); + return Hs(this.getNestableAttributes()); } getNestableAttributes() { return this.attributes.filter((t) => F(t).nestable); @@ -2100,7 +2100,7 @@ class ut extends $t { increaseNestingLevel() { const t = this.getLastNestableAttribute(); if (t) { - const e = this.attributes.lastIndexOf(t), i = $n(this.attributes, e + 1, 0, ...Hs(t)); + const e = this.attributes.lastIndexOf(t), i = $n(this.attributes, e + 1, 0, ...qs(t)); return this.copyWithAttributes(i); } return this; @@ -2201,10 +2201,10 @@ const dl = function(s) { }, va = function(s) { const t = s.getLength(); return t === 0 ? !1 : s.getTextAtRange([t - 1, t]).isBlockBreak(); -}, ml = (s) => s.copyWithoutAttribute("blockBreak"), Hs = function(s) { +}, ml = (s) => s.copyWithoutAttribute("blockBreak"), qs = function(s) { const { listAttribute: t } = F(s); return t ? [t, s] : [s]; -}, qs = (s) => s.slice(-1)[0], $s = function(s, t) { +}, Hs = (s) => s.slice(-1)[0], $s = function(s, t) { const e = s.lastIndexOf(t); return e === -1 ? s : $n(s, e, 1); }; @@ -2261,12 +2261,12 @@ class nt extends $t { let i, n; e = k(e); const [r] = e, a = this.locationFromPosition(r), o = this.getBlockAtIndex(a.index).getAttributes(), l = t.getBaseBlockAttributes(), c = o.slice(-l.length); - if (Ht(l, c)) { + if (qt(l, c)) { const p = o.slice(0, -l.length); i = t.copyWithBaseBlockAttributes(p); } else i = t.copy({ consolidateBlocks: !0 }).copyWithBaseBlockAttributes(o); const u = i.getBlockCount(), m = i.getBlockAtIndex(0); - if (Ht(o, m.getAttributes())) { + if (qt(o, m.getAttributes())) { const p = m.getTextWithoutBlockBreak(); if (n = this.insertTextAtRange(p, e), u > 1) { i = new this.constructor(i.getBlocks().slice(1)); @@ -2620,7 +2620,7 @@ const Us = function(s) { return {}; } }; -class Re extends q { +class Re extends H { static parse(t, e) { const i = new this(t, e); return i.parse(), i; @@ -2664,7 +2664,7 @@ class Re extends q { if (e === this.containerElement || this.isBlockElement(e)) { var i; const n = this.getBlockAttributes(e), r = this.getBlockHTMLAttributes(e); - Ht(n, (i = this.currentBlock) === null || i === void 0 ? void 0 : i.attributes) || (this.currentBlock = this.appendBlockForAttributesWithElement(n, e, r), this.currentBlockElement = e); + qt(n, (i = this.currentBlock) === null || i === void 0 ? void 0 : i.attributes) || (this.currentBlock = this.appendBlockForAttributesWithElement(n, e, r), this.currentBlockElement = e); } } appendBlockForElement(t) { @@ -2673,7 +2673,7 @@ class Re extends q { if (!this.isInsignificantTextNode(t.firstChild) || !this.isBlockElement(t.firstElementChild)) { const n = this.getBlockAttributes(t), r = this.getBlockHTMLAttributes(t); if (t.firstChild) { - if (i && Ht(n, this.currentBlock.attributes)) return this.appendStringWithAttributes(` + if (i && qt(n, this.currentBlock.attributes)) return this.appendStringWithAttributes(` `); this.currentBlock = this.appendBlockForAttributesWithElement(n, t, r), this.currentBlockElement = t; } @@ -2888,7 +2888,7 @@ const js = function(s) { }), t.innerHTML.replace(yl, ""); } }; var El = Object.freeze({ __proto__: null }); -class O extends q { +class O extends H { constructor(t, e) { super(...arguments), this.attachmentManager = t, this.attachment = e, this.id = this.attachment.id, this.file = this.attachment.file; } @@ -2897,7 +2897,7 @@ class O extends q { } } O.proxyMethod("attachment.getAttribute"), O.proxyMethod("attachment.hasAttribute"), O.proxyMethod("attachment.setAttribute"), O.proxyMethod("attachment.getAttributes"), O.proxyMethod("attachment.setAttributes"), O.proxyMethod("attachment.isPending"), O.proxyMethod("attachment.isPreviewable"), O.proxyMethod("attachment.getURL"), O.proxyMethod("attachment.getPreviewURL"), O.proxyMethod("attachment.setPreviewURL"), O.proxyMethod("attachment.getHref"), O.proxyMethod("attachment.getFilename"), O.proxyMethod("attachment.getFilesize"), O.proxyMethod("attachment.getFormattedFilesize"), O.proxyMethod("attachment.getExtension"), O.proxyMethod("attachment.getContentType"), O.proxyMethod("attachment.getFile"), O.proxyMethod("attachment.setFile"), O.proxyMethod("attachment.releaseFile"), O.proxyMethod("attachment.getUploadProgress"), O.proxyMethod("attachment.setUploadProgress"); -class Aa extends q { +class Aa extends H { constructor() { let t = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : []; super(...arguments), this.managedAttachments = {}, Array.from(t).forEach((e) => { @@ -2952,7 +2952,7 @@ class Ea { return this.block.hasAttributes() && !this.block.isListItem() && this.block.isEmpty(); } } -class xt extends q { +class xt extends H { constructor() { super(...arguments), this.document = new nt(), this.attachments = [], this.currentAttributes = {}, this.revision = 0; } @@ -3153,7 +3153,7 @@ class xt extends q { const i = this.getPreviousBlock(); if (i) return function() { let n = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : []; - return Ht((arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : []).slice(0, n.length), n); + return qt((arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : []).slice(0, n.length), n); }(i.getListItemAttributes(), e.getListItemAttributes()); } } @@ -3400,7 +3400,7 @@ class xt extends q { } } xt.proxyMethod("getSelectionManager().getPointRange"), xt.proxyMethod("getSelectionManager().setLocationRangeFromPointRange"), xt.proxyMethod("getSelectionManager().createLocationRangeFromDOMRange"), xt.proxyMethod("getSelectionManager().locationIsCursorTarget"), xt.proxyMethod("getSelectionManager().selectionIsExpanded"), xt.proxyMethod("delegate?.getSelectionManager"); -class In extends q { +class In extends H { constructor(t) { super(...arguments), this.composition = t, this.undoEntries = [], this.redoEntries = []; } @@ -3702,7 +3702,7 @@ class Ta { return [e[0], e[e.length - 1]]; } } -class Dt extends q { +class Dt extends H { constructor(t) { super(...arguments), this.didMouseDown = this.didMouseDown.bind(this), this.selectionDidChange = this.selectionDidChange.bind(this), this.element = t, this.locationMapper = new Ca(this.element), this.pointMapper = new Ta(), this.lockCount = 0, B("mousedown", { onElement: this.element, withCallback: this.didMouseDown }); } @@ -3770,7 +3770,7 @@ class Dt extends q { t = ["mousemove", "keydown"].map((n) => B(n, { onElement: document, withCallback: e })); } selectionDidChange() { - if (!this.paused && !Hn(this.element)) return this.updateCurrentLocationRange(); + if (!this.paused && !qn(this.element)) return this.updateCurrentLocationRange(); } updateCurrentLocationRange(t) { var e, i; @@ -3800,7 +3800,7 @@ const { lang: tn, css: Rt, keyNames: Tl } = Be, en = function(s) { t.do(), this.undos || (this.undos = []), this.undos.push(t.undo); }; }; -class Ia extends q { +class Ia extends H { constructor(t, e, i) { let n = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {}; super(...arguments), G(this, "makeElementMutable", en(() => ({ do: () => { @@ -3862,7 +3862,7 @@ class Ia extends q { return this.savePendingCaption(); } } -class Ra extends q { +class Ra extends H { constructor(t, e) { super(...arguments), this.didFocus = this.didFocus.bind(this), this.didBlur = this.didBlur.bind(this), this.didClickAttachment = this.didClickAttachment.bind(this), this.element = t, this.composition = e, this.documentView = new gi(this.composition.document, { element: this.element }), B("focus", { onElement: this.element, withCallback: this.didFocus }), B("blur", { onElement: this.element, withCallback: this.didBlur }), B("click", { onElement: this.element, matchingSelector: "a[contenteditable=false]", preventDefault: !0 }), B("mousedown", { onElement: this.element, matchingSelector: Mt, withCallback: this.didClickAttachment }), B("click", { onElement: this.element, matchingSelector: "a".concat(Mt), preventDefault: !0 }); } @@ -3877,7 +3877,7 @@ class Ra extends q { didBlur(t) { this.blurPromise = new Promise((e) => Un(() => { var i, n; - return Hn(this.element) || (this.focused = null, (i = this.delegate) === null || i === void 0 || (n = i.compositionControllerDidBlur) === null || n === void 0 || n.call(i)), this.blurPromise = null, e(); + return qn(this.element) || (this.focused = null, (i = this.delegate) === null || i === void 0 || (n = i.compositionControllerDidBlur) === null || n === void 0 || n.call(i)), this.blurPromise = null, e(); })); } didClickAttachment(t, e) { @@ -3952,10 +3952,10 @@ class Ra extends q { return this.composition.document.getAttachmentById(parseInt(t.dataset.trixId, 10)); } } -class Da extends q { +class Da extends H { } const Oa = "data-trix-mutable", kl = "[".concat(Oa, "]"), Il = { attributes: !0, childList: !0, characterData: !0, characterDataOldValue: !0, subtree: !0 }; -class Ba extends q { +class Ba extends H { constructor(t) { super(t), this.didMutate = this.didMutate.bind(this), this.element = t, this.observer = new window.MutationObserver(this.didMutate), this.start(); } @@ -4094,7 +4094,7 @@ class Rl { } } const Dl = (s, t) => zs(s) === zs(t), Ol = new RegExp("(".concat("", "|").concat(ui, "|").concat(St, "|\\s)+"), "g"), zs = (s) => s.replace(Ol, " ").trim(); -class pi extends q { +class pi extends H { constructor(t) { super(...arguments), this.element = t, this.mutationObserver = new Ba(this.element), this.mutationObserver.delegate = this, this.flakyKeyboardDetector = new Rl(this.element); for (const e in this.constructor.events) B(e, { onElement: this.element, withCallback: this.handlerFor(e) }); @@ -4127,7 +4127,7 @@ class pi extends q { handlerFor(t) { return (e) => { e.defaultPrevented || this.handleInput(() => { - if (!Hn(this.element)) { + if (!qn(this.element)) { if (this.flakyKeyboardDetector.shouldIgnore(e)) return; this.eventName = t, this.constructor.events[t].call(this, e); } @@ -4169,7 +4169,7 @@ class bt extends pi { this.inputSummary = {}; } reset() { - return this.resetInputSummary(), qt.reset(); + return this.resetInputSummary(), Ht.reset(); } elementDidMutate(t) { var e, i; @@ -4250,7 +4250,7 @@ G(bt, "events", { keydown(s) { ["ctrl", "alt", "shift", "meta"].forEach((r) => { var a; s["".concat(r, "Key")] && (r === "ctrl" && (r = "control"), n = (a = n) === null || a === void 0 ? void 0 : a[r]); - }), ((e = n) === null || e === void 0 ? void 0 : e[t]) != null && (this.setInputSummary({ keyName: t }), qt.reset(), n[t].call(this, s)); + }), ((e = n) === null || e === void 0 ? void 0 : e[t]) != null && (this.setInputSummary({ keyName: t }), Ht.reset(), n[t].call(this, s)); } if (na(s)) { const n = String.fromCharCode(s.keyCode).toLowerCase(); @@ -4311,7 +4311,7 @@ G(bt, "events", { keydown(s) { (t = this.responder) !== null && t !== void 0 && t.selectionIsExpanded() && this.serializeSelectionToDataTransfer(s.clipboardData) && s.preventDefault(); }, paste(s) { const t = s.clipboardData || s.testClipboardData, e = { clipboard: t }; - if (!t || Hl(s)) return void this.getPastedHTMLUsingHiddenElement((_) => { + if (!t || ql(s)) return void this.getPastedHTMLUsingHiddenElement((_) => { var S, E, z; return e.type = "text/html", e.html = _, (S = this.delegate) === null || S === void 0 || S.inputControllerWillPaste(e), (E = this.responder) === null || E === void 0 || E.insertHTML(e.html), this.requestRender(), (z = this.delegate) === null || z === void 0 ? void 0 : z.inputControllerDidPaste(e); }); @@ -4401,7 +4401,7 @@ const Nl = (s) => { let t; if (s.which === null ? t = s.keyCode : s.which !== 0 && s.charCode !== 0 && (t = s.charCode), t != null && Na[t] !== "escape") return ke.fromCodepoints([t]).toString(); } -}, Hl = function(s) { +}, ql = function(s) { const t = s.clipboardData; if (t) { if (t.types.includes("text/html")) { @@ -4417,7 +4417,7 @@ const Nl = (s) => { } } }; -class Et extends q { +class Et extends H { constructor(t) { super(...arguments), this.inputController = t, this.responder = this.inputController.responder, this.delegate = this.inputController.delegate, this.inputSummary = this.inputController.inputSummary, this.data = {}; } @@ -4505,14 +4505,14 @@ class Ce extends pi { } withTargetDOMRange(t, e) { var i; - return typeof t == "function" && (e = t, t = this.getTargetDOMRange()), t ? (i = this.responder) === null || i === void 0 ? void 0 : i.withTargetDOMRange(t, e.bind(this)) : (qt.reset(), e.call(this)); + return typeof t == "function" && (e = t, t = this.getTargetDOMRange()), t ? (i = this.responder) === null || i === void 0 ? void 0 : i.withTargetDOMRange(t, e.bind(this)) : (Ht.reset(), e.call(this)); } getTargetDOMRange() { var t, e; let { minLength: i } = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : { minLength: 0 }; const n = (t = (e = this.event).getTargetRanges) === null || t === void 0 ? void 0 : t.call(e); if (n && n.length) { - const r = ql(n[0]); + const r = Hl(n[0]); if (i === 0 || r.toString().length >= i) return r; } } @@ -4549,7 +4549,7 @@ G(Ce, "events", { keydown(s) { var i; t && (this.withEvent(s, t), e || this.scheduleRender()), e && this.render(); }, input(s) { - qt.reset(); + Ht.reset(); }, dragstart(s) { var t, e; (t = this.responder) !== null && t !== void 0 && t.selectionContainsAttachments() && (s.dataTransfer.setData("application/x-trix-dragging", !0), this.dragging = { range: (e = this.responder) === null || e === void 0 ? void 0 : e.getSelectedRange(), point: rn(s) }); @@ -4758,7 +4758,7 @@ G(Ce, "events", { keydown(s) { }, insertUnorderedList() { return this.toggleAttributeIfSupported("bullet"); } }); -const ql = function(s) { +const Hl = function(s) { const t = document.createRange(); return t.setStart(s.startContainer, s.startOffset), t.setEnd(s.endContainer, s.endOffset), t; }, sn = (s) => { @@ -4781,7 +4781,7 @@ const ql = function(s) { const t = []; return s.altKey && t.push("alt"), s.shiftKey && t.push("shift"), t.push(s.key), t; }, rn = (s) => ({ x: s.clientX, y: s.clientY }), Dn = "[data-trix-attribute]", On = "[data-trix-action]", jl = "".concat(Dn, ", ").concat(On), fi = "[data-trix-dialog]", Wl = "".concat(fi, "[data-trix-active]"), zl = "".concat(fi, " [data-trix-method]"), Ks = "".concat(fi, " [data-trix-input]"), Gs = (s, t) => (t || (t = te(s)), s.querySelector("[data-trix-input][name='".concat(t, "']"))), Js = (s) => s.getAttribute("data-trix-action"), te = (s) => s.getAttribute("data-trix-attribute") || s.getAttribute("data-trix-dialog-attribute"); -class Fa extends q { +class Fa extends H { constructor(t) { super(t), this.didClickActionButton = this.didClickActionButton.bind(this), this.didClickAttributeButton = this.didClickAttributeButton.bind(this), this.didClickDialogButton = this.didClickDialogButton.bind(this), this.didKeyDownDialogInput = this.didKeyDownDialogInput.bind(this), this.element = t, this.attributes = {}, this.actions = {}, this.resetDialogInputs(), B("mousedown", { onElement: this.element, matchingSelector: On, withCallback: this.didClickActionButton }), B("mousedown", { onElement: this.element, matchingSelector: Dn, withCallback: this.didClickAttributeButton }), B("click", { onElement: this.element, matchingSelector: jl, preventDefault: !0 }), B("click", { onElement: this.element, matchingSelector: zl, withCallback: this.didClickDialogButton }), B("keydown", { onElement: this.element, matchingSelector: Ks, withCallback: this.didKeyDownDialogInput }); } @@ -4890,13 +4890,13 @@ class Fa extends q { class we extends Da { constructor(t) { let { editorElement: e, document: i, html: n } = t; - super(...arguments), this.editorElement = e, this.selectionManager = new Dt(this.editorElement), this.selectionManager.delegate = this, this.composition = new xt(), this.composition.delegate = this, this.attachmentManager = new Aa(this.composition.getAttachments()), this.attachmentManager.delegate = this, this.inputController = qn.getLevel() === 2 ? new Ce(this.editorElement) : new bt(this.editorElement), this.inputController.delegate = this, this.inputController.responder = this.composition, this.compositionController = new Ra(this.editorElement, this.composition), this.compositionController.delegate = this, this.toolbarController = new Fa(this.editorElement.toolbarElement), this.toolbarController.delegate = this, this.editor = new La(this.composition, this.selectionManager, this.editorElement), i ? this.editor.loadDocument(i) : this.editor.loadHTML(n); + super(...arguments), this.editorElement = e, this.selectionManager = new Dt(this.editorElement), this.selectionManager.delegate = this, this.composition = new xt(), this.composition.delegate = this, this.attachmentManager = new Aa(this.composition.getAttachments()), this.attachmentManager.delegate = this, this.inputController = Hn.getLevel() === 2 ? new Ce(this.editorElement) : new bt(this.editorElement), this.inputController.delegate = this, this.inputController.responder = this.composition, this.compositionController = new Ra(this.editorElement, this.composition), this.compositionController.delegate = this, this.toolbarController = new Fa(this.editorElement.toolbarElement), this.toolbarController.delegate = this, this.editor = new La(this.composition, this.selectionManager, this.editorElement), i ? this.editor.loadDocument(i) : this.editor.loadHTML(n); } registerSelectionManager() { - return qt.registerSelectionManager(this.selectionManager); + return Ht.registerSelectionManager(this.selectionManager); } unregisterSelectionManager() { - return qt.unregisterSelectionManager(this.selectionManager); + return Ht.unregisterSelectionManager(this.selectionManager); } render() { return this.compositionController.render(); @@ -5171,7 +5171,7 @@ G(we, "actions", { undo: { test() { }, perform() { return this.editor.decreaseNestingLevel() && this.render(); } }, attachFiles: { test: () => !0, perform() { - return qn.pickFiles(this.editor.insertFiles); + return Hn.pickFiles(this.editor.insertFiles); } } }), we.proxyMethod("getSelectionManager().setLocationRange"), we.proxyMethod("getSelectionManager().getLocationRange"); var Kl = Object.freeze({ __proto__: null, AttachmentEditorController: Ia, CompositionController: Ra, Controller: Da, EditorController: we, InputController: pi, Level0InputController: bt, Level2InputController: Ce, ToolbarController: Fa }), Gl = Object.freeze({ __proto__: null, MutationObserver: Ba, SelectionChangeObserver: ra }), Jl = Object.freeze({ __proto__: null, FileVerificationOperation: Ma, ImagePreloadOperation: _a }); ea("trix-toolbar", `%t { @@ -5193,7 +5193,7 @@ ea("trix-toolbar", `%t { %t [data-trix-dialog] [data-trix-validate]:invalid { background-color: #ffdddd; }`); -class Ha extends HTMLElement { +class qa extends HTMLElement { connectedCallback() { this.innerHTML === "" && (this.innerHTML = ta.getDefaultHTML()); } @@ -5556,9 +5556,9 @@ class di extends HTMLElement { } } G(di, "formAssociated", "ElementInternals" in window), G(di, "observedAttributes", ["connected"]); -const Xs = { VERSION: io, config: Be, core: El, models: ka, views: wl, controllers: Kl, observers: Gl, operations: Jl, elements: Object.freeze({ __proto__: null, TrixEditorElement: di, TrixToolbarElement: Ha }), filters: Object.freeze({ __proto__: null, Filter: xa, attachmentGalleryFilter: Sa }) }; +const Xs = { VERSION: io, config: Be, core: El, models: ka, views: wl, controllers: Kl, observers: Gl, operations: Jl, elements: Object.freeze({ __proto__: null, TrixEditorElement: di, TrixToolbarElement: qa }), filters: Object.freeze({ __proto__: null, Filter: xa, attachmentGalleryFilter: Sa }) }; Object.assign(Xs, ka), window.Trix = Xs, setTimeout(function() { - customElements.get("trix-toolbar") || customElements.define("trix-toolbar", Ha), customElements.get("trix-editor") || customElements.define("trix-editor", di); + customElements.get("trix-toolbar") || customElements.define("trix-toolbar", qa), customElements.get("trix-editor") || customElements.define("trix-editor", di); }, 0); class nd extends HTMLElement { constructor() { @@ -6342,7 +6342,7 @@ class hd extends HTMLElement { } ci = new WeakMap(); const ud = "msr-component-wrapper", Zs = "msr-selected-items-container", tr = "msr-placeholder-no-selection-text", md = "msr-selected-item-pill", gd = "msr-selected-item-text", pd = "msr-item-name", fd = "msr-item-additional-data", bd = "msr-selected-item-role", er = "msr-selected-item-delete-btn", _d = "msr-controls-area", ir = "msr-pre-add-button", nr = "msr-input-area-wrapper", Qe = "msr-input-area-default-border", dn = "msr-input-area-staged", sr = "msr-staging-area-container", vd = "msr-staged-item-pill", yd = "msr-staged-item-text", cn = "msr-staged-role-select", rr = "msr-staged-cancel-btn", ar = "msr-text-input", or = "msr-add-button", lr = "msr-options-list", dr = "msr-option-item", Ad = "msr-option-item-name", Ed = "msr-option-item-detail", cr = "msr-option-item-highlighted", hn = "msr-hidden-select", xd = "msr-state-no-selection", Sd = "msr-state-has-selection", Ld = "msr-state-list-open", Cd = "msr-state-item-staged"; -class qa extends HTMLElement { +class Ha extends HTMLElement { constructor() { super(); Xt(this, "_blurTimeout", null); @@ -6783,8 +6783,8 @@ class qa extends HTMLElement { this.hasAttribute("disabled") || (this._value = this._value.filter((i) => i.instanceId !== e), this._updateFormValue(), this._renderSelectedItems(), this._stagedItem && this._stagedItem.item && this._renderStagedPillOrInput(), this.inputElement && this.inputElement.focus(), this._updatePreAddButtonVisibility()); } } -Xt(qa, "formAssociated", !0); -const wd = "mss-component-wrapper", hr = "mss-selected-items-container", Td = "mss-selected-item-pill", kd = "mss-selected-item-text", Id = "mss-selected-item-pill-detail", ur = "mss-selected-item-delete-btn", Rd = "mss-selected-item-edit-link", mr = "mss-input-controls-container", gr = "mss-input-wrapper", pr = "mss-input-wrapper-focused", fr = "mss-text-input", br = "mss-create-new-button", _r = "mss-toggle-button", Dd = "mss-inline-row", vr = "mss-options-list", Od = "mss-option-item", Bd = "mss-option-item-name", Md = "mss-option-item-detail", yr = "mss-option-item-highlighted", un = "mss-hidden-select", mn = "mss-no-items-text", Ar = "mss-loading", gn = 1, pn = 10, Nd = 250, Pd = "mss-state-no-selection", Fd = "mss-state-has-selection", Hd = "mss-state-list-open"; +Xt(Ha, "formAssociated", !0); +const wd = "mss-component-wrapper", hr = "mss-selected-items-container", Td = "mss-selected-item-pill", kd = "mss-selected-item-text", Id = "mss-selected-item-pill-detail", ur = "mss-selected-item-delete-btn", Rd = "mss-selected-item-edit-link", mr = "mss-input-controls-container", gr = "mss-input-wrapper", pr = "mss-input-wrapper-focused", fr = "mss-text-input", br = "mss-create-new-button", _r = "mss-toggle-button", Dd = "mss-inline-row", vr = "mss-options-list", Od = "mss-option-item", Bd = "mss-option-item-name", Md = "mss-option-item-detail", yr = "mss-option-item-highlighted", un = "mss-hidden-select", mn = "mss-no-items-text", Ar = "mss-loading", gn = 1, pn = 10, Nd = 250, Pd = "mss-state-no-selection", Fd = "mss-state-has-selection", qd = "mss-state-list-open"; class $a extends HTMLElement { constructor() { super(); @@ -7117,7 +7117,7 @@ class $a extends HTMLElement { this.inputElement && (this.inputElement.disabled = e), this.createNewButton && (this.createNewButton.disabled = e), this.toggleAttribute("disabled", e), this.querySelectorAll(`.${ur}`).forEach((i) => i.disabled = e), this.hiddenSelect && (this.hiddenSelect.disabled = e), e && this._hideOptionsList(); } _updateRootElementStateClasses() { - this.classList.toggle(Pd, this._value.length === 0), this.classList.toggle(Fd, this._value.length > 0), this.classList.toggle(Hd, this._isOptionsListVisible); + this.classList.toggle(Pd, this._value.length === 0), this.classList.toggle(Fd, this._value.length > 0), this.classList.toggle(qd, this._isOptionsListVisible); } _render() { const e = this.id || `mss-${crypto.randomUUID().slice(0, 8)}`; @@ -7391,7 +7391,7 @@ class $a extends HTMLElement { } } Xt($a, "formAssociated", !0); -const qd = "rbi-button", $d = "rbi-icon"; +const Hd = "rbi-button", $d = "rbi-icon"; class Ud extends HTMLElement { constructor() { super(), this.initialStates = /* @__PURE__ */ new Map(), this._controlledElements = [], this.button = null, this.lastOverallModifiedState = null, this.handleInputChange = this.handleInputChange.bind(this), this.handleReset = this.handleReset.bind(this); @@ -7401,7 +7401,7 @@ class Ud extends HTMLElement { } connectedCallback() { const t = ` - ${f} - +