diff --git a/.claude/skills/reset-total/SKILL.md b/.claude/skills/reset-total/SKILL.md new file mode 100644 index 0000000..05fbd5f --- /dev/null +++ b/.claude/skills/reset-total/SKILL.md @@ -0,0 +1,108 @@ +--- +name: reset-total +description: >- + Cierra la sesión antes de compactar o reiniciar: vuelca el estado del trabajo en Vitrina a un .md + de continuidad en docs/sessions/, pasa las reglas durables a CLAUDE.md y entrega el prompt para + reanudar. Úsala cuando Sergio diga "reset-total", "reset total", "voy a compactar", "voy a + colapsar", "guarda todo antes de compactar", "cierra la sesión", "prepárame para reiniciar", + antes de un /compact, o cada ~350k tokens en una sesión autónoma larga. +--- + +# reset-total — guardar la sesión antes de compactar + +Objetivo: que después de compactar o de abrir una sesión nueva no se pierda nada que importe, y +que Sergio no tenga que volver a dictar las instrucciones de siempre. + +Tres salidas, siempre las tres: **un .md de estado**, **CLAUDE.md al día**, y **un prompt de +reanudación**. + +## Procedimiento + +### 1. Recoger lo que hay que salvar + +Sale de la conversación actual. No se reconstruye de memoria ni se inventa. Seis cajones: + +1. **Pendiente de cerrar**: cambios sin commit, commits sin push, PR abiertas o pedidas, checks + rojos, preguntas hechas a Sergio que esperan respuesta. Para cada uno: qué es, estado real + (escrito, probado, commiteado, pusheado) y dónde está. +2. **Estado del trabajo**: rama, último commit (hash corto), qué ha cambiado en esta sesión por + capa (`src/domain`, `src/generation`, `src/server`, `src/studio`, `src/ui`, `src/app`, docs, + infra), y el resultado exacto de la última pasada de `pnpm lint`, `pnpm typecheck`, + `pnpm test`, `pnpm build`, `pnpm test:e2e` (número de tests, verde o rojo). +3. **Correcciones de datos**: cualquier cosa que se creía y resultó falsa (una API que no se + comporta como se suponía, un flag que no existe, una versión incompatible). Esto es lo que más + valor tiene, porque es lo que se vuelve a colar si se pierde. +4. **Decisiones tomadas**: producto, arquitectura, nombres, precios, alcance descartado — con el + motivo en una línea. Lo que Sergio decidió, separado de lo que decidió el agente. +5. **Reglas nuevas** que Sergio haya dado durante la sesión. +6. **Entregables generados**: rutas de ficheros y URLs (rama, PR, artefactos). + +### 2. Verificar antes de escribir + +- **Comprobar que cada ruta existe** con `ls` antes de listarla. Un fichero que no existe no se + lista. +- **Comprobar git**: `git status --short`, `git log --oneline -5`, `git log origin/ -1`. + Nada se da por commiteado o pusheado si no consta ahí. +- Si hay en `docs/sessions/` ficheros de estado **que no se escribieron en esta sesión**, se listan + marcados como "de otra sesión" y **no se describe lo que no se ha leído**. +- **Releer `CLAUDE.md`**: si otra sesión añadió reglas desde que empezó esta, cruzarlas con los + pendientes; una regla nueva puede cambiar un cambio que está a punto de salir. +- **Repasar el scratchpad** de la sesión (`CONTEXT.md` si existe) y absorber lo que siga vigente. + +### 3. Escribir el .md de estado + +Ruta: `docs/sessions/estado-sesion-.md` (fecha de hoy). Si ya hay uno de hoy, se +sustituye y la cabecera dice que sustituye al anterior. + +Estructura fija, en este orden: + +1. Pendiente de cerrar (tabla: qué · estado real · dónde está) +2. Estado del trabajo (rama, commit, cambios por capa, resultado de los checks) +3. Correcciones de datos +4. Decisiones tomadas +5. Reglas nuevas +6. Entregables (tabla con rutas y URLs verificadas) +7. Herramientas y estado técnico (servidores en marcha, puertos, env local, mock, Chromium, + lo que no se pudo probar y por qué) +8. Límites duros (lo que no se hace sin preguntar: push a otra rama, PR, borrar, producción) + +### 4. Persistir + +- El .md va en el repo: `git add docs/sessions/... && git commit` **solo del .md**, nunca mezclado + con código a medias. Se pushea a la rama de trabajo si esa rama ya se pusheaba en la sesión. +- Si hay código sin commit que Sergio no ha pedido commitear, se deja como está y se anota en + "Pendiente de cerrar". + +### 5. CLAUDE.md + +Solo si hay una **regla nueva y durable** o una **corrección de algo que ya está escrito**: + +- Regla nueva → una línea en "Rules of the house" (o un comando nuevo en la tabla). +- Corrección → editar la línea existente, no añadir otra que la contradiga. +- No guardar lo que ya está en el repo, en el git o en el propio .md de estado. + +### 6. Entregar el prompt de reanudación + +En un bloque de código, listo para pegar. Tiene que: + +- Nombrar el .md de estado y pedir que se lea primero, junto con `CLAUDE.md`. +- Nombrar la rama y el último commit. +- Resumir en cinco o seis líneas lo imprescindible: pendientes, checks rojos y límites duros. +- **No** repetir el .md entero. + +## Reglas duras + +- **Nunca escribir secretos** en el .md, en CLAUDE.md ni en el prompt: `APP_SECRET`, claves de + plataforma, tokens de Stripe, contraseñas de cuentas de prueba. Si se detectó uno durante la + sesión, se nombra el problema, no el valor. `.env` no se cita por contenido. +- Hashes, rutas, cifras y números de tests exactos. Lo no verificado se marca como tal. +- Cero emojis e iconos. +- Esta skill **no pushea código, no abre PR y no toca producción**. Solo guarda el estado. +- Si el commit del .md falla, el .md y el prompt se entregan igual. La persistencia nunca bloquea + la entrega. + +## Salida al usuario + +Corta. Tres líneas como mucho sobre qué se ha guardado y dónde, y el bloque con el prompt de +reanudación. Si al releer CLAUDE.md o el scratchpad ha salido algo que cambia un pendiente, se +dice en una línea. diff --git a/.claude/skills/wipe-local/SKILL.md b/.claude/skills/wipe-local/SKILL.md new file mode 100644 index 0000000..c16d9ba --- /dev/null +++ b/.claude/skills/wipe-local/SKILL.md @@ -0,0 +1,51 @@ +--- +name: wipe-local +description: Reset the local Vitrina environment to a clean slate — local database, uploads, build output and test artifacts (optionally node_modules) — then recreate the database. Use when the dev database is corrupted or full of test junk, migrations diverged, the build cache misbehaves, or you want to reproduce a first-run experience. Never for a deployed environment. +--- + +# wipe-local + +Wipes everything local and regenerated, keeps everything authored. + +## What it removes + +| Path | Why | +| --- | --- | +| `data/` (or `DATA_DIR`) | The libsql database file and local uploads | +| `.next/` | Build output and Turbopack cache | +| `test-results/`, `playwright-report/` | e2e artifacts | +| `node_modules/` | Only with `--deps`; reinstalled from the lockfile | + +Kept: `.env`, source, git history, `drizzle/` migrations. + +## Steps + +1. Confirm the target is local. The script refuses when `DATABASE_URL` is not a + `file:` URL — a remote (Turso) database is never reset from here. If the + user seems to be on a deployed environment, stop and say so. +2. Stop running servers first (`pnpm dev`, `pnpm start`, `pnpm mock:platform`): + an open libsql handle can recreate the file mid-reset. +3. Preview, then run: + + ```bash + node scripts/wipe-local.mjs --dry-run # lists what would go + node scripts/wipe-local.mjs --yes # data + build + artifacts + node scripts/wipe-local.mjs --yes --deps # also node_modules, then pnpm install + node scripts/wipe-local.mjs --yes --keep-uploads # keep data/uploads, drop the DB + ``` + + Without `--yes` the script asks to type `RESET` when run interactively. +4. The script ends by running the migrations, so the database exists and is + at the current schema. Start `pnpm dev` and register a fresh account + (30 trial credits); to skip Stripe, grant credits with + `pnpm credits `. +5. Report what was removed and that the studio is empty. + +## Rules + +- This is destructive and unrecoverable for local runs and uploads: when the + user did not explicitly ask for a reset, ask before running anything but + `--dry-run`. +- Do not "reset" by deleting individual tables or editing the database by + hand; the script is the one path so the migration journal stays consistent. +- Do not touch `.env`; a broken environment file is fixed by hand, not by reset. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..61f6637 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +node_modules +.next +.git +data +test-results +playwright-report +.env +.env.* +!.env.example diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index bdb0cab..0000000 --- a/.gitattributes +++ /dev/null @@ -1,17 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto - -# Custom for Visual Studio -*.cs diff=csharp - -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..15a494b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,66 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + checks: + name: Lint, types, unit tests, build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm lint + - run: pnpm typecheck + - run: pnpm test + - run: pnpm build + env: + APP_SECRET: ci-build-secret + - uses: actions/upload-artifact@v4 + with: + name: next-build + path: .next + retention-days: 1 + include-hidden-files: true + + e2e: + name: End-to-end (mock platform) + runs-on: ubuntu-latest + needs: checks + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + - run: pnpm install --frozen-lockfile + - uses: actions/download-artifact@v4 + with: + name: next-build + path: .next + - run: pnpm exec playwright install --with-deps chromium + - run: pnpm test:e2e + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-report + path: | + playwright-report + test-results + retention-days: 7 diff --git a/.gitignore b/.gitignore index cd2946a..881e6cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,19 @@ -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# ========================= -# Operating System Files -# ========================= - -# OSX -# ========================= - +node_modules .DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +.next +out +dist +*.local +.env +.env.* +!.env.example +.vercel +tsconfig.tsbuildinfo + +.env* +.idea + +# local database and uploads +data/ +test-results/ +playwright-report/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9f85e61 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,52 @@ +# Vitrina — working notes for agents + +Next.js 16 (App Router, Turbopack) · React 19 · TypeScript strict · plain CSS · +Zustand · Drizzle + libsql · better-auth · Stripe · vitest · Playwright · pnpm. + +## Commands + +| Command | Purpose | +| --- | --- | +| `pnpm dev` | Dev server on :3000 (migrations run on boot) | +| `pnpm mock:platform` | Stand-in generation API on :4010 — point `HF_API_BASE_URL` at it | +| `pnpm lint` / `pnpm typecheck` / `pnpm test` | ESLint, `tsc --noEmit`, vitest | +| `pnpm build` | Production build (standalone output) | +| `pnpm test:e2e` | Playwright against a fresh prod build + mock platform | +| `pnpm db:generate` | New migration after editing `src/db/schema.ts` | +| `pnpm credits ` | Grant credits without Stripe | +| `pnpm wipe:local [--deps] [--keep-uploads] [--yes] [--dry-run]` | Wipe local DB, uploads, build and test artifacts, then re-migrate (skill `wipe-local`) | + +## Skills + +- `reset-total` — close the session before a compact or restart: state file in + `docs/sessions/`, durable rules into this file, resume prompt. +- `wipe-local` — destructive local reset of data, build and artifacts. + +Set `PLAYWRIGHT_CHROMIUM_PATH` to reuse a Chromium already on disk. + +## Where things live + +- `src/domain/` — the vertical as data: channels, presets, prompt compiler, + pricing, plans. Pure, unit-tested, no I/O. Change the industry here. +- `src/generation/` — the engine inherited from open-higgsfield: catalog, + platform client, request mapper, poll loop, server actions (`actions.ts`). +- `src/server/` — DB-backed services and `actions/*` server actions. Every + action starts with `requireViewer()`; pages start with `viewerOrRedirect()`. +- `src/studio/` — the studio surface (composer, gallery, viewer, job rail). + `use-generation.ts` is the run lifecycle shared with the campaign page. +- `src/ui/` — app pages' client components and `app.css`. +- `src/app/` — routes. `/` marketing, `/login`, `/register`, `/app/*` behind + the session, `/api/*` auth, uploads, files, billing webhook. + +## Rules of the house + +- The catalog is the source of truth for what a model takes; presets and + channels only choose among what it declares. `resolveSettings` must always + produce something `parseSettings` accepts (there is a test for it). +- Credits are integers, reserved before the platform is asked, refunded once + per request. Never touch `credit_balance` outside `src/server/credits.ts`. +- Server actions in `"use server"` files export async functions only; put + helpers in a plain module (`src/server/validation.ts` is the pattern). +- CSS classes are prefixed `vt-`; tokens live on `.vt` in `studio.css`. +- Keep the design principles from the original README: accent is state, not + decoration; every control ships all its states; nothing loops decoratively. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9ac303f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +# syntax=docker/dockerfile:1.7 +# Vitrina — single-container deployment. The database and uploads live in +# /data, so mount a volume there. + +FROM node:22-bookworm-slim AS base +ENV PNPM_HOME=/pnpm PATH=/pnpm:$PATH +RUN corepack enable && corepack prepare pnpm@10 --activate +WORKDIR /app + +FROM base AS deps +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +FROM deps AS build +COPY . . +ENV NEXT_TELEMETRY_DISABLED=1 NODE_ENV=production +ARG NEXT_PUBLIC_STORAGE_DRIVER=local +ARG NEXT_PUBLIC_SITE_URL= +ENV NEXT_PUBLIC_STORAGE_DRIVER=$NEXT_PUBLIC_STORAGE_DRIVER NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL +RUN pnpm build + +FROM node:22-bookworm-slim AS runtime +ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 PORT=3000 HOSTNAME=0.0.0.0 +ENV DATA_DIR=/data DATABASE_URL=file:/data/vitrina.db STORAGE_DRIVER=local +WORKDIR /app +RUN groupadd --system vitrina && useradd --system --gid vitrina --home /app vitrina \ + && mkdir -p /data && chown vitrina:vitrina /data +COPY --from=build --chown=vitrina:vitrina /app/.next/standalone ./ +COPY --from=build --chown=vitrina:vitrina /app/.next/static ./.next/static +COPY --from=build --chown=vitrina:vitrina /app/public ./public +COPY --from=build --chown=vitrina:vitrina /app/drizzle ./drizzle +COPY --from=build --chown=vitrina:vitrina /app/scripts/credits.mjs ./scripts/credits.mjs +# Migrations run inside the server on boot (src/instrumentation.ts); the +# credits script rides along for operators without Stripe. +USER vitrina +VOLUME ["/data"] +EXPOSE 3000 +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s CMD node -e "fetch('http://localhost:3000/login').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" +CMD ["node", "server.js"] diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..5d6de2b --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,20 @@ +# Notice + +Vitrina is built on top of the generation studio published as +**open-higgsfield** (), which +describes itself as "the free, open-source alternative to Higgsfield AI". + +The imported foundation (catalog-driven image/video generation, the studio +surface, the platform client and poll loop, the design system) is theirs. The +vertical layer — domain, workspace, billing, presets, channels, app pages — is +this repository's own work. + +At the time of import the upstream repository carried **no LICENSE file**. The +README calls the project open source, but no license text grants the rights +that word implies. Before selling or redistributing this product, confirm the +upstream license terms with its author or replace the imported foundation. +This file records the provenance so that conversation can happen with the +facts in hand. + +Vitrina is not affiliated with Higgsfield AI. "Higgsfield" is used only to +name the generation platform the studio talks to. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c729bcb --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# Vitrina — AI product visuals for e-commerce brands + +> Product photos and clips for every channel, from the photo you already have. + +Vitrina is a vertical product built on top of an open image/video generation +studio. An e-commerce team uploads a product photo, picks a **preset** (pack +shot, lifestyle scene, flat lay, on-model, product reveal, UGC-style clip…) +and the **channel** it is for (Instagram, TikTok, Amazon, storefront hero, +YouTube, Pinterest, email). Vitrina writes the prompt from the **brand kit**, +picks a model out of the catalog, fixes the ratio and length the channel +wants, tracks every run per workspace, and exports approved results as one +folder per product and channel. + +It is sold as a SaaS (the operator's platform key, metered in credits with +Stripe plans) or run self-hosted where every workspace brings its own key. + +![Studio](docs/screenshots/studio.png) + +## What is in the box + +- **Studio** — one composer for image and video, a product / preset / channel + rail, the final prompt on request, a gallery with product and review + filters, selection, bulk approve / reject / favorite / download. +- **Products** with photos, appearance notes and features; **brand kit** + (style, palette, audience, things never to show) folded into every prompt. +- **Campaigns** — one product × a set of presets × channels, generated in one + press, reviewed per channel, exported as a zip filed + `brand/product/channel/preset-01.jpg`. +- **Workspaces & accounts** — email/password sign-in, trial credits on + sign-up, invite links with seats per plan, several brands per account with + a workspace switcher, password reset by email when a sender is configured. +- **Credits & billing** — integer credits reserved before a run and refunded + on failure, Stripe Checkout, customer portal, idempotent webhook, a manual + grant script for installs without Stripe. +- **Bring your own key** — a workspace's platform key sealed at rest; runs on + it cost no credits. +- **38 models** from the inherited catalog, each with its own settings; the + preset only chooses among what a model declares. +- Local or Vercel Blob storage, a mock platform server for development, unit + and end-to-end tests, CI, Docker. + +## Quick start + +```bash +pnpm install +cp .env.example .env # set APP_SECRET, HF_API_BASE_URL, PLATFORM_API_KEY +pnpm dev # http://localhost:3000 — migrations run on boot +``` + +No platform key yet? Point the studio at the mock platform and see the whole +flow with placeholder results: + +```bash +pnpm mock:platform # :4010 +# in .env: HF_API_BASE_URL=http://localhost:4010 PLATFORM_API_KEY=mock-id:mock-secret +``` + +Sign up at `/register`, fill in the brand kit, add a product with a photo, +open the studio, pick the product and a preset, press Generate. + +## Commands + +| Command | What it does | +| --- | --- | +| `pnpm dev` / `pnpm build` / `pnpm start` | Dev server, production build (standalone), serve | +| `pnpm lint` · `pnpm typecheck` · `pnpm test` | ESLint, TypeScript, vitest | +| `pnpm test:e2e` | Playwright against a fresh production build and the mock platform | +| `pnpm db:generate` · `pnpm db:migrate` · `pnpm db:studio` | Drizzle migrations and browser | +| `pnpm credits [note]` | Grant credits to a workspace by owner email | +| `pnpm mock:platform` | Stand-in generation API | +| `pnpm brand` | Rebuild icons and the Open Graph card | + +## How the pieces fit + +``` +src/domain the vertical as data — presets, channels, prompt compiler, pricing, plans +src/generation the engine — catalog, platform client, request mapper, poll loop, actions +src/server DB services + server actions — session, workspaces, credits, runs, billing +src/studio the studio surface — composer, job rail, gallery, viewer, run lifecycle hook +src/ui app pages' components and stylesheet +src/app routes: / marketing · /login /register · /app/* · /api/* +``` + +Read [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the run lifecycle, the +data model and how money moves; [docs/DEPLOY.md](docs/DEPLOY.md) for Docker, +Vercel and Stripe; [docs/VERTICAL.md](docs/VERTICAL.md) to re-aim the product +at another industry — it is a change to data files, not to the engine. + +## Business model, in one table + +| Plan | Credits / month | Price | Seats | Own key | +| --- | --- | --- | --- | --- | +| Trial | 30 once | free | 1 | — | +| Starter | 500 | €29 | 2 | — | +| Pro | 2,000 | €99 | 5 | yes | +| Studio | 6,000 | €249 | 15 | yes | + +One credit ≈ one standard image (2 at 2K, 4 at 4K); video is priced per second +by resolution with tier multipliers. All of it is in `src/domain/plans.ts` and +`src/domain/pricing.ts`. + +## Provenance and license + +The generation studio this product is built on is +[open-higgsfield](https://github.com/wide-trace/open-higgsfield). See +[NOTICE.md](NOTICE.md): at the time of import the upstream repository carried +no LICENSE file, which matters before this is sold. Vitrina is not +affiliated with Higgsfield AI. diff --git a/_notes/dwsync.xml b/_notes/dwsync.xml deleted file mode 100644 index 1e5c2df..0000000 --- a/_notes/dwsync.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/advanceERP.zip b/advanceERP.zip deleted file mode 100644 index 5b5dff2..0000000 Binary files a/advanceERP.zip and /dev/null differ diff --git a/change.php b/change.php deleted file mode 100644 index 9d5f8c0..0000000 --- a/change.php +++ /dev/null @@ -1,49 +0,0 @@ - - - - - Change Password - - -

Passwords Doesn't match

The passwords you entered didn't match. Try again.

"; - }else if($LS->login($LS->getUser("username"), $curpass, false, false) == false){ - echo "

Current Password Wrong!

The password you entered for your account is wrong.

"; - }else{ - $change_password = $LS->changePassword($new_password); - if($change_password === true){ - echo "

Password Changed Successfully

"; - } - } - }else{ - echo "

Password Fields was blank

Form fields were left blank

"; - } - } - ?> -
- - - - -
- - diff --git a/css/32px.png b/css/32px.png deleted file mode 100644 index 1532715..0000000 Binary files a/css/32px.png and /dev/null differ diff --git a/css/40px.png b/css/40px.png deleted file mode 100644 index 1959347..0000000 Binary files a/css/40px.png and /dev/null differ diff --git a/css/_notes/dwsync.xml b/css/_notes/dwsync.xml deleted file mode 100644 index 1df4714..0000000 --- a/css/_notes/dwsync.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/css/csstables/_notes/dwsync.xml b/css/csstables/_notes/dwsync.xml deleted file mode 100644 index 847a0e8..0000000 --- a/css/csstables/_notes/dwsync.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/css/csstables/buttons.dataTables.min.css b/css/csstables/buttons.dataTables.min.css deleted file mode 100644 index a8758ee..0000000 --- a/css/csstables/buttons.dataTables.min.css +++ /dev/null @@ -1 +0,0 @@ -div.dt-button-info{position:fixed;top:50%;left:50%;width:400px;margin-top:-100px;margin-left:-200px;background-color:white;border:2px solid #111;box-shadow:3px 3px 8px rgba(0,0,0,0.3);border-radius:3px;text-align:center;z-index:21}div.dt-button-info h2{padding:0.5em;margin:0;font-weight:normal;border-bottom:1px solid #ddd;background-color:#f3f3f3}div.dt-button-info>div{padding:1em}button.dt-button,div.dt-button,a.dt-button{position:relative;display:inline-block;box-sizing:border-box;margin-right:0.333em;padding:0.5em 1em;border:1px solid #999;border-radius:2px;cursor:pointer;font-size:0.88em;color:black;white-space:nowrap;overflow:hidden;background-color:#e9e9e9;background-image:-webkit-linear-gradient(top, #fff 0%, #e9e9e9 100%);background-image:-moz-linear-gradient(top, #fff 0%, #e9e9e9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #e9e9e9 100%);background-image:-o-linear-gradient(top, #fff 0%, #e9e9e9 100%);background-image:linear-gradient(to bottom, #fff 0%, #e9e9e9 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='white', EndColorStr='#e9e9e9');-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;outline:none}button.dt-button.disabled,div.dt-button.disabled,a.dt-button.disabled{color:#999;border:1px solid #d0d0d0;cursor:default;background-color:#f9f9f9;background-image:-webkit-linear-gradient(top, #fff 0%, #f9f9f9 100%);background-image:-moz-linear-gradient(top, #fff 0%, #f9f9f9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #f9f9f9 100%);background-image:-o-linear-gradient(top, #fff 0%, #f9f9f9 100%);background-image:linear-gradient(to bottom, #fff 0%, #f9f9f9 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#ffffff', EndColorStr='#f9f9f9')}button.dt-button:active:not(.disabled),button.dt-button.active:not(.disabled),div.dt-button:active:not(.disabled),div.dt-button.active:not(.disabled),a.dt-button:active:not(.disabled),a.dt-button.active:not(.disabled){background-color:#e2e2e2;background-image:-webkit-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%);background-image:-moz-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%);background-image:-ms-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%);background-image:-o-linear-gradient(top, #f3f3f3 0%, #e2e2e2 100%);background-image:linear-gradient(to bottom, #f3f3f3 0%, #e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f3f3f3', EndColorStr='#e2e2e2');box-shadow:inset 1px 1px 3px #999999}button.dt-button:active:not(.disabled):hover:not(.disabled),button.dt-button.active:not(.disabled):hover:not(.disabled),div.dt-button:active:not(.disabled):hover:not(.disabled),div.dt-button.active:not(.disabled):hover:not(.disabled),a.dt-button:active:not(.disabled):hover:not(.disabled),a.dt-button.active:not(.disabled):hover:not(.disabled){box-shadow:inset 1px 1px 3px #999999;background-color:#cccccc;background-image:-webkit-linear-gradient(top, #eaeaea 0%, #ccc 100%);background-image:-moz-linear-gradient(top, #eaeaea 0%, #ccc 100%);background-image:-ms-linear-gradient(top, #eaeaea 0%, #ccc 100%);background-image:-o-linear-gradient(top, #eaeaea 0%, #ccc 100%);background-image:linear-gradient(to bottom, #eaeaea 0%, #ccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#eaeaea', EndColorStr='#cccccc')}button.dt-button:hover,div.dt-button:hover,a.dt-button:hover{text-decoration:none}button.dt-button:hover:not(.disabled),div.dt-button:hover:not(.disabled),a.dt-button:hover:not(.disabled){border:1px solid #666;background-color:#e0e0e0;background-image:-webkit-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%);background-image:-moz-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%);background-image:-ms-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%);background-image:-o-linear-gradient(top, #f9f9f9 0%, #e0e0e0 100%);background-image:linear-gradient(to bottom, #f9f9f9 0%, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f9f9f9', EndColorStr='#e0e0e0')}button.dt-button:focus:not(.disabled),div.dt-button:focus:not(.disabled),a.dt-button:focus:not(.disabled){border:1px solid #426c9e;text-shadow:0 1px 0 #c4def1;outline:none;background-color:#79ace9;background-image:-webkit-linear-gradient(top, #bddef4 0%, #79ace9 100%);background-image:-moz-linear-gradient(top, #bddef4 0%, #79ace9 100%);background-image:-ms-linear-gradient(top, #bddef4 0%, #79ace9 100%);background-image:-o-linear-gradient(top, #bddef4 0%, #79ace9 100%);background-image:linear-gradient(to bottom, #bddef4 0%, #79ace9 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#bddef4', EndColorStr='#79ace9')}.dt-button embed{outline:none}div.dt-buttons{position:relative;float:left}div.dt-buttons.buttons-right{float:right}div.dt-button-collection{position:absolute;top:0;left:0;width:150px;margin-top:3px;padding:8px 8px 4px 8px;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.4);background-color:white;overflow:hidden;z-index:2002;border-radius:5px;box-shadow:3px 3px 5px rgba(0,0,0,0.3);z-index:2002;-webkit-column-gap:8px;-moz-column-gap:8px;-ms-column-gap:8px;-o-column-gap:8px;column-gap:8px}div.dt-button-collection button.dt-button,div.dt-button-collection div.dt-button,div.dt-button-collection a.dt-button{position:relative;left:0;right:0;display:block;float:none;margin-bottom:4px;margin-right:0}div.dt-button-collection button.dt-button:active:not(.disabled),div.dt-button-collection button.dt-button.active:not(.disabled),div.dt-button-collection div.dt-button:active:not(.disabled),div.dt-button-collection div.dt-button.active:not(.disabled),div.dt-button-collection a.dt-button:active:not(.disabled),div.dt-button-collection a.dt-button.active:not(.disabled){background-color:#dadada;background-image:-webkit-linear-gradient(top, #f0f0f0 0%, #dadada 100%);background-image:-moz-linear-gradient(top, #f0f0f0 0%, #dadada 100%);background-image:-ms-linear-gradient(top, #f0f0f0 0%, #dadada 100%);background-image:-o-linear-gradient(top, #f0f0f0 0%, #dadada 100%);background-image:linear-gradient(to bottom, #f0f0f0 0%, #dadada 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f0f0f0', EndColorStr='#dadada');box-shadow:inset 1px 1px 3px #666}div.dt-button-collection.fixed{position:fixed;top:50%;left:50%;margin-left:-75px;border-radius:0}div.dt-button-collection.fixed.two-column{margin-left:-150px}div.dt-button-collection.fixed.three-column{margin-left:-225px}div.dt-button-collection.fixed.four-column{margin-left:-300px}div.dt-button-collection>*{-webkit-column-break-inside:avoid;break-inside:avoid}div.dt-button-collection.two-column{width:300px;padding-bottom:1px;-webkit-column-count:2;-moz-column-count:2;-ms-column-count:2;-o-column-count:2;column-count:2}div.dt-button-collection.three-column{width:450px;padding-bottom:1px;-webkit-column-count:3;-moz-column-count:3;-ms-column-count:3;-o-column-count:3;column-count:3}div.dt-button-collection.four-column{width:600px;padding-bottom:1px;-webkit-column-count:4;-moz-column-count:4;-ms-column-count:4;-o-column-count:4;column-count:4}div.dt-button-background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:2001}@media screen and (max-width: 640px){div.dt-buttons{float:none !important;text-align:center}} diff --git a/css/csstables/dataTables.editor.css b/css/csstables/dataTables.editor.css deleted file mode 100644 index e69de29..0000000 diff --git a/css/csstables/editor.bootstrap.css b/css/csstables/editor.bootstrap.css deleted file mode 100644 index 433049b..0000000 --- a/css/csstables/editor.bootstrap.css +++ /dev/null @@ -1,689 +0,0 @@ -div.DTE div.DTE_Form_Error { - color: #b11f1f; -} - -div.modal div.DTE div.DTE_Form_Error { - display: none; - float: left; - padding-top: 7px; -} - -div.DTE_Field div.multi-value, -div.DTE_Field div.multi-restore { - display: none; - cursor: pointer; -} -div.DTE_Field div.multi-value span, -div.DTE_Field div.multi-restore span { - display: block; - color: #666; -} -div.DTE_Field div.multi-value:hover, -div.DTE_Field div.multi-restore:hover { - background-color: #f1f1f1; -} -div.DTE_Field div.multi-restore { - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; -} -div.DTE_Field:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; -} - -div.DTE_Inline { - position: relative; - display: table; - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field, -div.DTE_Inline div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field { - padding: 0; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field > label, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field > label { - display: none; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input { - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; -} -div.DTE_Inline div.DTE_Field input[type="color"], -div.DTE_Inline div.DTE_Field input[type="date"], -div.DTE_Inline div.DTE_Field input[type="datetime"], -div.DTE_Inline div.DTE_Field input[type="datetime-local"], -div.DTE_Inline div.DTE_Field input[type="email"], -div.DTE_Inline div.DTE_Field input[type="month"], -div.DTE_Inline div.DTE_Field input[type="number"], -div.DTE_Inline div.DTE_Field input[type="password"], -div.DTE_Inline div.DTE_Field input[type="search"], -div.DTE_Inline div.DTE_Field input[type="tel"], -div.DTE_Inline div.DTE_Field input[type="text"], -div.DTE_Inline div.DTE_Field input[type="time"], -div.DTE_Inline div.DTE_Field input[type="url"], -div.DTE_Inline div.DTE_Field input[type="week"] { - margin: -6px 0; -} -div.DTE_Inline div.DTE_Field_Error, -div.DTE_Inline div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; -} -div.DTE_Inline div.DTE_Field_Error:empty, -div.DTE_Inline div.DTE_Form_Error:empty { - margin-top: 0; -} - -span.dtr-data div.DTE_Inline { - display: inline-table; -} - -div.DTE_Inline div.DTE_Field { - width: 100%; -} -div.DTE_Inline div.DTE_Field > div { - width: 100%; - padding: 0; -} -div.DTE_Inline div.DTE_Field input.form-control { - height: 30px; -} -div.DTE_Inline div.DTE_Field div.help-block { - display: none; - margin-top: 10px; - margin-bottom: 0; -} -div.DTE_Inline.DTE_Processing:after { - top: 5px; -} - -div.DTE_Field_Type_checkbox div.controls, -div.DTE_Field_Type_radio div.controls { - margin-top: 0.4em; -} -div.DTE_Field_Type_checkbox div.controls label, -div.DTE_Field_Type_radio div.controls label { - margin-left: 0.75em; - margin-bottom: 0; - vertical-align: middle; - font-weight: normal; -} - -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table { - display: table; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form { - display: table-cell; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { - padding: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field:last-child { - margin-bottom: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Form_Info, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); -} -div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url("../images/ajax-loader-small.gif") no-repeat top left; -} -div.DTE_Bubble.below div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; -} -div.DTE_Bubble.below div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTE_Bubble_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTE_Bubble_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -div.DTE_Bubble div.DTE_Bubble_Liner { - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - border-radius: 6px; - padding: 0 0 0.5em 0; - border: 1px solid rgba(0, 0, 0, 0.2); -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field > div { - width: 100%; - float: none; - clear: both; - text-align: left; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label { - padding-bottom: 4px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - font-size: 14px; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - margin-top: -2px; - display: block; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - border-left: 1px solid rgba(0, 0, 0, 0.2); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 10; - background-color: rgba(0, 0, 0, 0.05); -} - -div.DTE div.editor_upload { - padding-top: 4px; -} -div.DTE div.editor_upload div.eu_table { - display: table; - width: 100%; -} -div.DTE div.editor_upload div.row { - display: table-row; -} -div.DTE div.editor_upload div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; -} -div.DTE div.editor_upload div.cell + div.cell { - padding-left: 10px; -} -div.DTE div.editor_upload div.row + div.row div.cell { - padding-top: 10px; -} -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; -} -div.DTE div.editor_upload input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; -} -div.DTE div.editor_upload div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; -} -div.DTE div.editor_upload div.drop.over { - border: 3px dashed #111; - color: #111; -} -div.DTE div.editor_upload div.drop span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; -} -div.DTE div.editor_upload div.rendered img { - max-width: 8em; - margin: 0 auto; -} -div.DTE div.editor_upload.noDrop div.drop { - display: none; -} -div.DTE div.editor_upload.noDrop div.row.second { - display: none; -} -div.DTE div.editor_upload.noDrop div.rendered { - margin-top: 10px; -} -div.DTE div.editor_upload.noClear div.clearValue button { - display: none; -} -div.DTE div.editor_upload.multi div.cell { - display: block; - width: 100%; -} -div.DTE div.editor_upload.multi div.cell div.drop { - min-height: 0; - padding-bottom: 5px; -} -div.DTE div.editor_upload.multi div.clearValue { - display: none; -} -div.DTE div.editor_upload.multi ul { - list-style-type: none; - margin: 0; - padding: 0; -} -div.DTE div.editor_upload.multi ul li { - position: relative; - margin-top: 0.5em; -} -div.DTE div.editor_upload.multi ul li:first-child { - margin-top: 0; -} -div.DTE div.editor_upload.multi ul li img { - vertical-align: middle; -} -div.DTE div.editor_upload.multi ul li button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; -} - -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - height: auto; -} -div.DTE div.editor_upload ul li button { - padding-bottom: 8px; -} - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5); - padding-bottom: 5px; -} -div.editor-datetime div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; -} -div.editor-datetime table { - border-spacing: 0; - margin: 6px 13px; -} -div.editor-datetime table th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; -} -div.editor-datetime table td { - font-size: 0.9em; - color: #444; - padding: 0; -} -div.editor-datetime table td.day { - text-align: right; - background: #f5f5f5; -} -div.editor-datetime table td.day.disabled { - color: #aaa; - background: white; -} -div.editor-datetime table td.day.today { - background-color: #ddd; -} -div.editor-datetime table td.day.today button { - font-weight: bold; -} -div.editor-datetime table td.day.selected button { - background: #337ab7; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.day button:hover { - background: #ff8000; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.editor-datetime-week { - font-size: 0.7em; -} -div.editor-datetime table button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; -} -div.editor-datetime table.weekNumber th { - width: 12.5%; -} -div.editor-datetime div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-label:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; -} -div.editor-datetime div.editor-datetime-label select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; -} -div.editor-datetime div.editor-datetime-time { - text-align: center; -} -div.editor-datetime div.editor-datetime-time > span { - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-iconLeft, -div.editor-datetime div.editor-datetime-iconRight, -div.editor-datetime div.editor-datetime-iconUp, -div.editor-datetime div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; -} -div.editor-datetime div.editor-datetime-iconLeft:hover, -div.editor-datetime div.editor-datetime-iconRight:hover, -div.editor-datetime div.editor-datetime-iconUp:hover, -div.editor-datetime div.editor-datetime-iconDown:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; -} -div.editor-datetime div.editor-datetime-iconLeft button, -div.editor-datetime div.editor-datetime-iconRight button, -div.editor-datetime div.editor-datetime-iconUp button, -div.editor-datetime div.editor-datetime-iconDown button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=="); -} -div.editor-datetime div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconUp { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconDown { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC"); -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} - -div.DTE div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -div.DTE.processing div.DTE_Processing_Indicator { - display: block; -} -div.DTE.DTE_Inline div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; -} -div.DTE.DTE_Bubble div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; -} -div.DTE div.DTE_Processing_Indicator span, -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.1); - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; -} -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; -} -div.DTE div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -div.DTE div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; -} -div.DTE div.DTE_Processing_Indicator:after { - left: 1em; -} -@-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -@keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -div.DTE div.DTE_Processing_Indicator { - top: 52px; - right: 12px; -} diff --git a/css/csstables/editor.bootstrap.min.css b/css/csstables/editor.bootstrap.min.css deleted file mode 100644 index c8d65cd..0000000 --- a/css/csstables/editor.bootstrap.min.css +++ /dev/null @@ -1,5 +0,0 @@ -div.DTE div.DTE_Form_Error{color:#b11f1f}div.modal div.DTE div.DTE_Form_Error{display:none;float:left;padding-top:7px}div.DTE_Field div.multi-value,div.DTE_Field div.multi-restore{display:none;cursor:pointer}div.DTE_Field div.multi-value span,div.DTE_Field div.multi-restore span{display:block;color:#666}div.DTE_Field div.multi-value:hover,div.DTE_Field div.multi-restore:hover{background-color:#f1f1f1}div.DTE_Field div.multi-restore{margin-top:0.5em;font-size:0.8em;line-height:1.25em}div.DTE_Field:after{display:block;content:".";height:0;line-height:0;clear:both;visibility:hidden}div.DTE_Inline{position:relative;display:table;width:100%}div.DTE_Inline div.DTE_Inline_Field,div.DTE_Inline div.DTE_Inline_Buttons{display:table-cell;vertical-align:middle}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field{padding:0}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field>label,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field>label{display:none}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input{width:100%}div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button{margin:-6px 0 -6px 4px;padding:5px}div.DTE_Inline div.DTE_Field input[type="color"],div.DTE_Inline div.DTE_Field input[type="date"],div.DTE_Inline div.DTE_Field input[type="datetime"],div.DTE_Inline div.DTE_Field input[type="datetime-local"],div.DTE_Inline div.DTE_Field input[type="email"],div.DTE_Inline div.DTE_Field input[type="month"],div.DTE_Inline div.DTE_Field input[type="number"],div.DTE_Inline div.DTE_Field input[type="password"],div.DTE_Inline div.DTE_Field input[type="search"],div.DTE_Inline div.DTE_Field input[type="tel"],div.DTE_Inline div.DTE_Field input[type="text"],div.DTE_Inline div.DTE_Field input[type="time"],div.DTE_Inline div.DTE_Field input[type="url"],div.DTE_Inline div.DTE_Field input[type="week"]{margin:-6px 0}div.DTE_Inline div.DTE_Field_Error,div.DTE_Inline div.DTE_Form_Error{font-size:11px;line-height:1.2em;padding:0;margin-top:10px}div.DTE_Inline div.DTE_Field_Error:empty,div.DTE_Inline div.DTE_Form_Error:empty{margin-top:0}span.dtr-data div.DTE_Inline{display:inline-table}div.DTE_Inline div.DTE_Field{width:100%}div.DTE_Inline div.DTE_Field>div{width:100%;padding:0}div.DTE_Inline div.DTE_Field input.form-control{height:30px}div.DTE_Inline div.DTE_Field div.help-block{display:none;margin-top:10px;margin-bottom:0}div.DTE_Inline.DTE_Processing:after{top:5px}div.DTE_Field_Type_checkbox div.controls,div.DTE_Field_Type_radio div.controls{margin-top:0.4em}div.DTE_Field_Type_checkbox div.controls label,div.DTE_Field_Type_radio div.controls label{margin-left:0.75em;margin-bottom:0;vertical-align:middle;font-weight:normal}div.DTE_Bubble{position:absolute;z-index:11;margin-top:-6px;opacity:0}div.DTE_Bubble div.DTE_Bubble_Liner{position:absolute;bottom:0;border:1px solid black;width:300px;margin-left:-150px;background-color:white;box-shadow:2px 2px 7px #555;border-radius:5px;border:2px solid #444;padding:1em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table{display:table;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form{display:table-cell}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content{padding:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{position:relative;zoom:1;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field:last-child{margin-bottom:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons{display:table-cell;vertical-align:bottom;padding:0 0 0 0.75em;width:1%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{border-top-left-radius:5px;border-top-right-radius:5px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Form_Info,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Bubble_Table{padding-top:42px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error{float:none;display:none;padding:0;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTE_Bubble div.DTE_Bubble_Triangle{position:absolute;height:10px;width:10px;top:-6px;background-color:white;border:2px solid #444;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after{position:absolute;content:' ';display:block;top:12px;right:18px;height:12px;width:17px;background:url("../images/ajax-loader-small.gif") no-repeat top left}div.DTE_Bubble.below div.DTE_Bubble_Liner{top:10px;bottom:auto}div.DTE_Bubble.below div.DTE_Bubble_Triangle{top:4px;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTE_Bubble_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTE_Bubble_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}div.DTE_Bubble div.DTE_Bubble_Liner{box-shadow:0 5px 10px rgba(0,0,0,0.2);border-radius:6px;padding:0 0 0.5em 0;border:1px solid rgba(0,0,0,0.2)}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field>div{width:100%;float:none;clear:both;text-align:left}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label{padding-bottom:4px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{background-color:#f7f7f7;border-bottom:1px solid #ebebeb;font-size:14px;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{margin-top:-2px;display:block}div.DTE_Bubble div.DTE_Bubble_Triangle{border-bottom:1px solid rgba(0,0,0,0.2);border-left:1px solid rgba(0,0,0,0.2)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:10;background-color:rgba(0,0,0,0.05)}div.DTE div.editor_upload{padding-top:4px}div.DTE div.editor_upload div.eu_table{display:table;width:100%}div.DTE div.editor_upload div.row{display:table-row}div.DTE div.editor_upload div.cell{display:table-cell;position:relative;width:50%;vertical-align:top}div.DTE div.editor_upload div.cell+div.cell{padding-left:10px}div.DTE div.editor_upload div.row+div.row div.cell{padding-top:10px}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{width:100%;height:2.3em;font-size:0.8em;text-align:center;line-height:1em}div.DTE div.editor_upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0}div.DTE div.editor_upload div.drop{position:relative;box-sizing:border-box;width:100%;height:100%;border:3px dashed #ccc;border-radius:6px;min-height:4em;color:#999;padding-top:3px;text-align:center}div.DTE div.editor_upload div.drop.over{border:3px dashed #111;color:#111}div.DTE div.editor_upload div.drop span{max-width:75%;font-size:0.85em;line-height:1em}div.DTE div.editor_upload div.rendered img{max-width:8em;margin:0 auto}div.DTE div.editor_upload.noDrop div.drop{display:none}div.DTE div.editor_upload.noDrop div.row.second{display:none}div.DTE div.editor_upload.noDrop div.rendered{margin-top:10px}div.DTE div.editor_upload.noClear div.clearValue button{display:none}div.DTE div.editor_upload.multi div.cell{display:block;width:100%}div.DTE div.editor_upload.multi div.cell div.drop{min-height:0;padding-bottom:5px}div.DTE div.editor_upload.multi div.clearValue{display:none}div.DTE div.editor_upload.multi ul{list-style-type:none;margin:0;padding:0}div.DTE div.editor_upload.multi ul li{position:relative;margin-top:0.5em}div.DTE div.editor_upload.multi ul li:first-child{margin-top:0}div.DTE div.editor_upload.multi ul li img{vertical-align:middle}div.DTE div.editor_upload.multi ul li button{position:absolute;width:40px;right:0;top:50%;margin-top:-1.5em}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{height:auto}div.DTE div.editor_upload ul li button{padding-bottom:8px}div.editor-datetime{position:absolute;background-color:white;z-index:2050;border:1px solid #ccc;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5);padding-bottom:5px}div.editor-datetime div.editor-datetime-title{text-align:center;padding:5px 0px 3px}div.editor-datetime table{border-spacing:0;margin:6px 13px}div.editor-datetime table th{font-size:0.8em;color:#777;font-weight:normal;width:14.285714286%;padding:0 0 4px 0;text-align:center}div.editor-datetime table td{font-size:0.9em;color:#444;padding:0}div.editor-datetime table td.day{text-align:right;background:#f5f5f5}div.editor-datetime table td.day.disabled{color:#aaa;background:white}div.editor-datetime table td.day.today{background-color:#ddd}div.editor-datetime table td.day.today button{font-weight:bold}div.editor-datetime table td.day.selected button{background:#337ab7;color:white;border-radius:2px}div.editor-datetime table td.day button:hover{background:#ff8000;color:white;border-radius:2px}div.editor-datetime table td.editor-datetime-week{font-size:0.7em}div.editor-datetime table button{width:100%;box-sizing:border-box;border:none;background:transparent;font-size:inherit;color:inherit;text-align:inherit;padding:5px 9px;cursor:pointer;margin:0}div.editor-datetime table.weekNumber th{width:12.5%}div.editor-datetime div.editor-datetime-label{position:relative;display:inline-block;height:30px;padding:5px 6px;border:1px solid transparent;box-sizing:border-box;cursor:pointer}div.editor-datetime div.editor-datetime-label:hover{border:1px solid #ddd;border-radius:2px;background-color:#f5f5f5}div.editor-datetime div.editor-datetime-label select{position:absolute;top:6px;left:0;cursor:pointer;opacity:0;-ms-filter:"alpha(opacity=0)"}div.editor-datetime div.editor-datetime-time{text-align:center}div.editor-datetime div.editor-datetime-time>span{vertical-align:middle}div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock{display:inline-block;vertical-align:middle}div.editor-datetime div.editor-datetime-iconLeft,div.editor-datetime div.editor-datetime-iconRight,div.editor-datetime div.editor-datetime-iconUp,div.editor-datetime div.editor-datetime-iconDown{width:30px;height:30px;background-position:center;background-repeat:no-repeat;opacity:0.3;overflow:hidden;box-sizing:border-box}div.editor-datetime div.editor-datetime-iconLeft:hover,div.editor-datetime div.editor-datetime-iconRight:hover,div.editor-datetime div.editor-datetime-iconUp:hover,div.editor-datetime div.editor-datetime-iconDown:hover{border:1px solid #ccc;border-radius:2px;background-color:#f0f0f0;opacity:0.6}div.editor-datetime div.editor-datetime-iconLeft button,div.editor-datetime div.editor-datetime-iconRight button,div.editor-datetime div.editor-datetime-iconUp button,div.editor-datetime div.editor-datetime-iconDown button{border:none;background:transparent;text-indent:30px;height:100%;width:100%;cursor:pointer}div.editor-datetime div.editor-datetime-iconLeft{position:absolute;top:5px;left:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}div.editor-datetime div.editor-datetime-iconRight{position:absolute;top:5px;right:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconUp{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconDown{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC")}div.editor-datetime-error{padding:0 1em;max-width:240px;font-size:11px;line-height:1.25em;text-align:center;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{position:absolute;top:17px;right:9px;height:2em;width:2em;z-index:20;font-size:12px;display:none;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}div.DTE.processing div.DTE_Processing_Indicator{display:block}div.DTE.DTE_Inline div.DTE_Processing_Indicator{top:5px;right:6px;font-size:6px}div.DTE.DTE_Bubble div.DTE_Processing_Indicator{top:10px;right:14px;font-size:8px}div.DTE div.DTE_Processing_Indicator span,div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{display:block;background:black;width:0.5em;height:1.5em;border:1px solid rgba(0,0,0,0.4);background-color:rgba(0,0,0,0.1);-webkit-animation:editorProcessing 0.9s infinite ease-in-out;animation:editorProcessing 0.9s infinite ease-in-out}div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{position:absolute;top:0;content:''}div.DTE div.DTE_Processing_Indicator:before{left:-1em;-webkit-animation-delay:-0.3s;animation-delay:-0.3s}div.DTE div.DTE_Processing_Indicator span{-webkit-animation-delay:-0.15s;animation-delay:-0.15s}div.DTE div.DTE_Processing_Indicator:after{left:1em}@-webkit-keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}@keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}div.DTE div.DTE_Processing_Indicator{top:52px;right:12px} diff --git a/css/csstables/editor.bootstrap4.css b/css/csstables/editor.bootstrap4.css deleted file mode 100644 index 8f2609c..0000000 --- a/css/csstables/editor.bootstrap4.css +++ /dev/null @@ -1,689 +0,0 @@ -div.DTE div.DTE_Form_Error { - color: #b11f1f; -} - -div.modal div.DTE div.DTE_Form_Error { - display: none; - float: left; - padding-top: 7px; -} - -div.DTE_Field div.multi-value, -div.DTE_Field div.multi-restore { - display: none; - cursor: pointer; -} -div.DTE_Field div.multi-value span, -div.DTE_Field div.multi-restore span { - display: block; - color: #666; -} -div.DTE_Field div.multi-value:hover, -div.DTE_Field div.multi-restore:hover { - background-color: #f1f1f1; -} -div.DTE_Field div.multi-restore { - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; -} -div.DTE_Field:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; -} - -div.DTE_Inline { - position: relative; - display: table; - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field, -div.DTE_Inline div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field { - padding: 0; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field > label, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field > label { - display: none; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input { - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; -} -div.DTE_Inline div.DTE_Field input[type="color"], -div.DTE_Inline div.DTE_Field input[type="date"], -div.DTE_Inline div.DTE_Field input[type="datetime"], -div.DTE_Inline div.DTE_Field input[type="datetime-local"], -div.DTE_Inline div.DTE_Field input[type="email"], -div.DTE_Inline div.DTE_Field input[type="month"], -div.DTE_Inline div.DTE_Field input[type="number"], -div.DTE_Inline div.DTE_Field input[type="password"], -div.DTE_Inline div.DTE_Field input[type="search"], -div.DTE_Inline div.DTE_Field input[type="tel"], -div.DTE_Inline div.DTE_Field input[type="text"], -div.DTE_Inline div.DTE_Field input[type="time"], -div.DTE_Inline div.DTE_Field input[type="url"], -div.DTE_Inline div.DTE_Field input[type="week"] { - margin: -6px 0; -} -div.DTE_Inline div.DTE_Field_Error, -div.DTE_Inline div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; -} -div.DTE_Inline div.DTE_Field_Error:empty, -div.DTE_Inline div.DTE_Form_Error:empty { - margin-top: 0; -} - -span.dtr-data div.DTE_Inline { - display: inline-table; -} - -div.DTE_Inline div.DTE_Field { - width: 100%; -} -div.DTE_Inline div.DTE_Field > div { - width: 100%; - padding: 0; -} -div.DTE_Inline div.DTE_Field input.form-control { - height: 30px; -} -div.DTE_Inline div.DTE_Field div.help-block { - display: none; - margin-top: 10px; - margin-bottom: 0; -} -div.DTE_Inline.DTE_Processing:after { - top: 5px; -} - -div.DTE_Field_Type_checkbox div.controls, -div.DTE_Field_Type_radio div.controls { - margin-top: 0.4em; -} -div.DTE_Field_Type_checkbox div.controls label, -div.DTE_Field_Type_radio div.controls label { - margin-left: 0.75em; - margin-bottom: 0; - vertical-align: middle; - font-weight: normal; -} - -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table { - display: table; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form { - display: table-cell; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { - padding: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field:last-child { - margin-bottom: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Form_Info, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); -} -div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url("../images/ajax-loader-small.gif") no-repeat top left; -} -div.DTE_Bubble.below div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; -} -div.DTE_Bubble.below div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTE_Bubble_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTE_Bubble_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -div.DTE_Bubble div.DTE_Bubble_Liner { - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - border-radius: 6px; - padding: 0 0 0.5em 0; - border: 1px solid rgba(0, 0, 0, 0.2); -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field > div { - width: 100%; - float: none; - clear: both; - text-align: left; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label { - padding-bottom: 4px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - font-size: 14px; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - margin-top: -2px; - display: block; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - border-left: 1px solid rgba(0, 0, 0, 0.2); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 10; - background-color: rgba(0, 0, 0, 0.05); -} - -div.DTE div.editor_upload { - padding-top: 4px; -} -div.DTE div.editor_upload div.eu_table { - display: table; - width: 100%; -} -div.DTE div.editor_upload div.row { - display: table-row; -} -div.DTE div.editor_upload div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; -} -div.DTE div.editor_upload div.cell + div.cell { - padding-left: 10px; -} -div.DTE div.editor_upload div.row + div.row div.cell { - padding-top: 10px; -} -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; -} -div.DTE div.editor_upload input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; -} -div.DTE div.editor_upload div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; -} -div.DTE div.editor_upload div.drop.over { - border: 3px dashed #111; - color: #111; -} -div.DTE div.editor_upload div.drop span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; -} -div.DTE div.editor_upload div.rendered img { - max-width: 8em; - margin: 0 auto; -} -div.DTE div.editor_upload.noDrop div.drop { - display: none; -} -div.DTE div.editor_upload.noDrop div.row.second { - display: none; -} -div.DTE div.editor_upload.noDrop div.rendered { - margin-top: 10px; -} -div.DTE div.editor_upload.noClear div.clearValue button { - display: none; -} -div.DTE div.editor_upload.multi div.cell { - display: block; - width: 100%; -} -div.DTE div.editor_upload.multi div.cell div.drop { - min-height: 0; - padding-bottom: 5px; -} -div.DTE div.editor_upload.multi div.clearValue { - display: none; -} -div.DTE div.editor_upload.multi ul { - list-style-type: none; - margin: 0; - padding: 0; -} -div.DTE div.editor_upload.multi ul li { - position: relative; - margin-top: 0.5em; -} -div.DTE div.editor_upload.multi ul li:first-child { - margin-top: 0; -} -div.DTE div.editor_upload.multi ul li img { - vertical-align: middle; -} -div.DTE div.editor_upload.multi ul li button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; -} - -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - height: auto; -} -div.DTE div.editor_upload ul li button { - padding-bottom: 8px; -} - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5); - padding-bottom: 5px; -} -div.editor-datetime div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; -} -div.editor-datetime table { - border-spacing: 0; - margin: 6px 13px; -} -div.editor-datetime table th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; -} -div.editor-datetime table td { - font-size: 0.9em; - color: #444; - padding: 0; -} -div.editor-datetime table td.day { - text-align: right; - background: #f5f5f5; -} -div.editor-datetime table td.day.disabled { - color: #aaa; - background: white; -} -div.editor-datetime table td.day.today { - background-color: #ddd; -} -div.editor-datetime table td.day.today button { - font-weight: bold; -} -div.editor-datetime table td.day.selected button { - background: #0275d8; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.day button:hover { - background: #ff8000; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.editor-datetime-week { - font-size: 0.7em; -} -div.editor-datetime table button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; -} -div.editor-datetime table.weekNumber th { - width: 12.5%; -} -div.editor-datetime div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-label:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; -} -div.editor-datetime div.editor-datetime-label select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; -} -div.editor-datetime div.editor-datetime-time { - text-align: center; -} -div.editor-datetime div.editor-datetime-time > span { - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-iconLeft, -div.editor-datetime div.editor-datetime-iconRight, -div.editor-datetime div.editor-datetime-iconUp, -div.editor-datetime div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; -} -div.editor-datetime div.editor-datetime-iconLeft:hover, -div.editor-datetime div.editor-datetime-iconRight:hover, -div.editor-datetime div.editor-datetime-iconUp:hover, -div.editor-datetime div.editor-datetime-iconDown:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; -} -div.editor-datetime div.editor-datetime-iconLeft button, -div.editor-datetime div.editor-datetime-iconRight button, -div.editor-datetime div.editor-datetime-iconUp button, -div.editor-datetime div.editor-datetime-iconDown button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=="); -} -div.editor-datetime div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconUp { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconDown { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC"); -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} - -div.DTE div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -div.DTE.processing div.DTE_Processing_Indicator { - display: block; -} -div.DTE.DTE_Inline div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; -} -div.DTE.DTE_Bubble div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; -} -div.DTE div.DTE_Processing_Indicator span, -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.1); - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; -} -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; -} -div.DTE div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -div.DTE div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; -} -div.DTE div.DTE_Processing_Indicator:after { - left: 1em; -} -@-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -@keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -div.DTE div.DTE_Processing_Indicator { - top: 52px; - right: 12px; -} diff --git a/css/csstables/editor.bootstrap4.min.css b/css/csstables/editor.bootstrap4.min.css deleted file mode 100644 index 398cf33..0000000 --- a/css/csstables/editor.bootstrap4.min.css +++ /dev/null @@ -1,5 +0,0 @@ -div.DTE div.DTE_Form_Error{color:#b11f1f}div.modal div.DTE div.DTE_Form_Error{display:none;float:left;padding-top:7px}div.DTE_Field div.multi-value,div.DTE_Field div.multi-restore{display:none;cursor:pointer}div.DTE_Field div.multi-value span,div.DTE_Field div.multi-restore span{display:block;color:#666}div.DTE_Field div.multi-value:hover,div.DTE_Field div.multi-restore:hover{background-color:#f1f1f1}div.DTE_Field div.multi-restore{margin-top:0.5em;font-size:0.8em;line-height:1.25em}div.DTE_Field:after{display:block;content:".";height:0;line-height:0;clear:both;visibility:hidden}div.DTE_Inline{position:relative;display:table;width:100%}div.DTE_Inline div.DTE_Inline_Field,div.DTE_Inline div.DTE_Inline_Buttons{display:table-cell;vertical-align:middle}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field{padding:0}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field>label,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field>label{display:none}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input{width:100%}div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button{margin:-6px 0 -6px 4px;padding:5px}div.DTE_Inline div.DTE_Field input[type="color"],div.DTE_Inline div.DTE_Field input[type="date"],div.DTE_Inline div.DTE_Field input[type="datetime"],div.DTE_Inline div.DTE_Field input[type="datetime-local"],div.DTE_Inline div.DTE_Field input[type="email"],div.DTE_Inline div.DTE_Field input[type="month"],div.DTE_Inline div.DTE_Field input[type="number"],div.DTE_Inline div.DTE_Field input[type="password"],div.DTE_Inline div.DTE_Field input[type="search"],div.DTE_Inline div.DTE_Field input[type="tel"],div.DTE_Inline div.DTE_Field input[type="text"],div.DTE_Inline div.DTE_Field input[type="time"],div.DTE_Inline div.DTE_Field input[type="url"],div.DTE_Inline div.DTE_Field input[type="week"]{margin:-6px 0}div.DTE_Inline div.DTE_Field_Error,div.DTE_Inline div.DTE_Form_Error{font-size:11px;line-height:1.2em;padding:0;margin-top:10px}div.DTE_Inline div.DTE_Field_Error:empty,div.DTE_Inline div.DTE_Form_Error:empty{margin-top:0}span.dtr-data div.DTE_Inline{display:inline-table}div.DTE_Inline div.DTE_Field{width:100%}div.DTE_Inline div.DTE_Field>div{width:100%;padding:0}div.DTE_Inline div.DTE_Field input.form-control{height:30px}div.DTE_Inline div.DTE_Field div.help-block{display:none;margin-top:10px;margin-bottom:0}div.DTE_Inline.DTE_Processing:after{top:5px}div.DTE_Field_Type_checkbox div.controls,div.DTE_Field_Type_radio div.controls{margin-top:0.4em}div.DTE_Field_Type_checkbox div.controls label,div.DTE_Field_Type_radio div.controls label{margin-left:0.75em;margin-bottom:0;vertical-align:middle;font-weight:normal}div.DTE_Bubble{position:absolute;z-index:11;margin-top:-6px;opacity:0}div.DTE_Bubble div.DTE_Bubble_Liner{position:absolute;bottom:0;border:1px solid black;width:300px;margin-left:-150px;background-color:white;box-shadow:2px 2px 7px #555;border-radius:5px;border:2px solid #444;padding:1em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table{display:table;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form{display:table-cell}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content{padding:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{position:relative;zoom:1;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field:last-child{margin-bottom:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons{display:table-cell;vertical-align:bottom;padding:0 0 0 0.75em;width:1%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{border-top-left-radius:5px;border-top-right-radius:5px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Form_Info,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Bubble_Table{padding-top:42px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error{float:none;display:none;padding:0;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTE_Bubble div.DTE_Bubble_Triangle{position:absolute;height:10px;width:10px;top:-6px;background-color:white;border:2px solid #444;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after{position:absolute;content:' ';display:block;top:12px;right:18px;height:12px;width:17px;background:url("../images/ajax-loader-small.gif") no-repeat top left}div.DTE_Bubble.below div.DTE_Bubble_Liner{top:10px;bottom:auto}div.DTE_Bubble.below div.DTE_Bubble_Triangle{top:4px;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTE_Bubble_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTE_Bubble_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}div.DTE_Bubble div.DTE_Bubble_Liner{box-shadow:0 5px 10px rgba(0,0,0,0.2);border-radius:6px;padding:0 0 0.5em 0;border:1px solid rgba(0,0,0,0.2)}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field>div{width:100%;float:none;clear:both;text-align:left}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label{padding-bottom:4px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{background-color:#f7f7f7;border-bottom:1px solid #ebebeb;font-size:14px;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{margin-top:-2px;display:block}div.DTE_Bubble div.DTE_Bubble_Triangle{border-bottom:1px solid rgba(0,0,0,0.2);border-left:1px solid rgba(0,0,0,0.2)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:10;background-color:rgba(0,0,0,0.05)}div.DTE div.editor_upload{padding-top:4px}div.DTE div.editor_upload div.eu_table{display:table;width:100%}div.DTE div.editor_upload div.row{display:table-row}div.DTE div.editor_upload div.cell{display:table-cell;position:relative;width:50%;vertical-align:top}div.DTE div.editor_upload div.cell+div.cell{padding-left:10px}div.DTE div.editor_upload div.row+div.row div.cell{padding-top:10px}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{width:100%;height:2.3em;font-size:0.8em;text-align:center;line-height:1em}div.DTE div.editor_upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0}div.DTE div.editor_upload div.drop{position:relative;box-sizing:border-box;width:100%;height:100%;border:3px dashed #ccc;border-radius:6px;min-height:4em;color:#999;padding-top:3px;text-align:center}div.DTE div.editor_upload div.drop.over{border:3px dashed #111;color:#111}div.DTE div.editor_upload div.drop span{max-width:75%;font-size:0.85em;line-height:1em}div.DTE div.editor_upload div.rendered img{max-width:8em;margin:0 auto}div.DTE div.editor_upload.noDrop div.drop{display:none}div.DTE div.editor_upload.noDrop div.row.second{display:none}div.DTE div.editor_upload.noDrop div.rendered{margin-top:10px}div.DTE div.editor_upload.noClear div.clearValue button{display:none}div.DTE div.editor_upload.multi div.cell{display:block;width:100%}div.DTE div.editor_upload.multi div.cell div.drop{min-height:0;padding-bottom:5px}div.DTE div.editor_upload.multi div.clearValue{display:none}div.DTE div.editor_upload.multi ul{list-style-type:none;margin:0;padding:0}div.DTE div.editor_upload.multi ul li{position:relative;margin-top:0.5em}div.DTE div.editor_upload.multi ul li:first-child{margin-top:0}div.DTE div.editor_upload.multi ul li img{vertical-align:middle}div.DTE div.editor_upload.multi ul li button{position:absolute;width:40px;right:0;top:50%;margin-top:-1.5em}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{height:auto}div.DTE div.editor_upload ul li button{padding-bottom:8px}div.editor-datetime{position:absolute;background-color:white;z-index:2050;border:1px solid #ccc;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5);padding-bottom:5px}div.editor-datetime div.editor-datetime-title{text-align:center;padding:5px 0px 3px}div.editor-datetime table{border-spacing:0;margin:6px 13px}div.editor-datetime table th{font-size:0.8em;color:#777;font-weight:normal;width:14.285714286%;padding:0 0 4px 0;text-align:center}div.editor-datetime table td{font-size:0.9em;color:#444;padding:0}div.editor-datetime table td.day{text-align:right;background:#f5f5f5}div.editor-datetime table td.day.disabled{color:#aaa;background:white}div.editor-datetime table td.day.today{background-color:#ddd}div.editor-datetime table td.day.today button{font-weight:bold}div.editor-datetime table td.day.selected button{background:#0275d8;color:white;border-radius:2px}div.editor-datetime table td.day button:hover{background:#ff8000;color:white;border-radius:2px}div.editor-datetime table td.editor-datetime-week{font-size:0.7em}div.editor-datetime table button{width:100%;box-sizing:border-box;border:none;background:transparent;font-size:inherit;color:inherit;text-align:inherit;padding:5px 9px;cursor:pointer;margin:0}div.editor-datetime table.weekNumber th{width:12.5%}div.editor-datetime div.editor-datetime-label{position:relative;display:inline-block;height:30px;padding:5px 6px;border:1px solid transparent;box-sizing:border-box;cursor:pointer}div.editor-datetime div.editor-datetime-label:hover{border:1px solid #ddd;border-radius:2px;background-color:#f5f5f5}div.editor-datetime div.editor-datetime-label select{position:absolute;top:6px;left:0;cursor:pointer;opacity:0;-ms-filter:"alpha(opacity=0)"}div.editor-datetime div.editor-datetime-time{text-align:center}div.editor-datetime div.editor-datetime-time>span{vertical-align:middle}div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock{display:inline-block;vertical-align:middle}div.editor-datetime div.editor-datetime-iconLeft,div.editor-datetime div.editor-datetime-iconRight,div.editor-datetime div.editor-datetime-iconUp,div.editor-datetime div.editor-datetime-iconDown{width:30px;height:30px;background-position:center;background-repeat:no-repeat;opacity:0.3;overflow:hidden;box-sizing:border-box}div.editor-datetime div.editor-datetime-iconLeft:hover,div.editor-datetime div.editor-datetime-iconRight:hover,div.editor-datetime div.editor-datetime-iconUp:hover,div.editor-datetime div.editor-datetime-iconDown:hover{border:1px solid #ccc;border-radius:2px;background-color:#f0f0f0;opacity:0.6}div.editor-datetime div.editor-datetime-iconLeft button,div.editor-datetime div.editor-datetime-iconRight button,div.editor-datetime div.editor-datetime-iconUp button,div.editor-datetime div.editor-datetime-iconDown button{border:none;background:transparent;text-indent:30px;height:100%;width:100%;cursor:pointer}div.editor-datetime div.editor-datetime-iconLeft{position:absolute;top:5px;left:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}div.editor-datetime div.editor-datetime-iconRight{position:absolute;top:5px;right:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconUp{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconDown{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC")}div.editor-datetime-error{padding:0 1em;max-width:240px;font-size:11px;line-height:1.25em;text-align:center;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{position:absolute;top:17px;right:9px;height:2em;width:2em;z-index:20;font-size:12px;display:none;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}div.DTE.processing div.DTE_Processing_Indicator{display:block}div.DTE.DTE_Inline div.DTE_Processing_Indicator{top:5px;right:6px;font-size:6px}div.DTE.DTE_Bubble div.DTE_Processing_Indicator{top:10px;right:14px;font-size:8px}div.DTE div.DTE_Processing_Indicator span,div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{display:block;background:black;width:0.5em;height:1.5em;border:1px solid rgba(0,0,0,0.4);background-color:rgba(0,0,0,0.1);-webkit-animation:editorProcessing 0.9s infinite ease-in-out;animation:editorProcessing 0.9s infinite ease-in-out}div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{position:absolute;top:0;content:''}div.DTE div.DTE_Processing_Indicator:before{left:-1em;-webkit-animation-delay:-0.3s;animation-delay:-0.3s}div.DTE div.DTE_Processing_Indicator span{-webkit-animation-delay:-0.15s;animation-delay:-0.15s}div.DTE div.DTE_Processing_Indicator:after{left:1em}@-webkit-keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}@keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}div.DTE div.DTE_Processing_Indicator{top:52px;right:12px} diff --git a/css/csstables/editor.dataTables.css b/css/csstables/editor.dataTables.css deleted file mode 100644 index f368d35..0000000 --- a/css/csstables/editor.dataTables.css +++ /dev/null @@ -1,1364 +0,0 @@ -div.DTE { - position: relative; -} -div.DTE div.DTE_Header { - position: absolute; - top: 0; - left: 0; - height: 50px; - width: 100%; - background-color: #f3f3f3; - border-bottom: 1px solid #ddd; - padding: 16px 10px 2px 16px; - font-size: 1.3em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE div.DTE_Footer { - position: absolute; - bottom: 0; - left: 0; - height: 50px; - width: 100%; - background-color: #f3f3f3; - border-top: 1px solid #ddd; - padding: 10px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE div.DTE_Body { - position: relative; -} -div.DTE div.DTE_Form_Info { - margin-bottom: 0.5em; - display: none; -} -div.DTE div.DTE_Form_Content { - position: relative; - padding: 10px; -} -div.DTE div.DTE_Form_Error { - float: left; - padding: 5px; - display: none; - color: #b11f1f; -} -div.DTE button.btn, -div.DTE div.DTE_Form_Buttons button { - position: relative; - text-align: center; - display: block; - margin-top: 0; - padding: 5px 15px; - cursor: pointer; - float: right; - margin-left: 0.75em; - font-size: 14px; - text-shadow: 0 1px 0 white; - border: 1px solid #999; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - -ms-border-radius: 4px; - -o-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 1px 1px 3px #ccc; - -moz-box-shadow: 1px 1px 3px #ccc; - box-shadow: 1px 1px 3px #ccc; - background-color: #f9f9f9 100%; - /* Fallback */ - background-image: -webkit-linear-gradient(top, #ffffff 0%, #eee 65%, #f9f9f9 100%); - /* Chrome 10+, Saf5.1+, iOS 5+ */ - background-image: -moz-linear-gradient(top, #ffffff 0%, #eee 65%, #f9f9f9 100%); - /* FF3.6 */ - background-image: -ms-linear-gradient(top, #ffffff 0%, #eee 65%, #f9f9f9 100%); - /* IE10 */ - background-image: -o-linear-gradient(top, #ffffff 0%, #eee 65%, #f9f9f9 100%); - /* Opera 11.10+ */ - background-image: linear-gradient(to bottom, #ffffff 0%, #eee 65%, #f9f9f9 100%); - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#ffffff', EndColorStr='#f9f9f9'); -} -div.DTE button.btn:hover, -div.DTE div.DTE_Form_Buttons button:hover { - border: 1px solid #666; - -webkit-box-shadow: 1px 1px 3px #999; - -moz-box-shadow: 1px 1px 3px #999; - box-shadow: 1px 1px 3px #999; - background-color: #f4f4f4 100%; - /* Fallback */ - background-image: -webkit-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%); - /* Chrome 10+, Saf5.1+, iOS 5+ */ - background-image: -moz-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%); - /* FF3.6 */ - background-image: -ms-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%); - /* IE10 */ - background-image: -o-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%); - /* Opera 11.10+ */ - background-image: linear-gradient(to bottom, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%); - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f3f3f3', EndColorStr='#f4f4f4'); -} -div.DTE button.btn:active, -div.DTE div.DTE_Form_Buttons button:active { - -webkit-box-shadow: inset 1px 1px 3px #999; - -moz-box-shadow: inset 1px 1px 3px #999; - box-shadow: inset 1px 1px 3px #999; -} -div.DTE button.btn:focus, -div.DTE div.DTE_Form_Buttons button:focus { - border: 1px solid #426c9e; - text-shadow: 0 1px 0 #c4def1; - background-color: #a3d0ef 100%; - /* Fallback */ - background-image: -webkit-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%); - /* Chrome 10+, Saf5.1+, iOS 5+ */ - background-image: -moz-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%); - /* FF3.6 */ - background-image: -ms-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%); - /* IE10 */ - background-image: -o-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%); - /* Opera 11.10+ */ - background-image: linear-gradient(to bottom, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%); - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#a3d0ef', EndColorStr='#a3d0ef'); - outline: none; -} -div.DTE button.btn:focus:after, -div.DTE div.DTE_Form_Buttons button:focus:after { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: white; - display: block; - content: " "; - -webkit-animation-duration: 1s; - -webkit-animation-name: buttonPulse; - -webkit-animation-fill-mode: forwards; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -webkit-animation-direction: alternate; - -moz-animation-duration: 1s; - -moz-animation-name: buttonPulse; - -moz-animation-fill-mode: forwards; - -moz-animation-iteration-count: infinite; - -moz-animation-timing-function: linear; - -moz-animation-direction: alternate; - -o-animation-duration: 1s; - -o-animation-name: buttonPulse; - -o-animation-fill-mode: forwards; - -o-animation-iteration-count: infinite; - -o-animation-timing-function: linear; - -o-animation-direction: alternate; - animation-duration: 1s; - animation-name: buttonPulse; - animation-fill-mode: forwards; - animation-iteration-count: infinite; - animation-timing-function: linear; - animation-direction: alternate; -} -div.DTE.DTE_Action_Remove div.DTE_Body_Content { - text-align: center; - padding: 20px 0; -} - -@-webkit-keyframes buttonPulse { - 0% { - opacity: 0; - } - 100% { - opacity: 0.2; - } -} -@-moz-keyframes buttonPulse { - 0% { - opacity: 0; - } - 100% { - opacity: 0.2; - } -} -@-o-keyframes buttonPulse { - 0% { - opacity: 0; - } - 100% { - opacity: 0.2; - } -} -@keyframes buttonPulse { - 0% { - opacity: 0; - } - 100% { - opacity: 0.2; - } -} -div.DTTT_container { - float: left; -} - -div.DTE_Field input, -div.DTE_Field textarea { - box-sizing: border-box; - background-color: white; - -webkit-transition: background-color ease-in-out .15s; - transition: background-color ease-in-out .15s; -} -div.DTE_Field input:focus, -div.DTE_Field textarea:focus { - background-color: #ffffee; -} -div.DTE_Field input[type="color"], -div.DTE_Field input[type="date"], -div.DTE_Field input[type="datetime"], -div.DTE_Field input[type="datetime-local"], -div.DTE_Field input[type="email"], -div.DTE_Field input[type="month"], -div.DTE_Field input[type="number"], -div.DTE_Field input[type="password"], -div.DTE_Field input[type="search"], -div.DTE_Field input[type="tel"], -div.DTE_Field input[type="text"], -div.DTE_Field input[type="time"], -div.DTE_Field input[type="url"], -div.DTE_Field input[type="week"] { - padding: 6px 4px; - width: 100%; -} -div.DTE_Field label div.DTE_Label_Info { - font-size: 0.85em; - margin-top: 0.25em; -} -div.DTE_Field label div.DTE_Label_Info:empty { - margin-top: 0; -} -div.DTE_Field div.DTE_Field_Info, -div.DTE_Field div.DTE_Field_Message, -div.DTE_Field div.DTE_Field_Error { - font-size: 11px; - line-height: 1em; - margin-top: 5px; -} -div.DTE_Field div.DTE_Field_Info:empty, -div.DTE_Field div.DTE_Field_Message:empty, -div.DTE_Field div.DTE_Field_Error:empty { - margin-top: 0; -} -div.DTE_Field div.DTE_Field_Error { - display: none; - color: #b11f1f; -} -div.DTE_Field div.multi-value { - display: none; - border: 1px dotted #666; - border-radius: 3px; - padding: 5px; - background-color: #fafafa; - cursor: pointer; -} -div.DTE_Field div.multi-value span { - font-size: 0.8em; - line-height: 1.25em; - display: block; - color: #666; -} -div.DTE_Field div.multi-value.multi-noEdit { - border: 1px solid #ccc; - cursor: auto; - background-color: #fcfcfc; -} -div.DTE_Field div.multi-value.multi-noEdit:hover { - background-color: #fcfcfc; -} -div.DTE_Field div.multi-value:hover { - background-color: #f1f1f1; -} -div.DTE_Field.disabled { - color: grey; -} -div.DTE_Field.disabled div.multi-value { - cursor: default; - border: 1px dotted #aaa; - background-color: transparent; -} -div.DTE_Field div.multi-restore { - display: none; - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; - color: #3879d9; -} -div.DTE_Field div.multi-restore:hover { - text-decoration: underline; - cursor: pointer; -} - -div.DTE_Field_Type_textarea textarea { - padding: 3px; - width: 100%; - height: 80px; -} - -div.DTE_Field.DTE_Field_Type_date img { - vertical-align: middle; - cursor: pointer; - *cursor: hand; -} -div.DTE_Field.DTE_Field_Type_date input.jqueryui { - width: 87%; - margin-right: 6px; -} - -div.DTE_Field_Type_checkbox div.DTE_Field_Input > div > div, -div.DTE_Field_Type_radio div.DTE_Field_Input > div > div { - margin-bottom: 0.25em; -} -div.DTE_Field_Type_checkbox div.DTE_Field_Input > div > div:last-child, -div.DTE_Field_Type_radio div.DTE_Field_Input > div > div:last-child { - margin-bottom: 0; -} -div.DTE_Field_Type_checkbox div.DTE_Field_Input > div > div label, -div.DTE_Field_Type_radio div.DTE_Field_Input > div > div label { - margin-left: 0.75em; - vertical-align: middle; -} - -div.DTE_Field_Type_select div.DTE_Field_Input { - padding-top: 4px; -} - -div.DTE_Body { - padding: 50px 0; -} -div.DTE_Body div.DTE_Body_Content { - position: relative; - overflow: auto; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Form_Info { - padding: 1em 1em 0 1em; - margin: 0; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field { - position: relative; - zoom: 1; - clear: both; - padding: 5px 20%; - border: 1px solid transparent; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field:hover { - background-color: #f9f9f9; - border: 1px solid #f3f3f3; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field > label { - float: left; - width: 40%; - padding-top: 6px; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field > div.DTE_Field_Input { - float: right; - width: 60%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.full { - padding: 5px 0 5px 20%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label { - width: 30%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input { - width: 70%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - float: none; - clear: both; - width: 100%; -} - -html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field > label { - float: right; -} -html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field > div.DTE_Field_Input { - float: left; -} -html[dir="rtl"] div.DTE div.DTE_Form_Buttons button { - float: left; -} - -@media only screen and (max-width: 768px) { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - padding: 5px 10%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full { - padding: 5px 0 5px 10%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label { - width: 35.5%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input { - width: 64.5%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - width: 100%; - } -} -@media only screen and (max-width: 640px) { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - padding: 5px 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full { - padding: 5px 0%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label { - width: 40%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input { - width: 60%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - width: 100%; - } -} -@media only screen and (max-width: 580px) { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - position: relative; - zoom: 1; - clear: both; - padding: 5px 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field > label { - float: none; - width: auto; - padding-top: 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field > div.DTE_Field_Input { - float: none; - width: auto; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full, div.DTE_Body div.DTE_Body_Content div.DTE_Field.block { - padding: 5px 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label, - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input, div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > label, - div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - width: 100%; - } -} -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table { - display: table; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form { - display: table-cell; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { - padding: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field:last-child { - margin-bottom: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Form_Info, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); -} -div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url("../images/ajax-loader-small.gif") no-repeat top left; -} -div.DTE_Bubble.below div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; -} -div.DTE_Bubble.below div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTE_Bubble_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTE_Bubble_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -div.DTE_Inline { - position: relative; - display: table; - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field, -div.DTE_Inline div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field { - padding: 0; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field > label, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field > label { - display: none; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input { - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; -} -div.DTE_Inline div.DTE_Field input[type="color"], -div.DTE_Inline div.DTE_Field input[type="date"], -div.DTE_Inline div.DTE_Field input[type="datetime"], -div.DTE_Inline div.DTE_Field input[type="datetime-local"], -div.DTE_Inline div.DTE_Field input[type="email"], -div.DTE_Inline div.DTE_Field input[type="month"], -div.DTE_Inline div.DTE_Field input[type="number"], -div.DTE_Inline div.DTE_Field input[type="password"], -div.DTE_Inline div.DTE_Field input[type="search"], -div.DTE_Inline div.DTE_Field input[type="tel"], -div.DTE_Inline div.DTE_Field input[type="text"], -div.DTE_Inline div.DTE_Field input[type="time"], -div.DTE_Inline div.DTE_Field input[type="url"], -div.DTE_Inline div.DTE_Field input[type="week"] { - margin: -6px 0; -} -div.DTE_Inline div.DTE_Field_Error, -div.DTE_Inline div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; -} -div.DTE_Inline div.DTE_Field_Error:empty, -div.DTE_Inline div.DTE_Form_Error:empty { - margin-top: 0; -} - -span.dtr-data div.DTE_Inline { - display: inline-table; -} - -div.DTED_Lightbox_Wrapper { - position: fixed; - top: 0; - left: 50%; - margin-left: -390px; - width: 780px; - height: 100%; - z-index: 11; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container { - *position: absolute; - /* IE6 */ - *top: 50%; - #position: absolute; - /* IE7 */ - #top: 50%; - display: table; - height: 100%; - width: 100%; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper { - *position: relative; - /* IE6 */ - #position: relative; - /* IE7 */ - display: table-cell; - vertical-align: middle; - width: 100%; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content { - *top: -50%; - /* IE6 */ - #top: -50%; - /* IE7 */ - position: relative; - border: 7px solid rgba(220, 220, 220, 0.5); - box-shadow: 2px 2px 10px #555; - border-radius: 10px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE { - background: white; - border-radius: 6px; - box-shadow: 0 0 5px #555; - border: 2px solid #444; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Header { - top: 2px; - left: 2px; - right: 2px; - width: auto; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Footer { - bottom: 2px; - left: 2px; - right: 2px; - width: auto; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} - -div.DTED_Lightbox_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTED_Lightbox_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTED_Lightbox_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Background { - height: 0; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Shown { - display: none; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper { - position: absolute; - top: 0px; - left: 0px; - right: 0px; - bottom: 0px; - width: auto; - height: auto; - margin-left: 0; - -webkit-overflow-scrolling: touch; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container { - display: block; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper { - display: block; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content { - border: 4px solid rgba(220, 220, 220, 0.5); - border-radius: 0; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE { - border-radius: 0; - box-shadow: 0 0 5px #555; - border: 2px solid #444; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Header { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Footer { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} -body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close { - top: 11px; - right: 15px; -} - -@media only screen and (max-width: 780px) { - div.DTED_Lightbox_Wrapper { - position: fixed; - top: 0; - left: 0; - width: 100%; - margin-left: 0; - } -} -/* - * Namespace: DTED - DataTables Editor Display - Envelope - */ -div.DTED_Envelope_Wrapper { - position: absolute; - top: 0; - bottom: 0; - left: 50%; - height: 100%; - z-index: 11; - display: none; - overflow: hidden; -} -div.DTED_Envelope_Wrapper div.DTED_Envelope_Shadow { - position: absolute; - top: -10px; - left: 10px; - right: 10px; - height: 10px; - z-index: 10; - box-shadow: 0 0 20px black; -} -div.DTED_Envelope_Wrapper div.DTED_Envelope_Container { - position: absolute; - top: 0; - left: 5%; - width: 90%; - border-left: 1px solid #777; - border-right: 1px solid #777; - border-bottom: 1px solid #777; - box-shadow: 3px 3px 10px #555; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - background-color: white; -} -div.DTED_Envelope_Wrapper div.DTED_Envelope_Container div.DTE_Processing_Indicator { - right: 36px; -} -div.DTED_Envelope_Wrapper div.DTED_Envelope_Container div.DTE_Footer { - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; -} -div.DTED_Envelope_Wrapper div.DTED_Envelope_Container div.DTED_Envelope_Close { - position: absolute; - top: 16px; - right: 10px; - width: 18px; - height: 18px; - cursor: pointer; - *cursor: hand; - z-index: 12; - text-align: center; - font-size: 12px; - background: #F8F8F8; - background: -webkit-gradient(linear, center bottom, center top, from(#CCC), to(white)); - background: -moz-linear-gradient(top, white, #CCC); - background: linear-gradient(to bottom, white, #CCC); - text-shadow: 0 1px 0 white; - border: 1px solid #999; - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - box-shadow: 0px 0px 1px #999; - -moz-box-shadow: 0px 0px 1px #999; - -webkit-box-shadow: 0px 0px 1px #999; -} - -div.DTED_Envelope_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10; - background: rgba(0, 0, 0, 0.4); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.1)), color-stop(1, rgba(0, 0, 0, 0.4))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 100%); - /* W3C Markup, IE10 Release Preview */ -} - -table.dataTable tbody tr.highlight { - background-color: #FFFBCC !important; -} -table.dataTable tbody tr.highlight, -table.dataTable tbody tr.noHighlight, -table.dataTable tbody tr.highlight td, -table.dataTable tbody tr.noHighlight td { - -webkit-transition: background-color 500ms linear; - -moz-transition: background-color 500ms linear; - -ms-transition: background-color 500ms linear; - -o-transition: background-color 500ms linear; - transition: background-color 500ms linear; -} -table.dataTable.stripe tbody tr.odd.highlight, table.dataTable.display tbody tr.odd.highlight { - background-color: #f9f5c7; -} -table.dataTable.hover tbody tr:hover.highlight, -table.dataTable.hover tbody tr.odd:hover.highlight, -table.dataTable.hover tbody tr.even:hover.highlight, table.dataTable.display tbody tr:hover.highlight, -table.dataTable.display tbody tr.odd:hover.highlight, -table.dataTable.display tbody tr.even:hover.highlight { - background-color: #f6f2c5; -} -table.dataTable.order-column tbody tr.highlight > .sorting_1, -table.dataTable.order-column tbody tr.highlight > .sorting_2, -table.dataTable.order-column tbody tr.highlight > .sorting_3, table.dataTable.display tbody tr.highlight > .sorting_1, -table.dataTable.display tbody tr.highlight > .sorting_2, -table.dataTable.display tbody tr.highlight > .sorting_3 { - background-color: #faf6c8; -} -table.dataTable.display tbody tr.odd.highlight > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.highlight > .sorting_1 { - background-color: #f1edc1; -} -table.dataTable.display tbody tr.odd.highlight > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.highlight > .sorting_2 { - background-color: #f3efc2; -} -table.dataTable.display tbody tr.odd.highlight > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.highlight > .sorting_3 { - background-color: #f5f1c4; -} -table.dataTable.display tbody tr.even.highlight > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.highlight > .sorting_1 { - background-color: #faf6c8; -} -table.dataTable.display tbody tr.even.highlight > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.highlight > .sorting_2 { - background-color: #fcf8ca; -} -table.dataTable.display tbody tr.even.highlight > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.highlight > .sorting_3 { - background-color: #fefacb; -} -table.dataTable.display tbody tr:hover.highlight > .sorting_1, -table.dataTable.display tbody tr.odd:hover.highlight > .sorting_1, -table.dataTable.display tbody tr.even:hover.highlight > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.highlight > .sorting_1, -table.dataTable.order-column.hover tbody tr.odd:hover.highlight > .sorting_1, -table.dataTable.order-column.hover tbody tr.even:hover.highlight > .sorting_1 { - background-color: #eae6bb; -} -table.dataTable.display tbody tr:hover.highlight > .sorting_2, -table.dataTable.display tbody tr.odd:hover.highlight > .sorting_2, -table.dataTable.display tbody tr.even:hover.highlight > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.highlight > .sorting_2, -table.dataTable.order-column.hover tbody tr.odd:hover.highlight > .sorting_2, -table.dataTable.order-column.hover tbody tr.even:hover.highlight > .sorting_2 { - background-color: #ece8bd; -} -table.dataTable.display tbody tr:hover.highlight > .sorting_3, -table.dataTable.display tbody tr.odd:hover.highlight > .sorting_3, -table.dataTable.display tbody tr.even:hover.highlight > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.highlight > .sorting_3, -table.dataTable.order-column.hover tbody tr.odd:hover.highlight > .sorting_3, -table.dataTable.order-column.hover tbody tr.even:hover.highlight > .sorting_3 { - background-color: #efebbf; -} - -div.DTE div.editor_upload { - padding-top: 4px; -} -div.DTE div.editor_upload div.eu_table { - display: table; - width: 100%; -} -div.DTE div.editor_upload div.row { - display: table-row; -} -div.DTE div.editor_upload div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; -} -div.DTE div.editor_upload div.cell + div.cell { - padding-left: 10px; -} -div.DTE div.editor_upload div.row + div.row div.cell { - padding-top: 10px; -} -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; -} -div.DTE div.editor_upload input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; -} -div.DTE div.editor_upload div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; -} -div.DTE div.editor_upload div.drop.over { - border: 3px dashed #111; - color: #111; -} -div.DTE div.editor_upload div.drop span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; -} -div.DTE div.editor_upload div.rendered img { - max-width: 8em; - margin: 0 auto; -} -div.DTE div.editor_upload.noDrop div.drop { - display: none; -} -div.DTE div.editor_upload.noDrop div.row.second { - display: none; -} -div.DTE div.editor_upload.noDrop div.rendered { - margin-top: 10px; -} -div.DTE div.editor_upload.noClear div.clearValue button { - display: none; -} -div.DTE div.editor_upload.multi div.cell { - display: block; - width: 100%; -} -div.DTE div.editor_upload.multi div.cell div.drop { - min-height: 0; - padding-bottom: 5px; -} -div.DTE div.editor_upload.multi div.clearValue { - display: none; -} -div.DTE div.editor_upload.multi ul { - list-style-type: none; - margin: 0; - padding: 0; -} -div.DTE div.editor_upload.multi ul li { - position: relative; - margin-top: 0.5em; -} -div.DTE div.editor_upload.multi ul li:first-child { - margin-top: 0; -} -div.DTE div.editor_upload.multi ul li img { - vertical-align: middle; -} -div.DTE div.editor_upload.multi ul li button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; -} - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5); - padding-bottom: 5px; -} -div.editor-datetime div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; -} -div.editor-datetime table { - border-spacing: 0; - margin: 6px 13px; -} -div.editor-datetime table th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; -} -div.editor-datetime table td { - font-size: 0.9em; - color: #444; - padding: 0; -} -div.editor-datetime table td.day { - text-align: right; - background: #f5f5f5; -} -div.editor-datetime table td.day.disabled { - color: #aaa; - background: white; -} -div.editor-datetime table td.day.today { - background-color: #ddd; -} -div.editor-datetime table td.day.today button { - font-weight: bold; -} -div.editor-datetime table td.day.selected button { - background: #4E6CA3; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.day button:hover { - background: #ff8000; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.editor-datetime-week { - font-size: 0.7em; -} -div.editor-datetime table button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; -} -div.editor-datetime table.weekNumber th { - width: 12.5%; -} -div.editor-datetime div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-label:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; -} -div.editor-datetime div.editor-datetime-label select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; -} -div.editor-datetime div.editor-datetime-time { - text-align: center; -} -div.editor-datetime div.editor-datetime-time > span { - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-iconLeft, -div.editor-datetime div.editor-datetime-iconRight, -div.editor-datetime div.editor-datetime-iconUp, -div.editor-datetime div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; -} -div.editor-datetime div.editor-datetime-iconLeft:hover, -div.editor-datetime div.editor-datetime-iconRight:hover, -div.editor-datetime div.editor-datetime-iconUp:hover, -div.editor-datetime div.editor-datetime-iconDown:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; -} -div.editor-datetime div.editor-datetime-iconLeft button, -div.editor-datetime div.editor-datetime-iconRight button, -div.editor-datetime div.editor-datetime-iconUp button, -div.editor-datetime div.editor-datetime-iconDown button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=="); -} -div.editor-datetime div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconUp { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconDown { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC"); -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} - -div.DTE div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -div.DTE.processing div.DTE_Processing_Indicator { - display: block; -} -div.DTE.DTE_Inline div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; -} -div.DTE.DTE_Bubble div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; -} -div.DTE div.DTE_Processing_Indicator span, -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.1); - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; -} -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; -} -div.DTE div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -div.DTE div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; -} -div.DTE div.DTE_Processing_Indicator:after { - left: 1em; -} -@-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -@keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} diff --git a/css/csstables/editor.dataTables.min.css b/css/csstables/editor.dataTables.min.css deleted file mode 100644 index 2ab33d6..0000000 --- a/css/csstables/editor.dataTables.min.css +++ /dev/null @@ -1,5 +0,0 @@ -div.DTE{position:relative}div.DTE div.DTE_Header{position:absolute;top:0;left:0;height:50px;width:100%;background-color:#f3f3f3;border-bottom:1px solid #ddd;padding:16px 10px 2px 16px;font-size:1.3em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE div.DTE_Footer{position:absolute;bottom:0;left:0;height:50px;width:100%;background-color:#f3f3f3;border-top:1px solid #ddd;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE div.DTE_Body{position:relative}div.DTE div.DTE_Form_Info{margin-bottom:0.5em;display:none}div.DTE div.DTE_Form_Content{position:relative;padding:10px}div.DTE div.DTE_Form_Error{float:left;padding:5px;display:none;color:#b11f1f}div.DTE button.btn,div.DTE div.DTE_Form_Buttons button{position:relative;text-align:center;display:block;margin-top:0;padding:5px 15px;cursor:pointer;float:right;margin-left:0.75em;font-size:14px;text-shadow:0 1px 0 white;border:1px solid #999;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;-o-border-radius:4px;border-radius:4px;-webkit-box-shadow:1px 1px 3px #ccc;-moz-box-shadow:1px 1px 3px #ccc;box-shadow:1px 1px 3px #ccc;background-color:#f9f9f9 100%;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 65%, #f9f9f9 100%);background-image:-moz-linear-gradient(top, #fff 0%, #eee 65%, #f9f9f9 100%);background-image:-ms-linear-gradient(top, #fff 0%, #eee 65%, #f9f9f9 100%);background-image:-o-linear-gradient(top, #fff 0%, #eee 65%, #f9f9f9 100%);background-image:linear-gradient(to bottom, #fff 0%, #eee 65%, #f9f9f9 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#ffffff', EndColorStr='#f9f9f9')}div.DTE button.btn:hover,div.DTE div.DTE_Form_Buttons button:hover{border:1px solid #666;-webkit-box-shadow:1px 1px 3px #999;-moz-box-shadow:1px 1px 3px #999;box-shadow:1px 1px 3px #999;background-color:#f4f4f4 100%;background-image:-webkit-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%);background-image:-moz-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%);background-image:-ms-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%);background-image:-o-linear-gradient(top, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%);background-image:linear-gradient(to bottom, #f3f3f3 0%, #dbdbdb 65%, #f4f4f4 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f3f3f3', EndColorStr='#f4f4f4')}div.DTE button.btn:active,div.DTE div.DTE_Form_Buttons button:active{-webkit-box-shadow:inset 1px 1px 3px #999;-moz-box-shadow:inset 1px 1px 3px #999;box-shadow:inset 1px 1px 3px #999}div.DTE button.btn:focus,div.DTE div.DTE_Form_Buttons button:focus{border:1px solid #426c9e;text-shadow:0 1px 0 #c4def1;background-color:#a3d0ef 100%;background-image:-webkit-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%);background-image:-moz-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%);background-image:-ms-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%);background-image:-o-linear-gradient(top, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%);background-image:linear-gradient(to bottom, #a3d0ef 0%, #79ace9 65%, #a3d0ef 100%);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#a3d0ef', EndColorStr='#a3d0ef');outline:none}div.DTE button.btn:focus:after,div.DTE div.DTE_Form_Buttons button:focus:after{position:absolute;top:0;left:0;right:0;bottom:0;background:white;display:block;content:" ";-webkit-animation-duration:1s;-webkit-animation-name:buttonPulse;-webkit-animation-fill-mode:forwards;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-webkit-animation-direction:alternate;-moz-animation-duration:1s;-moz-animation-name:buttonPulse;-moz-animation-fill-mode:forwards;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear;-moz-animation-direction:alternate;-o-animation-duration:1s;-o-animation-name:buttonPulse;-o-animation-fill-mode:forwards;-o-animation-iteration-count:infinite;-o-animation-timing-function:linear;-o-animation-direction:alternate;animation-duration:1s;animation-name:buttonPulse;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-timing-function:linear;animation-direction:alternate}div.DTE.DTE_Action_Remove div.DTE_Body_Content{text-align:center;padding:20px 0}@-webkit-keyframes buttonPulse{0%{opacity:0}100%{opacity:0.2}}@-moz-keyframes buttonPulse{0%{opacity:0}100%{opacity:0.2}}@-o-keyframes buttonPulse{0%{opacity:0}100%{opacity:0.2}}@keyframes buttonPulse{0%{opacity:0}100%{opacity:0.2}}div.DTTT_container{float:left}div.DTE_Field input,div.DTE_Field textarea{box-sizing:border-box;background-color:white;-webkit-transition:background-color ease-in-out .15s;transition:background-color ease-in-out .15s}div.DTE_Field input:focus,div.DTE_Field textarea:focus{background-color:#ffffee}div.DTE_Field input[type="color"],div.DTE_Field input[type="date"],div.DTE_Field input[type="datetime"],div.DTE_Field input[type="datetime-local"],div.DTE_Field input[type="email"],div.DTE_Field input[type="month"],div.DTE_Field input[type="number"],div.DTE_Field input[type="password"],div.DTE_Field input[type="search"],div.DTE_Field input[type="tel"],div.DTE_Field input[type="text"],div.DTE_Field input[type="time"],div.DTE_Field input[type="url"],div.DTE_Field input[type="week"]{padding:6px 4px;width:100%}div.DTE_Field label div.DTE_Label_Info{font-size:0.85em;margin-top:0.25em}div.DTE_Field label div.DTE_Label_Info:empty{margin-top:0}div.DTE_Field div.DTE_Field_Info,div.DTE_Field div.DTE_Field_Message,div.DTE_Field div.DTE_Field_Error{font-size:11px;line-height:1em;margin-top:5px}div.DTE_Field div.DTE_Field_Info:empty,div.DTE_Field div.DTE_Field_Message:empty,div.DTE_Field div.DTE_Field_Error:empty{margin-top:0}div.DTE_Field div.DTE_Field_Error{display:none;color:#b11f1f}div.DTE_Field div.multi-value{display:none;border:1px dotted #666;border-radius:3px;padding:5px;background-color:#fafafa;cursor:pointer}div.DTE_Field div.multi-value span{font-size:0.8em;line-height:1.25em;display:block;color:#666}div.DTE_Field div.multi-value.multi-noEdit{border:1px solid #ccc;cursor:auto;background-color:#fcfcfc}div.DTE_Field div.multi-value.multi-noEdit:hover{background-color:#fcfcfc}div.DTE_Field div.multi-value:hover{background-color:#f1f1f1}div.DTE_Field.disabled{color:grey}div.DTE_Field.disabled div.multi-value{cursor:default;border:1px dotted #aaa;background-color:transparent}div.DTE_Field div.multi-restore{display:none;margin-top:0.5em;font-size:0.8em;line-height:1.25em;color:#3879d9}div.DTE_Field div.multi-restore:hover{text-decoration:underline;cursor:pointer}div.DTE_Field_Type_textarea textarea{padding:3px;width:100%;height:80px}div.DTE_Field.DTE_Field_Type_date img{vertical-align:middle;cursor:pointer;*cursor:hand}div.DTE_Field.DTE_Field_Type_date input.jqueryui{width:87%;margin-right:6px}div.DTE_Field_Type_checkbox div.DTE_Field_Input>div>div,div.DTE_Field_Type_radio div.DTE_Field_Input>div>div{margin-bottom:0.25em}div.DTE_Field_Type_checkbox div.DTE_Field_Input>div>div:last-child,div.DTE_Field_Type_radio div.DTE_Field_Input>div>div:last-child{margin-bottom:0}div.DTE_Field_Type_checkbox div.DTE_Field_Input>div>div label,div.DTE_Field_Type_radio div.DTE_Field_Input>div>div label{margin-left:0.75em;vertical-align:middle}div.DTE_Field_Type_select div.DTE_Field_Input{padding-top:4px}div.DTE_Body{padding:50px 0}div.DTE_Body div.DTE_Body_Content{position:relative;overflow:auto}div.DTE_Body div.DTE_Body_Content div.DTE_Form_Info{padding:1em 1em 0 1em;margin:0}div.DTE_Body div.DTE_Body_Content div.DTE_Field{position:relative;zoom:1;clear:both;padding:5px 20%;border:1px solid transparent}div.DTE_Body div.DTE_Body_Content div.DTE_Field:after{display:block;content:".";height:0;line-height:0;clear:both;visibility:hidden}div.DTE_Body div.DTE_Body_Content div.DTE_Field:hover{background-color:#f9f9f9;border:1px solid #f3f3f3}div.DTE_Body div.DTE_Body_Content div.DTE_Field>label{float:left;width:40%;padding-top:6px}div.DTE_Body div.DTE_Body_Content div.DTE_Field>div.DTE_Field_Input{float:right;width:60%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full{padding:5px 0 5px 20%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label{width:30%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input{width:70%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{float:none;clear:both;width:100%}html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field>label{float:right}html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field>div.DTE_Field_Input{float:left}html[dir="rtl"] div.DTE div.DTE_Form_Buttons button{float:left}@media only screen and (max-width: 768px){div.DTE_Body div.DTE_Body_Content div.DTE_Field{padding:5px 10%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full{padding:5px 0 5px 10%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label{width:35.5%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input{width:64.5%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{width:100%}}@media only screen and (max-width: 640px){div.DTE_Body div.DTE_Body_Content div.DTE_Field{padding:5px 0}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full{padding:5px 0%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label{width:40%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input{width:60%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{width:100%}}@media only screen and (max-width: 580px){div.DTE_Body div.DTE_Body_Content div.DTE_Field{position:relative;zoom:1;clear:both;padding:5px 0}div.DTE_Body div.DTE_Body_Content div.DTE_Field>label{float:none;width:auto;padding-top:0}div.DTE_Body div.DTE_Body_Content div.DTE_Field>div.DTE_Field_Input{float:none;width:auto}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full,div.DTE_Body div.DTE_Body_Content div.DTE_Field.block{padding:5px 0}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label,div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input,div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>label,div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{width:100%}}div.DTE_Bubble{position:absolute;z-index:11;margin-top:-6px;opacity:0}div.DTE_Bubble div.DTE_Bubble_Liner{position:absolute;bottom:0;border:1px solid black;width:300px;margin-left:-150px;background-color:white;box-shadow:2px 2px 7px #555;border-radius:5px;border:2px solid #444;padding:1em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table{display:table;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form{display:table-cell}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content{padding:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{position:relative;zoom:1;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field:last-child{margin-bottom:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons{display:table-cell;vertical-align:bottom;padding:0 0 0 0.75em;width:1%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{border-top-left-radius:5px;border-top-right-radius:5px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Form_Info,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Bubble_Table{padding-top:42px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error{float:none;display:none;padding:0;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTE_Bubble div.DTE_Bubble_Triangle{position:absolute;height:10px;width:10px;top:-6px;background-color:white;border:2px solid #444;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after{position:absolute;content:' ';display:block;top:12px;right:18px;height:12px;width:17px;background:url("../images/ajax-loader-small.gif") no-repeat top left}div.DTE_Bubble.below div.DTE_Bubble_Liner{top:10px;bottom:auto}div.DTE_Bubble.below div.DTE_Bubble_Triangle{top:4px;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTE_Bubble_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTE_Bubble_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}div.DTE_Inline{position:relative;display:table;width:100%}div.DTE_Inline div.DTE_Inline_Field,div.DTE_Inline div.DTE_Inline_Buttons{display:table-cell;vertical-align:middle}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field{padding:0}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field>label,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field>label{display:none}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input{width:100%}div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button{margin:-6px 0 -6px 4px;padding:5px}div.DTE_Inline div.DTE_Field input[type="color"],div.DTE_Inline div.DTE_Field input[type="date"],div.DTE_Inline div.DTE_Field input[type="datetime"],div.DTE_Inline div.DTE_Field input[type="datetime-local"],div.DTE_Inline div.DTE_Field input[type="email"],div.DTE_Inline div.DTE_Field input[type="month"],div.DTE_Inline div.DTE_Field input[type="number"],div.DTE_Inline div.DTE_Field input[type="password"],div.DTE_Inline div.DTE_Field input[type="search"],div.DTE_Inline div.DTE_Field input[type="tel"],div.DTE_Inline div.DTE_Field input[type="text"],div.DTE_Inline div.DTE_Field input[type="time"],div.DTE_Inline div.DTE_Field input[type="url"],div.DTE_Inline div.DTE_Field input[type="week"]{margin:-6px 0}div.DTE_Inline div.DTE_Field_Error,div.DTE_Inline div.DTE_Form_Error{font-size:11px;line-height:1.2em;padding:0;margin-top:10px}div.DTE_Inline div.DTE_Field_Error:empty,div.DTE_Inline div.DTE_Form_Error:empty{margin-top:0}span.dtr-data div.DTE_Inline{display:inline-table}div.DTED_Lightbox_Wrapper{position:fixed;top:0;left:50%;margin-left:-390px;width:780px;height:100%;z-index:11}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container{*position:absolute;*top:50%;#position:absolute;#top:50%;display:table;height:100%;width:100%}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper{*position:relative;#position:relative;display:table-cell;vertical-align:middle;width:100%}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content{*top:-50%;#top:-50%;position:relative;border:7px solid rgba(220,220,220,0.5);box-shadow:2px 2px 10px #555;border-radius:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE{background:white;border-radius:6px;box-shadow:0 0 5px #555;border:2px solid #444;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Header{top:2px;left:2px;right:2px;width:auto;border-top-left-radius:5px;border-top-right-radius:5px}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Footer{bottom:2px;left:2px;right:2px;width:auto;border-bottom-left-radius:5px;border-bottom-right-radius:5px}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTED_Lightbox_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTED_Lightbox_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTED_Lightbox_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Background{height:0}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Shown{display:none}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper{position:absolute;top:0px;left:0px;right:0px;bottom:0px;width:auto;height:auto;margin-left:0;-webkit-overflow-scrolling:touch}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container{display:block}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper{display:block}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content{border:4px solid rgba(220,220,220,0.5);border-radius:0}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE{border-radius:0;box-shadow:0 0 5px #555;border:2px solid #444}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Header{border-top-left-radius:0;border-top-right-radius:0}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTE div.DTE_Footer{border-bottom-left-radius:0;border-bottom-right-radius:0}body.DTED_Lightbox_Mobile div.DTED_Lightbox_Wrapper div.DTED_Lightbox_Container div.DTED_Lightbox_Content_Wrapper div.DTED_Lightbox_Content div.DTED_Lightbox_Close{top:11px;right:15px}@media only screen and (max-width: 780px){div.DTED_Lightbox_Wrapper{position:fixed;top:0;left:0;width:100%;margin-left:0}}div.DTED_Envelope_Wrapper{position:absolute;top:0;bottom:0;left:50%;height:100%;z-index:11;display:none;overflow:hidden}div.DTED_Envelope_Wrapper div.DTED_Envelope_Shadow{position:absolute;top:-10px;left:10px;right:10px;height:10px;z-index:10;box-shadow:0 0 20px black}div.DTED_Envelope_Wrapper div.DTED_Envelope_Container{position:absolute;top:0;left:5%;width:90%;border-left:1px solid #777;border-right:1px solid #777;border-bottom:1px solid #777;box-shadow:3px 3px 10px #555;border-bottom-left-radius:5px;border-bottom-right-radius:5px;background-color:white}div.DTED_Envelope_Wrapper div.DTED_Envelope_Container div.DTE_Processing_Indicator{right:36px}div.DTED_Envelope_Wrapper div.DTED_Envelope_Container div.DTE_Footer{border-bottom-left-radius:5px;border-bottom-right-radius:5px}div.DTED_Envelope_Wrapper div.DTED_Envelope_Container div.DTED_Envelope_Close{position:absolute;top:16px;right:10px;width:18px;height:18px;cursor:pointer;*cursor:hand;z-index:12;text-align:center;font-size:12px;background:#F8F8F8;background:-webkit-gradient(linear, center bottom, center top, from(#CCC), to(#fff));background:-moz-linear-gradient(top, #fff, #CCC);background:linear-gradient(to bottom, #fff, #CCC);text-shadow:0 1px 0 white;border:1px solid #999;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px;box-shadow:0px 0px 1px #999;-moz-box-shadow:0px 0px 1px #999;-webkit-box-shadow:0px 0px 1px #999}div.DTED_Envelope_Background{position:fixed;top:0;left:0;width:100%;height:100%;z-index:10;background:rgba(0,0,0,0.4);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.4) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.4) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.4) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.1)), color-stop(1, rgba(0,0,0,0.4)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.4) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.4) 100%)}table.dataTable tbody tr.highlight{background-color:#FFFBCC !important}table.dataTable tbody tr.highlight,table.dataTable tbody tr.noHighlight,table.dataTable tbody tr.highlight td,table.dataTable tbody tr.noHighlight td{-webkit-transition:background-color 500ms linear;-moz-transition:background-color 500ms linear;-ms-transition:background-color 500ms linear;-o-transition:background-color 500ms linear;transition:background-color 500ms linear}table.dataTable.stripe tbody tr.odd.highlight,table.dataTable.display tbody tr.odd.highlight{background-color:#f9f5c7}table.dataTable.hover tbody tr:hover.highlight,table.dataTable.hover tbody tr.odd:hover.highlight,table.dataTable.hover tbody tr.even:hover.highlight,table.dataTable.display tbody tr:hover.highlight,table.dataTable.display tbody tr.odd:hover.highlight,table.dataTable.display tbody tr.even:hover.highlight{background-color:#f6f2c5}table.dataTable.order-column tbody tr.highlight>.sorting_1,table.dataTable.order-column tbody tr.highlight>.sorting_2,table.dataTable.order-column tbody tr.highlight>.sorting_3,table.dataTable.display tbody tr.highlight>.sorting_1,table.dataTable.display tbody tr.highlight>.sorting_2,table.dataTable.display tbody tr.highlight>.sorting_3{background-color:#faf6c8}table.dataTable.display tbody tr.odd.highlight>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.highlight>.sorting_1{background-color:#f1edc1}table.dataTable.display tbody tr.odd.highlight>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.highlight>.sorting_2{background-color:#f3efc2}table.dataTable.display tbody tr.odd.highlight>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.highlight>.sorting_3{background-color:#f5f1c4}table.dataTable.display tbody tr.even.highlight>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.highlight>.sorting_1{background-color:#faf6c8}table.dataTable.display tbody tr.even.highlight>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.highlight>.sorting_2{background-color:#fcf8ca}table.dataTable.display tbody tr.even.highlight>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.highlight>.sorting_3{background-color:#fefacb}table.dataTable.display tbody tr:hover.highlight>.sorting_1,table.dataTable.display tbody tr.odd:hover.highlight>.sorting_1,table.dataTable.display tbody tr.even:hover.highlight>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.highlight>.sorting_1,table.dataTable.order-column.hover tbody tr.odd:hover.highlight>.sorting_1,table.dataTable.order-column.hover tbody tr.even:hover.highlight>.sorting_1{background-color:#eae6bb}table.dataTable.display tbody tr:hover.highlight>.sorting_2,table.dataTable.display tbody tr.odd:hover.highlight>.sorting_2,table.dataTable.display tbody tr.even:hover.highlight>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.highlight>.sorting_2,table.dataTable.order-column.hover tbody tr.odd:hover.highlight>.sorting_2,table.dataTable.order-column.hover tbody tr.even:hover.highlight>.sorting_2{background-color:#ece8bd}table.dataTable.display tbody tr:hover.highlight>.sorting_3,table.dataTable.display tbody tr.odd:hover.highlight>.sorting_3,table.dataTable.display tbody tr.even:hover.highlight>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.highlight>.sorting_3,table.dataTable.order-column.hover tbody tr.odd:hover.highlight>.sorting_3,table.dataTable.order-column.hover tbody tr.even:hover.highlight>.sorting_3{background-color:#efebbf}div.DTE div.editor_upload{padding-top:4px}div.DTE div.editor_upload div.eu_table{display:table;width:100%}div.DTE div.editor_upload div.row{display:table-row}div.DTE div.editor_upload div.cell{display:table-cell;position:relative;width:50%;vertical-align:top}div.DTE div.editor_upload div.cell+div.cell{padding-left:10px}div.DTE div.editor_upload div.row+div.row div.cell{padding-top:10px}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{width:100%;height:2.3em;font-size:0.8em;text-align:center;line-height:1em}div.DTE div.editor_upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0}div.DTE div.editor_upload div.drop{position:relative;box-sizing:border-box;width:100%;height:100%;border:3px dashed #ccc;border-radius:6px;min-height:4em;color:#999;padding-top:3px;text-align:center}div.DTE div.editor_upload div.drop.over{border:3px dashed #111;color:#111}div.DTE div.editor_upload div.drop span{max-width:75%;font-size:0.85em;line-height:1em}div.DTE div.editor_upload div.rendered img{max-width:8em;margin:0 auto}div.DTE div.editor_upload.noDrop div.drop{display:none}div.DTE div.editor_upload.noDrop div.row.second{display:none}div.DTE div.editor_upload.noDrop div.rendered{margin-top:10px}div.DTE div.editor_upload.noClear div.clearValue button{display:none}div.DTE div.editor_upload.multi div.cell{display:block;width:100%}div.DTE div.editor_upload.multi div.cell div.drop{min-height:0;padding-bottom:5px}div.DTE div.editor_upload.multi div.clearValue{display:none}div.DTE div.editor_upload.multi ul{list-style-type:none;margin:0;padding:0}div.DTE div.editor_upload.multi ul li{position:relative;margin-top:0.5em}div.DTE div.editor_upload.multi ul li:first-child{margin-top:0}div.DTE div.editor_upload.multi ul li img{vertical-align:middle}div.DTE div.editor_upload.multi ul li button{position:absolute;width:40px;right:0;top:50%;margin-top:-1.5em}div.editor-datetime{position:absolute;background-color:white;z-index:2050;border:1px solid #ccc;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5);padding-bottom:5px}div.editor-datetime div.editor-datetime-title{text-align:center;padding:5px 0px 3px}div.editor-datetime table{border-spacing:0;margin:6px 13px}div.editor-datetime table th{font-size:0.8em;color:#777;font-weight:normal;width:14.285714286%;padding:0 0 4px 0;text-align:center}div.editor-datetime table td{font-size:0.9em;color:#444;padding:0}div.editor-datetime table td.day{text-align:right;background:#f5f5f5}div.editor-datetime table td.day.disabled{color:#aaa;background:white}div.editor-datetime table td.day.today{background-color:#ddd}div.editor-datetime table td.day.today button{font-weight:bold}div.editor-datetime table td.day.selected button{background:#4E6CA3;color:white;border-radius:2px}div.editor-datetime table td.day button:hover{background:#ff8000;color:white;border-radius:2px}div.editor-datetime table td.editor-datetime-week{font-size:0.7em}div.editor-datetime table button{width:100%;box-sizing:border-box;border:none;background:transparent;font-size:inherit;color:inherit;text-align:inherit;padding:5px 9px;cursor:pointer;margin:0}div.editor-datetime table.weekNumber th{width:12.5%}div.editor-datetime div.editor-datetime-label{position:relative;display:inline-block;height:30px;padding:5px 6px;border:1px solid transparent;box-sizing:border-box;cursor:pointer}div.editor-datetime div.editor-datetime-label:hover{border:1px solid #ddd;border-radius:2px;background-color:#f5f5f5}div.editor-datetime div.editor-datetime-label select{position:absolute;top:6px;left:0;cursor:pointer;opacity:0;-ms-filter:"alpha(opacity=0)"}div.editor-datetime div.editor-datetime-time{text-align:center}div.editor-datetime div.editor-datetime-time>span{vertical-align:middle}div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock{display:inline-block;vertical-align:middle}div.editor-datetime div.editor-datetime-iconLeft,div.editor-datetime div.editor-datetime-iconRight,div.editor-datetime div.editor-datetime-iconUp,div.editor-datetime div.editor-datetime-iconDown{width:30px;height:30px;background-position:center;background-repeat:no-repeat;opacity:0.3;overflow:hidden;box-sizing:border-box}div.editor-datetime div.editor-datetime-iconLeft:hover,div.editor-datetime div.editor-datetime-iconRight:hover,div.editor-datetime div.editor-datetime-iconUp:hover,div.editor-datetime div.editor-datetime-iconDown:hover{border:1px solid #ccc;border-radius:2px;background-color:#f0f0f0;opacity:0.6}div.editor-datetime div.editor-datetime-iconLeft button,div.editor-datetime div.editor-datetime-iconRight button,div.editor-datetime div.editor-datetime-iconUp button,div.editor-datetime div.editor-datetime-iconDown button{border:none;background:transparent;text-indent:30px;height:100%;width:100%;cursor:pointer}div.editor-datetime div.editor-datetime-iconLeft{position:absolute;top:5px;left:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}div.editor-datetime div.editor-datetime-iconRight{position:absolute;top:5px;right:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconUp{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconDown{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC")}div.editor-datetime-error{padding:0 1em;max-width:240px;font-size:11px;line-height:1.25em;text-align:center;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{position:absolute;top:17px;right:9px;height:2em;width:2em;z-index:20;font-size:12px;display:none;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}div.DTE.processing div.DTE_Processing_Indicator{display:block}div.DTE.DTE_Inline div.DTE_Processing_Indicator{top:5px;right:6px;font-size:6px}div.DTE.DTE_Bubble div.DTE_Processing_Indicator{top:10px;right:14px;font-size:8px}div.DTE div.DTE_Processing_Indicator span,div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{display:block;background:black;width:0.5em;height:1.5em;border:1px solid rgba(0,0,0,0.4);background-color:rgba(0,0,0,0.1);-webkit-animation:editorProcessing 0.9s infinite ease-in-out;animation:editorProcessing 0.9s infinite ease-in-out}div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{position:absolute;top:0;content:''}div.DTE div.DTE_Processing_Indicator:before{left:-1em;-webkit-animation-delay:-0.3s;animation-delay:-0.3s}div.DTE div.DTE_Processing_Indicator span{-webkit-animation-delay:-0.15s;animation-delay:-0.15s}div.DTE div.DTE_Processing_Indicator:after{left:1em}@-webkit-keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}@keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}} diff --git a/css/csstables/editor.foundation.css b/css/csstables/editor.foundation.css deleted file mode 100644 index 97f0cb6..0000000 --- a/css/csstables/editor.foundation.css +++ /dev/null @@ -1,703 +0,0 @@ -div.DTE div.DTE_Form_Error { - display: none; - color: #b11f1f; -} - -div.DTE_Field div.multi-value, -div.DTE_Field div.multi-restore { - display: none; - cursor: pointer; - padding: 0.75rem; -} -div.DTE_Field div.multi-value span, -div.DTE_Field div.multi-restore span { - display: block; - color: #666; - font-size: 0.8em; - line-height: 1.25em; -} -div.DTE_Field div.multi-value:hover, -div.DTE_Field div.multi-restore:hover { - background-color: #e5e5e5; -} -div.DTE_Field div.multi-restore { - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; - padding: 0.5rem 0.75rem; -} -div.DTE_Field div.label { - margin-top: 0.5rem; - margin-bottom: 0.5rem; -} -div.DTE_Field div.label:empty { - padding: 0; - margin: 0; -} -div.DTE_Field:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; -} - -div.reveal-modal button.close { - position: absolute; - top: -1.5em; - right: -2.5em; -} -div.reveal-modal button.close.close-button { - right: 1rem; - top: .5rem; - z-index: 100; -} -div.reveal-modal div.DTE_Header { - position: relative; - top: -0.5em; - font-size: 2.05556rem; - line-height: 1.4; -} -div.reveal-modal div.DTE_Form_Content { - width: 75%; - margin: 0 auto; -} -div.reveal-modal div.DTE_Footer { - position: relative; - bottom: -0.5em; - float: right; -} -div.reveal-modal div.DTE_Footer button { - margin-bottom: 0; -} - -div.DTE_Inline { - position: relative; - display: table; - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field, -div.DTE_Inline div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field { - padding: 0; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field > label, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field > label { - display: none; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input { - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; -} -div.DTE_Inline div.DTE_Field input[type="color"], -div.DTE_Inline div.DTE_Field input[type="date"], -div.DTE_Inline div.DTE_Field input[type="datetime"], -div.DTE_Inline div.DTE_Field input[type="datetime-local"], -div.DTE_Inline div.DTE_Field input[type="email"], -div.DTE_Inline div.DTE_Field input[type="month"], -div.DTE_Inline div.DTE_Field input[type="number"], -div.DTE_Inline div.DTE_Field input[type="password"], -div.DTE_Inline div.DTE_Field input[type="search"], -div.DTE_Inline div.DTE_Field input[type="tel"], -div.DTE_Inline div.DTE_Field input[type="text"], -div.DTE_Inline div.DTE_Field input[type="time"], -div.DTE_Inline div.DTE_Field input[type="url"], -div.DTE_Inline div.DTE_Field input[type="week"] { - margin: -6px 0; -} -div.DTE_Inline div.DTE_Field_Error, -div.DTE_Inline div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; -} -div.DTE_Inline div.DTE_Field_Error:empty, -div.DTE_Inline div.DTE_Form_Error:empty { - margin-top: 0; -} - -span.dtr-data div.DTE_Inline { - display: inline-table; -} - -div.DTE_Inline div.DTE_Field > div { - width: 100%; - padding: 0; -} -div.DTE_Inline div.DTE_Field input { - height: 30px; - margin-bottom: 0; -} -div.DTE_Inline div.DTE_Field div.label:empty { - display: none; -} - -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table { - display: table; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form { - display: table-cell; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { - padding: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field:last-child { - margin-bottom: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Form_Info, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); -} -div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url("../images/ajax-loader-small.gif") no-repeat top left; -} -div.DTE_Bubble.below div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; -} -div.DTE_Bubble.below div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTE_Bubble_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTE_Bubble_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -div.DTE_Bubble div.DTE_Bubble_Liner { - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - border-radius: 6px; - border: 1px solid rgba(0, 0, 0, 0.2); -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field > div { - width: 100%; - float: none; - clear: both; - text-align: left; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label { - padding-bottom: 4px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - font-size: 14px; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - margin-top: -2px; - display: block; -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 10; - background-color: rgba(0, 0, 0, 0.05); -} - -div.DTE div.editor_upload { - padding-top: 4px; -} -div.DTE div.editor_upload div.eu_table { - display: table; - width: 100%; -} -div.DTE div.editor_upload div.row { - display: table-row; -} -div.DTE div.editor_upload div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; -} -div.DTE div.editor_upload div.cell + div.cell { - padding-left: 10px; -} -div.DTE div.editor_upload div.row + div.row div.cell { - padding-top: 10px; -} -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; -} -div.DTE div.editor_upload input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; -} -div.DTE div.editor_upload div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; -} -div.DTE div.editor_upload div.drop.over { - border: 3px dashed #111; - color: #111; -} -div.DTE div.editor_upload div.drop span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; -} -div.DTE div.editor_upload div.rendered img { - max-width: 8em; - margin: 0 auto; -} -div.DTE div.editor_upload.noDrop div.drop { - display: none; -} -div.DTE div.editor_upload.noDrop div.row.second { - display: none; -} -div.DTE div.editor_upload.noDrop div.rendered { - margin-top: 10px; -} -div.DTE div.editor_upload.noClear div.clearValue button { - display: none; -} -div.DTE div.editor_upload.multi div.cell { - display: block; - width: 100%; -} -div.DTE div.editor_upload.multi div.cell div.drop { - min-height: 0; - padding-bottom: 5px; -} -div.DTE div.editor_upload.multi div.clearValue { - display: none; -} -div.DTE div.editor_upload.multi ul { - list-style-type: none; - margin: 0; - padding: 0; -} -div.DTE div.editor_upload.multi ul li { - position: relative; - margin-top: 0.5em; -} -div.DTE div.editor_upload.multi ul li:first-child { - margin-top: 0; -} -div.DTE div.editor_upload.multi ul li img { - vertical-align: middle; -} -div.DTE div.editor_upload.multi ul li button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; -} - -div.DTE div.editor_upload button.button, -div.DTE div.editor_upload input[type=file] { - width: 100%; - font-size: 0.8em; - margin-bottom: 0; -} -div.DTE div.editor_upload ul li button { - width: 63px; -} - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5); - padding-bottom: 5px; -} -div.editor-datetime div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; -} -div.editor-datetime table { - border-spacing: 0; - margin: 6px 13px; -} -div.editor-datetime table th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; -} -div.editor-datetime table td { - font-size: 0.9em; - color: #444; - padding: 0; -} -div.editor-datetime table td.day { - text-align: right; - background: #f5f5f5; -} -div.editor-datetime table td.day.disabled { - color: #aaa; - background: white; -} -div.editor-datetime table td.day.today { - background-color: #ddd; -} -div.editor-datetime table td.day.today button { - font-weight: bold; -} -div.editor-datetime table td.day.selected button { - background: #008CBA; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.day button:hover { - background: #ff8000; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.editor-datetime-week { - font-size: 0.7em; -} -div.editor-datetime table button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; -} -div.editor-datetime table.weekNumber th { - width: 12.5%; -} -div.editor-datetime div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-label:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; -} -div.editor-datetime div.editor-datetime-label select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; -} -div.editor-datetime div.editor-datetime-time { - text-align: center; -} -div.editor-datetime div.editor-datetime-time > span { - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-iconLeft, -div.editor-datetime div.editor-datetime-iconRight, -div.editor-datetime div.editor-datetime-iconUp, -div.editor-datetime div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; -} -div.editor-datetime div.editor-datetime-iconLeft:hover, -div.editor-datetime div.editor-datetime-iconRight:hover, -div.editor-datetime div.editor-datetime-iconUp:hover, -div.editor-datetime div.editor-datetime-iconDown:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; -} -div.editor-datetime div.editor-datetime-iconLeft button, -div.editor-datetime div.editor-datetime-iconRight button, -div.editor-datetime div.editor-datetime-iconUp button, -div.editor-datetime div.editor-datetime-iconDown button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=="); -} -div.editor-datetime div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconUp { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconDown { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC"); -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} - -div.DTE div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -div.DTE.processing div.DTE_Processing_Indicator { - display: block; -} -div.DTE.DTE_Inline div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; -} -div.DTE.DTE_Bubble div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; -} -div.DTE div.DTE_Processing_Indicator span, -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.1); - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; -} -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; -} -div.DTE div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -div.DTE div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; -} -div.DTE div.DTE_Processing_Indicator:after { - left: 1em; -} -@-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -@keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -div.DTE div.DTE_Processing_Indicator { - top: 26px; - right: 95px; -} diff --git a/css/csstables/editor.foundation.min.css b/css/csstables/editor.foundation.min.css deleted file mode 100644 index 1a83836..0000000 --- a/css/csstables/editor.foundation.min.css +++ /dev/null @@ -1,5 +0,0 @@ -div.DTE div.DTE_Form_Error{display:none;color:#b11f1f}div.DTE_Field div.multi-value,div.DTE_Field div.multi-restore{display:none;cursor:pointer;padding:0.75rem}div.DTE_Field div.multi-value span,div.DTE_Field div.multi-restore span{display:block;color:#666;font-size:0.8em;line-height:1.25em}div.DTE_Field div.multi-value:hover,div.DTE_Field div.multi-restore:hover{background-color:#e5e5e5}div.DTE_Field div.multi-restore{margin-top:0.5em;font-size:0.8em;line-height:1.25em;padding:0.5rem 0.75rem}div.DTE_Field div.label{margin-top:0.5rem;margin-bottom:0.5rem}div.DTE_Field div.label:empty{padding:0;margin:0}div.DTE_Field:after{display:block;content:".";height:0;line-height:0;clear:both;visibility:hidden}div.reveal-modal button.close{position:absolute;top:-1.5em;right:-2.5em}div.reveal-modal button.close.close-button{right:1rem;top:.5rem;z-index:100}div.reveal-modal div.DTE_Header{position:relative;top:-0.5em;font-size:2.05556rem;line-height:1.4}div.reveal-modal div.DTE_Form_Content{width:75%;margin:0 auto}div.reveal-modal div.DTE_Footer{position:relative;bottom:-0.5em;float:right}div.reveal-modal div.DTE_Footer button{margin-bottom:0}div.DTE_Inline{position:relative;display:table;width:100%}div.DTE_Inline div.DTE_Inline_Field,div.DTE_Inline div.DTE_Inline_Buttons{display:table-cell;vertical-align:middle}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field{padding:0}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field>label,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field>label{display:none}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input{width:100%}div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button{margin:-6px 0 -6px 4px;padding:5px}div.DTE_Inline div.DTE_Field input[type="color"],div.DTE_Inline div.DTE_Field input[type="date"],div.DTE_Inline div.DTE_Field input[type="datetime"],div.DTE_Inline div.DTE_Field input[type="datetime-local"],div.DTE_Inline div.DTE_Field input[type="email"],div.DTE_Inline div.DTE_Field input[type="month"],div.DTE_Inline div.DTE_Field input[type="number"],div.DTE_Inline div.DTE_Field input[type="password"],div.DTE_Inline div.DTE_Field input[type="search"],div.DTE_Inline div.DTE_Field input[type="tel"],div.DTE_Inline div.DTE_Field input[type="text"],div.DTE_Inline div.DTE_Field input[type="time"],div.DTE_Inline div.DTE_Field input[type="url"],div.DTE_Inline div.DTE_Field input[type="week"]{margin:-6px 0}div.DTE_Inline div.DTE_Field_Error,div.DTE_Inline div.DTE_Form_Error{font-size:11px;line-height:1.2em;padding:0;margin-top:10px}div.DTE_Inline div.DTE_Field_Error:empty,div.DTE_Inline div.DTE_Form_Error:empty{margin-top:0}span.dtr-data div.DTE_Inline{display:inline-table}div.DTE_Inline div.DTE_Field>div{width:100%;padding:0}div.DTE_Inline div.DTE_Field input{height:30px;margin-bottom:0}div.DTE_Inline div.DTE_Field div.label:empty{display:none}div.DTE_Bubble{position:absolute;z-index:11;margin-top:-6px;opacity:0}div.DTE_Bubble div.DTE_Bubble_Liner{position:absolute;bottom:0;border:1px solid black;width:300px;margin-left:-150px;background-color:white;box-shadow:2px 2px 7px #555;border-radius:5px;border:2px solid #444;padding:1em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table{display:table;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form{display:table-cell}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content{padding:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{position:relative;zoom:1;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field:last-child{margin-bottom:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons{display:table-cell;vertical-align:bottom;padding:0 0 0 0.75em;width:1%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{border-top-left-radius:5px;border-top-right-radius:5px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Form_Info,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Bubble_Table{padding-top:42px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error{float:none;display:none;padding:0;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTE_Bubble div.DTE_Bubble_Triangle{position:absolute;height:10px;width:10px;top:-6px;background-color:white;border:2px solid #444;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after{position:absolute;content:' ';display:block;top:12px;right:18px;height:12px;width:17px;background:url("../images/ajax-loader-small.gif") no-repeat top left}div.DTE_Bubble.below div.DTE_Bubble_Liner{top:10px;bottom:auto}div.DTE_Bubble.below div.DTE_Bubble_Triangle{top:4px;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTE_Bubble_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTE_Bubble_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}div.DTE_Bubble div.DTE_Bubble_Liner{box-shadow:0 5px 10px rgba(0,0,0,0.2);border-radius:6px;border:1px solid rgba(0,0,0,0.2)}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field>div{width:100%;float:none;clear:both;text-align:left}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label{padding-bottom:4px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{background-color:#f7f7f7;border-bottom:1px solid #ebebeb;font-size:14px;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{margin-top:-2px;display:block}div.DTE_Bubble_Background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:10;background-color:rgba(0,0,0,0.05)}div.DTE div.editor_upload{padding-top:4px}div.DTE div.editor_upload div.eu_table{display:table;width:100%}div.DTE div.editor_upload div.row{display:table-row}div.DTE div.editor_upload div.cell{display:table-cell;position:relative;width:50%;vertical-align:top}div.DTE div.editor_upload div.cell+div.cell{padding-left:10px}div.DTE div.editor_upload div.row+div.row div.cell{padding-top:10px}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{width:100%;height:2.3em;font-size:0.8em;text-align:center;line-height:1em}div.DTE div.editor_upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0}div.DTE div.editor_upload div.drop{position:relative;box-sizing:border-box;width:100%;height:100%;border:3px dashed #ccc;border-radius:6px;min-height:4em;color:#999;padding-top:3px;text-align:center}div.DTE div.editor_upload div.drop.over{border:3px dashed #111;color:#111}div.DTE div.editor_upload div.drop span{max-width:75%;font-size:0.85em;line-height:1em}div.DTE div.editor_upload div.rendered img{max-width:8em;margin:0 auto}div.DTE div.editor_upload.noDrop div.drop{display:none}div.DTE div.editor_upload.noDrop div.row.second{display:none}div.DTE div.editor_upload.noDrop div.rendered{margin-top:10px}div.DTE div.editor_upload.noClear div.clearValue button{display:none}div.DTE div.editor_upload.multi div.cell{display:block;width:100%}div.DTE div.editor_upload.multi div.cell div.drop{min-height:0;padding-bottom:5px}div.DTE div.editor_upload.multi div.clearValue{display:none}div.DTE div.editor_upload.multi ul{list-style-type:none;margin:0;padding:0}div.DTE div.editor_upload.multi ul li{position:relative;margin-top:0.5em}div.DTE div.editor_upload.multi ul li:first-child{margin-top:0}div.DTE div.editor_upload.multi ul li img{vertical-align:middle}div.DTE div.editor_upload.multi ul li button{position:absolute;width:40px;right:0;top:50%;margin-top:-1.5em}div.DTE div.editor_upload button.button,div.DTE div.editor_upload input[type=file]{width:100%;font-size:0.8em;margin-bottom:0}div.DTE div.editor_upload ul li button{width:63px}div.editor-datetime{position:absolute;background-color:white;z-index:2050;border:1px solid #ccc;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5);padding-bottom:5px}div.editor-datetime div.editor-datetime-title{text-align:center;padding:5px 0px 3px}div.editor-datetime table{border-spacing:0;margin:6px 13px}div.editor-datetime table th{font-size:0.8em;color:#777;font-weight:normal;width:14.285714286%;padding:0 0 4px 0;text-align:center}div.editor-datetime table td{font-size:0.9em;color:#444;padding:0}div.editor-datetime table td.day{text-align:right;background:#f5f5f5}div.editor-datetime table td.day.disabled{color:#aaa;background:white}div.editor-datetime table td.day.today{background-color:#ddd}div.editor-datetime table td.day.today button{font-weight:bold}div.editor-datetime table td.day.selected button{background:#008CBA;color:white;border-radius:2px}div.editor-datetime table td.day button:hover{background:#ff8000;color:white;border-radius:2px}div.editor-datetime table td.editor-datetime-week{font-size:0.7em}div.editor-datetime table button{width:100%;box-sizing:border-box;border:none;background:transparent;font-size:inherit;color:inherit;text-align:inherit;padding:5px 9px;cursor:pointer;margin:0}div.editor-datetime table.weekNumber th{width:12.5%}div.editor-datetime div.editor-datetime-label{position:relative;display:inline-block;height:30px;padding:5px 6px;border:1px solid transparent;box-sizing:border-box;cursor:pointer}div.editor-datetime div.editor-datetime-label:hover{border:1px solid #ddd;border-radius:2px;background-color:#f5f5f5}div.editor-datetime div.editor-datetime-label select{position:absolute;top:6px;left:0;cursor:pointer;opacity:0;-ms-filter:"alpha(opacity=0)"}div.editor-datetime div.editor-datetime-time{text-align:center}div.editor-datetime div.editor-datetime-time>span{vertical-align:middle}div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock{display:inline-block;vertical-align:middle}div.editor-datetime div.editor-datetime-iconLeft,div.editor-datetime div.editor-datetime-iconRight,div.editor-datetime div.editor-datetime-iconUp,div.editor-datetime div.editor-datetime-iconDown{width:30px;height:30px;background-position:center;background-repeat:no-repeat;opacity:0.3;overflow:hidden;box-sizing:border-box}div.editor-datetime div.editor-datetime-iconLeft:hover,div.editor-datetime div.editor-datetime-iconRight:hover,div.editor-datetime div.editor-datetime-iconUp:hover,div.editor-datetime div.editor-datetime-iconDown:hover{border:1px solid #ccc;border-radius:2px;background-color:#f0f0f0;opacity:0.6}div.editor-datetime div.editor-datetime-iconLeft button,div.editor-datetime div.editor-datetime-iconRight button,div.editor-datetime div.editor-datetime-iconUp button,div.editor-datetime div.editor-datetime-iconDown button{border:none;background:transparent;text-indent:30px;height:100%;width:100%;cursor:pointer}div.editor-datetime div.editor-datetime-iconLeft{position:absolute;top:5px;left:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}div.editor-datetime div.editor-datetime-iconRight{position:absolute;top:5px;right:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconUp{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconDown{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC")}div.editor-datetime-error{padding:0 1em;max-width:240px;font-size:11px;line-height:1.25em;text-align:center;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{position:absolute;top:17px;right:9px;height:2em;width:2em;z-index:20;font-size:12px;display:none;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}div.DTE.processing div.DTE_Processing_Indicator{display:block}div.DTE.DTE_Inline div.DTE_Processing_Indicator{top:5px;right:6px;font-size:6px}div.DTE.DTE_Bubble div.DTE_Processing_Indicator{top:10px;right:14px;font-size:8px}div.DTE div.DTE_Processing_Indicator span,div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{display:block;background:black;width:0.5em;height:1.5em;border:1px solid rgba(0,0,0,0.4);background-color:rgba(0,0,0,0.1);-webkit-animation:editorProcessing 0.9s infinite ease-in-out;animation:editorProcessing 0.9s infinite ease-in-out}div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{position:absolute;top:0;content:''}div.DTE div.DTE_Processing_Indicator:before{left:-1em;-webkit-animation-delay:-0.3s;animation-delay:-0.3s}div.DTE div.DTE_Processing_Indicator span{-webkit-animation-delay:-0.15s;animation-delay:-0.15s}div.DTE div.DTE_Processing_Indicator:after{left:1em}@-webkit-keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}@keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}div.DTE div.DTE_Processing_Indicator{top:26px;right:95px} diff --git a/css/csstables/editor.jqueryui.css b/css/csstables/editor.jqueryui.css deleted file mode 100644 index 654bd7e..0000000 --- a/css/csstables/editor.jqueryui.css +++ /dev/null @@ -1,919 +0,0 @@ -div.DTE_Field input, -div.DTE_Field textarea { - box-sizing: border-box; - background-color: white; - -webkit-transition: background-color ease-in-out .15s; - transition: background-color ease-in-out .15s; -} -div.DTE_Field input:focus, -div.DTE_Field textarea:focus { - background-color: #ffffee; -} -div.DTE_Field input[type="color"], -div.DTE_Field input[type="date"], -div.DTE_Field input[type="datetime"], -div.DTE_Field input[type="datetime-local"], -div.DTE_Field input[type="email"], -div.DTE_Field input[type="month"], -div.DTE_Field input[type="number"], -div.DTE_Field input[type="password"], -div.DTE_Field input[type="search"], -div.DTE_Field input[type="tel"], -div.DTE_Field input[type="text"], -div.DTE_Field input[type="time"], -div.DTE_Field input[type="url"], -div.DTE_Field input[type="week"] { - padding: 6px 4px; - width: 100%; -} -div.DTE_Field label div.DTE_Label_Info { - font-size: 0.85em; - margin-top: 0.25em; -} -div.DTE_Field label div.DTE_Label_Info:empty { - margin-top: 0; -} -div.DTE_Field div.DTE_Field_Info, -div.DTE_Field div.DTE_Field_Message, -div.DTE_Field div.DTE_Field_Error { - font-size: 11px; - line-height: 1em; - margin-top: 5px; -} -div.DTE_Field div.DTE_Field_Info:empty, -div.DTE_Field div.DTE_Field_Message:empty, -div.DTE_Field div.DTE_Field_Error:empty { - margin-top: 0; -} -div.DTE_Field div.DTE_Field_Error { - display: none; - color: #b11f1f; -} -div.DTE_Field div.multi-value { - display: none; - border: 1px dotted #666; - border-radius: 3px; - padding: 5px; - background-color: #fafafa; - cursor: pointer; -} -div.DTE_Field div.multi-value span { - font-size: 0.8em; - line-height: 1.25em; - display: block; - color: #666; -} -div.DTE_Field div.multi-value.multi-noEdit { - border: 1px solid #ccc; - cursor: auto; - background-color: #fcfcfc; -} -div.DTE_Field div.multi-value.multi-noEdit:hover { - background-color: #fcfcfc; -} -div.DTE_Field div.multi-value:hover { - background-color: #f1f1f1; -} -div.DTE_Field.disabled { - color: grey; -} -div.DTE_Field.disabled div.multi-value { - cursor: default; - border: 1px dotted #aaa; - background-color: transparent; -} -div.DTE_Field div.multi-restore { - display: none; - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; - color: #3879d9; -} -div.DTE_Field div.multi-restore:hover { - text-decoration: underline; - cursor: pointer; -} - -div.DTE_Field_Type_textarea textarea { - padding: 3px; - width: 100%; - height: 80px; -} - -div.DTE_Field.DTE_Field_Type_date img { - vertical-align: middle; - cursor: pointer; - *cursor: hand; -} -div.DTE_Field.DTE_Field_Type_date input.jqueryui { - width: 87%; - margin-right: 6px; -} - -div.DTE_Field_Type_checkbox div.DTE_Field_Input > div > div, -div.DTE_Field_Type_radio div.DTE_Field_Input > div > div { - margin-bottom: 0.25em; -} -div.DTE_Field_Type_checkbox div.DTE_Field_Input > div > div:last-child, -div.DTE_Field_Type_radio div.DTE_Field_Input > div > div:last-child { - margin-bottom: 0; -} -div.DTE_Field_Type_checkbox div.DTE_Field_Input > div > div label, -div.DTE_Field_Type_radio div.DTE_Field_Input > div > div label { - margin-left: 0.75em; - vertical-align: middle; -} - -div.DTE_Field_Type_select div.DTE_Field_Input { - padding-top: 4px; -} - -div.DTE_Body { - padding: 50px 0; -} -div.DTE_Body div.DTE_Body_Content { - position: relative; - overflow: auto; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Form_Info { - padding: 1em 1em 0 1em; - margin: 0; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field { - position: relative; - zoom: 1; - clear: both; - padding: 5px 20%; - border: 1px solid transparent; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field:hover { - background-color: #f9f9f9; - border: 1px solid #f3f3f3; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field > label { - float: left; - width: 40%; - padding-top: 6px; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field > div.DTE_Field_Input { - float: right; - width: 60%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.full { - padding: 5px 0 5px 20%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label { - width: 30%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input { - width: 70%; -} -div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - float: none; - clear: both; - width: 100%; -} - -html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field > label { - float: right; -} -html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field > div.DTE_Field_Input { - float: left; -} -html[dir="rtl"] div.DTE div.DTE_Form_Buttons button { - float: left; -} - -@media only screen and (max-width: 768px) { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - padding: 5px 10%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full { - padding: 5px 0 5px 10%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label { - width: 35.5%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input { - width: 64.5%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - width: 100%; - } -} -@media only screen and (max-width: 640px) { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - padding: 5px 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full { - padding: 5px 0%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label { - width: 40%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input { - width: 60%; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - width: 100%; - } -} -@media only screen and (max-width: 580px) { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - position: relative; - zoom: 1; - clear: both; - padding: 5px 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field > label { - float: none; - width: auto; - padding-top: 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field > div.DTE_Field_Input { - float: none; - width: auto; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full, div.DTE_Body div.DTE_Body_Content div.DTE_Field.block { - padding: 5px 0; - } - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > label, - div.DTE_Body div.DTE_Body_Content div.DTE_Field.full > div.DTE_Field_Input, div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > label, - div.DTE_Body div.DTE_Body_Content div.DTE_Field.block > div.DTE_Field_Input { - width: 100%; - } -} -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table { - display: table; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form { - display: table-cell; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { - padding: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field:last-child { - margin-bottom: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Form_Info, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); -} -div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url("../images/ajax-loader-small.gif") no-repeat top left; -} -div.DTE_Bubble.below div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; -} -div.DTE_Bubble.below div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTE_Bubble_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTE_Bubble_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -div.DTE_Inline { - position: relative; - display: table; - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field, -div.DTE_Inline div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field { - padding: 0; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field > label, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field > label { - display: none; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input { - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; -} -div.DTE_Inline div.DTE_Field input[type="color"], -div.DTE_Inline div.DTE_Field input[type="date"], -div.DTE_Inline div.DTE_Field input[type="datetime"], -div.DTE_Inline div.DTE_Field input[type="datetime-local"], -div.DTE_Inline div.DTE_Field input[type="email"], -div.DTE_Inline div.DTE_Field input[type="month"], -div.DTE_Inline div.DTE_Field input[type="number"], -div.DTE_Inline div.DTE_Field input[type="password"], -div.DTE_Inline div.DTE_Field input[type="search"], -div.DTE_Inline div.DTE_Field input[type="tel"], -div.DTE_Inline div.DTE_Field input[type="text"], -div.DTE_Inline div.DTE_Field input[type="time"], -div.DTE_Inline div.DTE_Field input[type="url"], -div.DTE_Inline div.DTE_Field input[type="week"] { - margin: -6px 0; -} -div.DTE_Inline div.DTE_Field_Error, -div.DTE_Inline div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; -} -div.DTE_Inline div.DTE_Field_Error:empty, -div.DTE_Inline div.DTE_Form_Error:empty { - margin-top: 0; -} - -span.dtr-data div.DTE_Inline { - display: inline-table; -} - -table.dataTable tbody tr.highlight { - background-color: #FFFBCC !important; -} -table.dataTable tbody tr.highlight, -table.dataTable tbody tr.noHighlight, -table.dataTable tbody tr.highlight td, -table.dataTable tbody tr.noHighlight td { - -webkit-transition: background-color 500ms linear; - -moz-transition: background-color 500ms linear; - -ms-transition: background-color 500ms linear; - -o-transition: background-color 500ms linear; - transition: background-color 500ms linear; -} -table.dataTable.stripe tbody tr.odd.highlight, table.dataTable.display tbody tr.odd.highlight { - background-color: #f9f5c7; -} -table.dataTable.hover tbody tr:hover.highlight, -table.dataTable.hover tbody tr.odd:hover.highlight, -table.dataTable.hover tbody tr.even:hover.highlight, table.dataTable.display tbody tr:hover.highlight, -table.dataTable.display tbody tr.odd:hover.highlight, -table.dataTable.display tbody tr.even:hover.highlight { - background-color: #f6f2c5; -} -table.dataTable.order-column tbody tr.highlight > .sorting_1, -table.dataTable.order-column tbody tr.highlight > .sorting_2, -table.dataTable.order-column tbody tr.highlight > .sorting_3, table.dataTable.display tbody tr.highlight > .sorting_1, -table.dataTable.display tbody tr.highlight > .sorting_2, -table.dataTable.display tbody tr.highlight > .sorting_3 { - background-color: #faf6c8; -} -table.dataTable.display tbody tr.odd.highlight > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.highlight > .sorting_1 { - background-color: #f1edc1; -} -table.dataTable.display tbody tr.odd.highlight > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.highlight > .sorting_2 { - background-color: #f3efc2; -} -table.dataTable.display tbody tr.odd.highlight > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.highlight > .sorting_3 { - background-color: #f5f1c4; -} -table.dataTable.display tbody tr.even.highlight > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.highlight > .sorting_1 { - background-color: #faf6c8; -} -table.dataTable.display tbody tr.even.highlight > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.highlight > .sorting_2 { - background-color: #fcf8ca; -} -table.dataTable.display tbody tr.even.highlight > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.highlight > .sorting_3 { - background-color: #fefacb; -} -table.dataTable.display tbody tr:hover.highlight > .sorting_1, -table.dataTable.display tbody tr.odd:hover.highlight > .sorting_1, -table.dataTable.display tbody tr.even:hover.highlight > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.highlight > .sorting_1, -table.dataTable.order-column.hover tbody tr.odd:hover.highlight > .sorting_1, -table.dataTable.order-column.hover tbody tr.even:hover.highlight > .sorting_1 { - background-color: #eae6bb; -} -table.dataTable.display tbody tr:hover.highlight > .sorting_2, -table.dataTable.display tbody tr.odd:hover.highlight > .sorting_2, -table.dataTable.display tbody tr.even:hover.highlight > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.highlight > .sorting_2, -table.dataTable.order-column.hover tbody tr.odd:hover.highlight > .sorting_2, -table.dataTable.order-column.hover tbody tr.even:hover.highlight > .sorting_2 { - background-color: #ece8bd; -} -table.dataTable.display tbody tr:hover.highlight > .sorting_3, -table.dataTable.display tbody tr.odd:hover.highlight > .sorting_3, -table.dataTable.display tbody tr.even:hover.highlight > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.highlight > .sorting_3, -table.dataTable.order-column.hover tbody tr.odd:hover.highlight > .sorting_3, -table.dataTable.order-column.hover tbody tr.even:hover.highlight > .sorting_3 { - background-color: #efebbf; -} - -div.DTE div.editor_upload { - padding-top: 4px; -} -div.DTE div.editor_upload div.eu_table { - display: table; - width: 100%; -} -div.DTE div.editor_upload div.row { - display: table-row; -} -div.DTE div.editor_upload div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; -} -div.DTE div.editor_upload div.cell + div.cell { - padding-left: 10px; -} -div.DTE div.editor_upload div.row + div.row div.cell { - padding-top: 10px; -} -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; -} -div.DTE div.editor_upload input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; -} -div.DTE div.editor_upload div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; -} -div.DTE div.editor_upload div.drop.over { - border: 3px dashed #111; - color: #111; -} -div.DTE div.editor_upload div.drop span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; -} -div.DTE div.editor_upload div.rendered img { - max-width: 8em; - margin: 0 auto; -} -div.DTE div.editor_upload.noDrop div.drop { - display: none; -} -div.DTE div.editor_upload.noDrop div.row.second { - display: none; -} -div.DTE div.editor_upload.noDrop div.rendered { - margin-top: 10px; -} -div.DTE div.editor_upload.noClear div.clearValue button { - display: none; -} -div.DTE div.editor_upload.multi div.cell { - display: block; - width: 100%; -} -div.DTE div.editor_upload.multi div.cell div.drop { - min-height: 0; - padding-bottom: 5px; -} -div.DTE div.editor_upload.multi div.clearValue { - display: none; -} -div.DTE div.editor_upload.multi ul { - list-style-type: none; - margin: 0; - padding: 0; -} -div.DTE div.editor_upload.multi ul li { - position: relative; - margin-top: 0.5em; -} -div.DTE div.editor_upload.multi ul li:first-child { - margin-top: 0; -} -div.DTE div.editor_upload.multi ul li img { - vertical-align: middle; -} -div.DTE div.editor_upload.multi ul li button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; -} - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5); - padding-bottom: 5px; -} -div.editor-datetime div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; -} -div.editor-datetime table { - border-spacing: 0; - margin: 6px 13px; -} -div.editor-datetime table th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; -} -div.editor-datetime table td { - font-size: 0.9em; - color: #444; - padding: 0; -} -div.editor-datetime table td.day { - text-align: right; - background: #f5f5f5; -} -div.editor-datetime table td.day.disabled { - color: #aaa; - background: white; -} -div.editor-datetime table td.day.today { - background-color: #ddd; -} -div.editor-datetime table td.day.today button { - font-weight: bold; -} -div.editor-datetime table td.day.selected button { - background: #4E6CA3; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.day button:hover { - background: #ff8000; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.editor-datetime-week { - font-size: 0.7em; -} -div.editor-datetime table button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; -} -div.editor-datetime table.weekNumber th { - width: 12.5%; -} -div.editor-datetime div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-label:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; -} -div.editor-datetime div.editor-datetime-label select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; -} -div.editor-datetime div.editor-datetime-time { - text-align: center; -} -div.editor-datetime div.editor-datetime-time > span { - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-iconLeft, -div.editor-datetime div.editor-datetime-iconRight, -div.editor-datetime div.editor-datetime-iconUp, -div.editor-datetime div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; -} -div.editor-datetime div.editor-datetime-iconLeft:hover, -div.editor-datetime div.editor-datetime-iconRight:hover, -div.editor-datetime div.editor-datetime-iconUp:hover, -div.editor-datetime div.editor-datetime-iconDown:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; -} -div.editor-datetime div.editor-datetime-iconLeft button, -div.editor-datetime div.editor-datetime-iconRight button, -div.editor-datetime div.editor-datetime-iconUp button, -div.editor-datetime div.editor-datetime-iconDown button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=="); -} -div.editor-datetime div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconUp { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconDown { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC"); -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} - -div.DTE div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -div.DTE.processing div.DTE_Processing_Indicator { - display: block; -} -div.DTE.DTE_Inline div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; -} -div.DTE.DTE_Bubble div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; -} -div.DTE div.DTE_Processing_Indicator span, -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.1); - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; -} -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; -} -div.DTE div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -div.DTE div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; -} -div.DTE div.DTE_Processing_Indicator:after { - left: 1em; -} -@-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -@keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -div.DTE { - font-size: 0.91em; -} -div.DTE div.DTE_Header { - display: none; -} -div.DTE div.DTE_Body { - padding: 0; -} -div.DTE div.DTE_Body div.DTE_Body_Content { - overflow: hidden; -} -div.DTE div.DTE_Body div.DTE_Body_Content div.DTE_Field { - padding: 5px 5%; -} -div.DTE div.DTE_Footer { - display: none; -} -div.DTE div.DTE_Form_Error { - padding-top: 1em; - color: red; - display: none; - color: #b11f1f; -} -div.DTE div.DTE_Processing_Indicator { - top: 12px; - right: 2px; - font-size: 0.8em; -} diff --git a/css/csstables/editor.jqueryui.min.css b/css/csstables/editor.jqueryui.min.css deleted file mode 100644 index e3fa43d..0000000 --- a/css/csstables/editor.jqueryui.min.css +++ /dev/null @@ -1,5 +0,0 @@ -div.DTE_Field input,div.DTE_Field textarea{box-sizing:border-box;background-color:white;-webkit-transition:background-color ease-in-out .15s;transition:background-color ease-in-out .15s}div.DTE_Field input:focus,div.DTE_Field textarea:focus{background-color:#ffffee}div.DTE_Field input[type="color"],div.DTE_Field input[type="date"],div.DTE_Field input[type="datetime"],div.DTE_Field input[type="datetime-local"],div.DTE_Field input[type="email"],div.DTE_Field input[type="month"],div.DTE_Field input[type="number"],div.DTE_Field input[type="password"],div.DTE_Field input[type="search"],div.DTE_Field input[type="tel"],div.DTE_Field input[type="text"],div.DTE_Field input[type="time"],div.DTE_Field input[type="url"],div.DTE_Field input[type="week"]{padding:6px 4px;width:100%}div.DTE_Field label div.DTE_Label_Info{font-size:0.85em;margin-top:0.25em}div.DTE_Field label div.DTE_Label_Info:empty{margin-top:0}div.DTE_Field div.DTE_Field_Info,div.DTE_Field div.DTE_Field_Message,div.DTE_Field div.DTE_Field_Error{font-size:11px;line-height:1em;margin-top:5px}div.DTE_Field div.DTE_Field_Info:empty,div.DTE_Field div.DTE_Field_Message:empty,div.DTE_Field div.DTE_Field_Error:empty{margin-top:0}div.DTE_Field div.DTE_Field_Error{display:none;color:#b11f1f}div.DTE_Field div.multi-value{display:none;border:1px dotted #666;border-radius:3px;padding:5px;background-color:#fafafa;cursor:pointer}div.DTE_Field div.multi-value span{font-size:0.8em;line-height:1.25em;display:block;color:#666}div.DTE_Field div.multi-value.multi-noEdit{border:1px solid #ccc;cursor:auto;background-color:#fcfcfc}div.DTE_Field div.multi-value.multi-noEdit:hover{background-color:#fcfcfc}div.DTE_Field div.multi-value:hover{background-color:#f1f1f1}div.DTE_Field.disabled{color:grey}div.DTE_Field.disabled div.multi-value{cursor:default;border:1px dotted #aaa;background-color:transparent}div.DTE_Field div.multi-restore{display:none;margin-top:0.5em;font-size:0.8em;line-height:1.25em;color:#3879d9}div.DTE_Field div.multi-restore:hover{text-decoration:underline;cursor:pointer}div.DTE_Field_Type_textarea textarea{padding:3px;width:100%;height:80px}div.DTE_Field.DTE_Field_Type_date img{vertical-align:middle;cursor:pointer;*cursor:hand}div.DTE_Field.DTE_Field_Type_date input.jqueryui{width:87%;margin-right:6px}div.DTE_Field_Type_checkbox div.DTE_Field_Input>div>div,div.DTE_Field_Type_radio div.DTE_Field_Input>div>div{margin-bottom:0.25em}div.DTE_Field_Type_checkbox div.DTE_Field_Input>div>div:last-child,div.DTE_Field_Type_radio div.DTE_Field_Input>div>div:last-child{margin-bottom:0}div.DTE_Field_Type_checkbox div.DTE_Field_Input>div>div label,div.DTE_Field_Type_radio div.DTE_Field_Input>div>div label{margin-left:0.75em;vertical-align:middle}div.DTE_Field_Type_select div.DTE_Field_Input{padding-top:4px}div.DTE_Body{padding:50px 0}div.DTE_Body div.DTE_Body_Content{position:relative;overflow:auto}div.DTE_Body div.DTE_Body_Content div.DTE_Form_Info{padding:1em 1em 0 1em;margin:0}div.DTE_Body div.DTE_Body_Content div.DTE_Field{position:relative;zoom:1;clear:both;padding:5px 20%;border:1px solid transparent}div.DTE_Body div.DTE_Body_Content div.DTE_Field:after{display:block;content:".";height:0;line-height:0;clear:both;visibility:hidden}div.DTE_Body div.DTE_Body_Content div.DTE_Field:hover{background-color:#f9f9f9;border:1px solid #f3f3f3}div.DTE_Body div.DTE_Body_Content div.DTE_Field>label{float:left;width:40%;padding-top:6px}div.DTE_Body div.DTE_Body_Content div.DTE_Field>div.DTE_Field_Input{float:right;width:60%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full{padding:5px 0 5px 20%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label{width:30%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input{width:70%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{float:none;clear:both;width:100%}html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field>label{float:right}html[dir="rtl"] div.DTE_Body div.DTE_Body_Content div.DTE_Field>div.DTE_Field_Input{float:left}html[dir="rtl"] div.DTE div.DTE_Form_Buttons button{float:left}@media only screen and (max-width: 768px){div.DTE_Body div.DTE_Body_Content div.DTE_Field{padding:5px 10%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full{padding:5px 0 5px 10%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label{width:35.5%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input{width:64.5%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{width:100%}}@media only screen and (max-width: 640px){div.DTE_Body div.DTE_Body_Content div.DTE_Field{padding:5px 0}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full{padding:5px 0%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label{width:40%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input{width:60%}div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{width:100%}}@media only screen and (max-width: 580px){div.DTE_Body div.DTE_Body_Content div.DTE_Field{position:relative;zoom:1;clear:both;padding:5px 0}div.DTE_Body div.DTE_Body_Content div.DTE_Field>label{float:none;width:auto;padding-top:0}div.DTE_Body div.DTE_Body_Content div.DTE_Field>div.DTE_Field_Input{float:none;width:auto}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full,div.DTE_Body div.DTE_Body_Content div.DTE_Field.block{padding:5px 0}div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>label,div.DTE_Body div.DTE_Body_Content div.DTE_Field.full>div.DTE_Field_Input,div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>label,div.DTE_Body div.DTE_Body_Content div.DTE_Field.block>div.DTE_Field_Input{width:100%}}div.DTE_Bubble{position:absolute;z-index:11;margin-top:-6px;opacity:0}div.DTE_Bubble div.DTE_Bubble_Liner{position:absolute;bottom:0;border:1px solid black;width:300px;margin-left:-150px;background-color:white;box-shadow:2px 2px 7px #555;border-radius:5px;border:2px solid #444;padding:1em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table{display:table;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form{display:table-cell}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content{padding:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{position:relative;zoom:1;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field:last-child{margin-bottom:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons{display:table-cell;vertical-align:bottom;padding:0 0 0 0.75em;width:1%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{border-top-left-radius:5px;border-top-right-radius:5px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Form_Info,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Bubble_Table{padding-top:42px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error{float:none;display:none;padding:0;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTE_Bubble div.DTE_Bubble_Triangle{position:absolute;height:10px;width:10px;top:-6px;background-color:white;border:2px solid #444;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after{position:absolute;content:' ';display:block;top:12px;right:18px;height:12px;width:17px;background:url("../images/ajax-loader-small.gif") no-repeat top left}div.DTE_Bubble.below div.DTE_Bubble_Liner{top:10px;bottom:auto}div.DTE_Bubble.below div.DTE_Bubble_Triangle{top:4px;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTE_Bubble_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTE_Bubble_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}div.DTE_Inline{position:relative;display:table;width:100%}div.DTE_Inline div.DTE_Inline_Field,div.DTE_Inline div.DTE_Inline_Buttons{display:table-cell;vertical-align:middle}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field{padding:0}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field>label,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field>label{display:none}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input{width:100%}div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button{margin:-6px 0 -6px 4px;padding:5px}div.DTE_Inline div.DTE_Field input[type="color"],div.DTE_Inline div.DTE_Field input[type="date"],div.DTE_Inline div.DTE_Field input[type="datetime"],div.DTE_Inline div.DTE_Field input[type="datetime-local"],div.DTE_Inline div.DTE_Field input[type="email"],div.DTE_Inline div.DTE_Field input[type="month"],div.DTE_Inline div.DTE_Field input[type="number"],div.DTE_Inline div.DTE_Field input[type="password"],div.DTE_Inline div.DTE_Field input[type="search"],div.DTE_Inline div.DTE_Field input[type="tel"],div.DTE_Inline div.DTE_Field input[type="text"],div.DTE_Inline div.DTE_Field input[type="time"],div.DTE_Inline div.DTE_Field input[type="url"],div.DTE_Inline div.DTE_Field input[type="week"]{margin:-6px 0}div.DTE_Inline div.DTE_Field_Error,div.DTE_Inline div.DTE_Form_Error{font-size:11px;line-height:1.2em;padding:0;margin-top:10px}div.DTE_Inline div.DTE_Field_Error:empty,div.DTE_Inline div.DTE_Form_Error:empty{margin-top:0}span.dtr-data div.DTE_Inline{display:inline-table}table.dataTable tbody tr.highlight{background-color:#FFFBCC !important}table.dataTable tbody tr.highlight,table.dataTable tbody tr.noHighlight,table.dataTable tbody tr.highlight td,table.dataTable tbody tr.noHighlight td{-webkit-transition:background-color 500ms linear;-moz-transition:background-color 500ms linear;-ms-transition:background-color 500ms linear;-o-transition:background-color 500ms linear;transition:background-color 500ms linear}table.dataTable.stripe tbody tr.odd.highlight,table.dataTable.display tbody tr.odd.highlight{background-color:#f9f5c7}table.dataTable.hover tbody tr:hover.highlight,table.dataTable.hover tbody tr.odd:hover.highlight,table.dataTable.hover tbody tr.even:hover.highlight,table.dataTable.display tbody tr:hover.highlight,table.dataTable.display tbody tr.odd:hover.highlight,table.dataTable.display tbody tr.even:hover.highlight{background-color:#f6f2c5}table.dataTable.order-column tbody tr.highlight>.sorting_1,table.dataTable.order-column tbody tr.highlight>.sorting_2,table.dataTable.order-column tbody tr.highlight>.sorting_3,table.dataTable.display tbody tr.highlight>.sorting_1,table.dataTable.display tbody tr.highlight>.sorting_2,table.dataTable.display tbody tr.highlight>.sorting_3{background-color:#faf6c8}table.dataTable.display tbody tr.odd.highlight>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.highlight>.sorting_1{background-color:#f1edc1}table.dataTable.display tbody tr.odd.highlight>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.highlight>.sorting_2{background-color:#f3efc2}table.dataTable.display tbody tr.odd.highlight>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.highlight>.sorting_3{background-color:#f5f1c4}table.dataTable.display tbody tr.even.highlight>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.highlight>.sorting_1{background-color:#faf6c8}table.dataTable.display tbody tr.even.highlight>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.highlight>.sorting_2{background-color:#fcf8ca}table.dataTable.display tbody tr.even.highlight>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.highlight>.sorting_3{background-color:#fefacb}table.dataTable.display tbody tr:hover.highlight>.sorting_1,table.dataTable.display tbody tr.odd:hover.highlight>.sorting_1,table.dataTable.display tbody tr.even:hover.highlight>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.highlight>.sorting_1,table.dataTable.order-column.hover tbody tr.odd:hover.highlight>.sorting_1,table.dataTable.order-column.hover tbody tr.even:hover.highlight>.sorting_1{background-color:#eae6bb}table.dataTable.display tbody tr:hover.highlight>.sorting_2,table.dataTable.display tbody tr.odd:hover.highlight>.sorting_2,table.dataTable.display tbody tr.even:hover.highlight>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.highlight>.sorting_2,table.dataTable.order-column.hover tbody tr.odd:hover.highlight>.sorting_2,table.dataTable.order-column.hover tbody tr.even:hover.highlight>.sorting_2{background-color:#ece8bd}table.dataTable.display tbody tr:hover.highlight>.sorting_3,table.dataTable.display tbody tr.odd:hover.highlight>.sorting_3,table.dataTable.display tbody tr.even:hover.highlight>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.highlight>.sorting_3,table.dataTable.order-column.hover tbody tr.odd:hover.highlight>.sorting_3,table.dataTable.order-column.hover tbody tr.even:hover.highlight>.sorting_3{background-color:#efebbf}div.DTE div.editor_upload{padding-top:4px}div.DTE div.editor_upload div.eu_table{display:table;width:100%}div.DTE div.editor_upload div.row{display:table-row}div.DTE div.editor_upload div.cell{display:table-cell;position:relative;width:50%;vertical-align:top}div.DTE div.editor_upload div.cell+div.cell{padding-left:10px}div.DTE div.editor_upload div.row+div.row div.cell{padding-top:10px}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{width:100%;height:2.3em;font-size:0.8em;text-align:center;line-height:1em}div.DTE div.editor_upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0}div.DTE div.editor_upload div.drop{position:relative;box-sizing:border-box;width:100%;height:100%;border:3px dashed #ccc;border-radius:6px;min-height:4em;color:#999;padding-top:3px;text-align:center}div.DTE div.editor_upload div.drop.over{border:3px dashed #111;color:#111}div.DTE div.editor_upload div.drop span{max-width:75%;font-size:0.85em;line-height:1em}div.DTE div.editor_upload div.rendered img{max-width:8em;margin:0 auto}div.DTE div.editor_upload.noDrop div.drop{display:none}div.DTE div.editor_upload.noDrop div.row.second{display:none}div.DTE div.editor_upload.noDrop div.rendered{margin-top:10px}div.DTE div.editor_upload.noClear div.clearValue button{display:none}div.DTE div.editor_upload.multi div.cell{display:block;width:100%}div.DTE div.editor_upload.multi div.cell div.drop{min-height:0;padding-bottom:5px}div.DTE div.editor_upload.multi div.clearValue{display:none}div.DTE div.editor_upload.multi ul{list-style-type:none;margin:0;padding:0}div.DTE div.editor_upload.multi ul li{position:relative;margin-top:0.5em}div.DTE div.editor_upload.multi ul li:first-child{margin-top:0}div.DTE div.editor_upload.multi ul li img{vertical-align:middle}div.DTE div.editor_upload.multi ul li button{position:absolute;width:40px;right:0;top:50%;margin-top:-1.5em}div.editor-datetime{position:absolute;background-color:white;z-index:2050;border:1px solid #ccc;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5);padding-bottom:5px}div.editor-datetime div.editor-datetime-title{text-align:center;padding:5px 0px 3px}div.editor-datetime table{border-spacing:0;margin:6px 13px}div.editor-datetime table th{font-size:0.8em;color:#777;font-weight:normal;width:14.285714286%;padding:0 0 4px 0;text-align:center}div.editor-datetime table td{font-size:0.9em;color:#444;padding:0}div.editor-datetime table td.day{text-align:right;background:#f5f5f5}div.editor-datetime table td.day.disabled{color:#aaa;background:white}div.editor-datetime table td.day.today{background-color:#ddd}div.editor-datetime table td.day.today button{font-weight:bold}div.editor-datetime table td.day.selected button{background:#4E6CA3;color:white;border-radius:2px}div.editor-datetime table td.day button:hover{background:#ff8000;color:white;border-radius:2px}div.editor-datetime table td.editor-datetime-week{font-size:0.7em}div.editor-datetime table button{width:100%;box-sizing:border-box;border:none;background:transparent;font-size:inherit;color:inherit;text-align:inherit;padding:5px 9px;cursor:pointer;margin:0}div.editor-datetime table.weekNumber th{width:12.5%}div.editor-datetime div.editor-datetime-label{position:relative;display:inline-block;height:30px;padding:5px 6px;border:1px solid transparent;box-sizing:border-box;cursor:pointer}div.editor-datetime div.editor-datetime-label:hover{border:1px solid #ddd;border-radius:2px;background-color:#f5f5f5}div.editor-datetime div.editor-datetime-label select{position:absolute;top:6px;left:0;cursor:pointer;opacity:0;-ms-filter:"alpha(opacity=0)"}div.editor-datetime div.editor-datetime-time{text-align:center}div.editor-datetime div.editor-datetime-time>span{vertical-align:middle}div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock{display:inline-block;vertical-align:middle}div.editor-datetime div.editor-datetime-iconLeft,div.editor-datetime div.editor-datetime-iconRight,div.editor-datetime div.editor-datetime-iconUp,div.editor-datetime div.editor-datetime-iconDown{width:30px;height:30px;background-position:center;background-repeat:no-repeat;opacity:0.3;overflow:hidden;box-sizing:border-box}div.editor-datetime div.editor-datetime-iconLeft:hover,div.editor-datetime div.editor-datetime-iconRight:hover,div.editor-datetime div.editor-datetime-iconUp:hover,div.editor-datetime div.editor-datetime-iconDown:hover{border:1px solid #ccc;border-radius:2px;background-color:#f0f0f0;opacity:0.6}div.editor-datetime div.editor-datetime-iconLeft button,div.editor-datetime div.editor-datetime-iconRight button,div.editor-datetime div.editor-datetime-iconUp button,div.editor-datetime div.editor-datetime-iconDown button{border:none;background:transparent;text-indent:30px;height:100%;width:100%;cursor:pointer}div.editor-datetime div.editor-datetime-iconLeft{position:absolute;top:5px;left:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}div.editor-datetime div.editor-datetime-iconRight{position:absolute;top:5px;right:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconUp{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconDown{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC")}div.editor-datetime-error{padding:0 1em;max-width:240px;font-size:11px;line-height:1.25em;text-align:center;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{position:absolute;top:17px;right:9px;height:2em;width:2em;z-index:20;font-size:12px;display:none;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}div.DTE.processing div.DTE_Processing_Indicator{display:block}div.DTE.DTE_Inline div.DTE_Processing_Indicator{top:5px;right:6px;font-size:6px}div.DTE.DTE_Bubble div.DTE_Processing_Indicator{top:10px;right:14px;font-size:8px}div.DTE div.DTE_Processing_Indicator span,div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{display:block;background:black;width:0.5em;height:1.5em;border:1px solid rgba(0,0,0,0.4);background-color:rgba(0,0,0,0.1);-webkit-animation:editorProcessing 0.9s infinite ease-in-out;animation:editorProcessing 0.9s infinite ease-in-out}div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{position:absolute;top:0;content:''}div.DTE div.DTE_Processing_Indicator:before{left:-1em;-webkit-animation-delay:-0.3s;animation-delay:-0.3s}div.DTE div.DTE_Processing_Indicator span{-webkit-animation-delay:-0.15s;animation-delay:-0.15s}div.DTE div.DTE_Processing_Indicator:after{left:1em}@-webkit-keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}@keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}div.DTE{font-size:0.91em}div.DTE div.DTE_Header{display:none}div.DTE div.DTE_Body{padding:0}div.DTE div.DTE_Body div.DTE_Body_Content{overflow:hidden}div.DTE div.DTE_Body div.DTE_Body_Content div.DTE_Field{padding:5px 5%}div.DTE div.DTE_Footer{display:none}div.DTE div.DTE_Form_Error{padding-top:1em;color:red;display:none;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{top:12px;right:2px;font-size:0.8em} diff --git a/css/csstables/editor.semanticui.css b/css/csstables/editor.semanticui.css deleted file mode 100644 index c60ebf2..0000000 --- a/css/csstables/editor.semanticui.css +++ /dev/null @@ -1,680 +0,0 @@ -div.DTE div.DTE_Form_Error { - display: none; - color: #b11f1f; -} -div.DTE label { - padding-top: 9px !important; - align-self: flex-start; - justify-content: flex-end; -} -div.DTE div.eight.wide.field { - flex-direction: column; -} -div.DTE div.DTE_Field_InputControl { - width: 100%; - margin: 0 !important; -} -div.DTE div.ui.message:empty { - display: none; -} - -div.DTE_Field div.ui.message { - width: 100%; -} -div.DTE_Field div.multi-value, -div.DTE_Field div.multi-restore { - display: none; - cursor: pointer; - margin-top: 0; -} -div.DTE_Field div.multi-value span, -div.DTE_Field div.multi-restore span { - display: block; - color: #666; - font-size: 0.85em; - line-height: 1.35em; -} -div.DTE_Field div.multi-value:hover, -div.DTE_Field div.multi-restore:hover { - background-color: #f1f1f1; -} -div.DTE_Field div.multi-restore { - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; -} -div.DTE_Field:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; -} - -div.DTE_Inline { - position: relative; - display: table; - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field, -div.DTE_Inline div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field { - padding: 0; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field > label, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field > label { - display: none; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input { - width: 100%; -} -div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button, -div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; -} -div.DTE_Inline div.DTE_Field input[type="color"], -div.DTE_Inline div.DTE_Field input[type="date"], -div.DTE_Inline div.DTE_Field input[type="datetime"], -div.DTE_Inline div.DTE_Field input[type="datetime-local"], -div.DTE_Inline div.DTE_Field input[type="email"], -div.DTE_Inline div.DTE_Field input[type="month"], -div.DTE_Inline div.DTE_Field input[type="number"], -div.DTE_Inline div.DTE_Field input[type="password"], -div.DTE_Inline div.DTE_Field input[type="search"], -div.DTE_Inline div.DTE_Field input[type="tel"], -div.DTE_Inline div.DTE_Field input[type="text"], -div.DTE_Inline div.DTE_Field input[type="time"], -div.DTE_Inline div.DTE_Field input[type="url"], -div.DTE_Inline div.DTE_Field input[type="week"] { - margin: -6px 0; -} -div.DTE_Inline div.DTE_Field_Error, -div.DTE_Inline div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; -} -div.DTE_Inline div.DTE_Field_Error:empty, -div.DTE_Inline div.DTE_Form_Error:empty { - margin-top: 0; -} - -span.dtr-data div.DTE_Inline { - display: inline-table; -} - -div.DTE.DTE_Inline.ui.form label { - display: none !important; -} -div.DTE.DTE_Inline.ui.form div.DTE_Field { - width: 100%; - margin: 0 !important; -} -div.DTE.DTE_Inline.ui.form div.DTE_Field div.DTE_Field_Input { - width: 100% !important; - box-sizing: border-box; -} -div.DTE.DTE_Inline.ui.form div.DTE_Field > div { - width: 100%; - padding: 0; -} -div.DTE.DTE_Inline.ui.form.DTE_Processing:after { - top: 5px; -} - -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table { - display: table; - width: 100%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form { - display: table-cell; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content { - padding: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field:last-child { - margin-bottom: 0; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Form_Info, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); -} -div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url("../images/ajax-loader-small.gif") no-repeat top left; -} -div.DTE_Bubble.below div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; -} -div.DTE_Bubble.below div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); -} - -div.DTE_Bubble_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7))); - /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); - /* W3C Markup, IE10 Release Preview */ - z-index: 10; -} -div.DTE_Bubble_Background > div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; -} -div.DTE_Bubble_Background > div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); -} - -div.DTE_Bubble { - z-index: 1001; -} -div.DTE_Bubble div.DTE_Bubble_Liner { - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - border-radius: 6px; - padding: 1em; - border: 1px solid rgba(0, 0, 0, 0.2); -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field { - flex-direction: column; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label, -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field > div { - justify-content: flex-start; - width: 100% !important; - float: none; - clear: both; - text-align: left; -} -div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table > form div.DTE_Form_Content div.DTE_Field label { - padding-bottom: 4px; -} -div.DTE_Bubble div.DTE_Bubble_Triangle { - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - border-left: 1px solid rgba(0, 0, 0, 0.2); -} - -div.DTE div.editor_upload { - padding-top: 4px; -} -div.DTE div.editor_upload div.eu_table { - display: table; - width: 100%; -} -div.DTE div.editor_upload div.row { - display: table-row; -} -div.DTE div.editor_upload div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; -} -div.DTE div.editor_upload div.cell + div.cell { - padding-left: 10px; -} -div.DTE div.editor_upload div.row + div.row div.cell { - padding-top: 10px; -} -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; -} -div.DTE div.editor_upload input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; -} -div.DTE div.editor_upload div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; -} -div.DTE div.editor_upload div.drop.over { - border: 3px dashed #111; - color: #111; -} -div.DTE div.editor_upload div.drop span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; -} -div.DTE div.editor_upload div.rendered img { - max-width: 8em; - margin: 0 auto; -} -div.DTE div.editor_upload.noDrop div.drop { - display: none; -} -div.DTE div.editor_upload.noDrop div.row.second { - display: none; -} -div.DTE div.editor_upload.noDrop div.rendered { - margin-top: 10px; -} -div.DTE div.editor_upload.noClear div.clearValue button { - display: none; -} -div.DTE div.editor_upload.multi div.cell { - display: block; - width: 100%; -} -div.DTE div.editor_upload.multi div.cell div.drop { - min-height: 0; - padding-bottom: 5px; -} -div.DTE div.editor_upload.multi div.clearValue { - display: none; -} -div.DTE div.editor_upload.multi ul { - list-style-type: none; - margin: 0; - padding: 0; -} -div.DTE div.editor_upload.multi ul li { - position: relative; - margin-top: 0.5em; -} -div.DTE div.editor_upload.multi ul li:first-child { - margin-top: 0; -} -div.DTE div.editor_upload.multi ul li img { - vertical-align: middle; -} -div.DTE div.editor_upload.multi ul li button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; -} - -div.DTE div.editor_upload button.btn, -div.DTE div.editor_upload input[type=file] { - height: auto; -} -div.DTE div.editor_upload ul li button { - padding-bottom: 8px; -} - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5); - padding-bottom: 5px; -} -div.editor-datetime div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; -} -div.editor-datetime table { - border-spacing: 0; - margin: 6px 13px; -} -div.editor-datetime table th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; -} -div.editor-datetime table td { - font-size: 0.9em; - color: #444; - padding: 0; -} -div.editor-datetime table td.day { - text-align: right; - background: #f5f5f5; -} -div.editor-datetime table td.day.disabled { - color: #aaa; - background: white; -} -div.editor-datetime table td.day.today { - background-color: #ddd; -} -div.editor-datetime table td.day.today button { - font-weight: bold; -} -div.editor-datetime table td.day.selected button { - background: #2185D0; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.day button:hover { - background: #ff8000; - color: white; - border-radius: 2px; -} -div.editor-datetime table td.editor-datetime-week { - font-size: 0.7em; -} -div.editor-datetime table button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; -} -div.editor-datetime table.weekNumber th { - width: 12.5%; -} -div.editor-datetime div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-label:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; -} -div.editor-datetime div.editor-datetime-label select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; -} -div.editor-datetime div.editor-datetime-time { - text-align: center; -} -div.editor-datetime div.editor-datetime-time > span { - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; -} -div.editor-datetime div.editor-datetime-iconLeft, -div.editor-datetime div.editor-datetime-iconRight, -div.editor-datetime div.editor-datetime-iconUp, -div.editor-datetime div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; -} -div.editor-datetime div.editor-datetime-iconLeft:hover, -div.editor-datetime div.editor-datetime-iconRight:hover, -div.editor-datetime div.editor-datetime-iconUp:hover, -div.editor-datetime div.editor-datetime-iconDown:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; -} -div.editor-datetime div.editor-datetime-iconLeft button, -div.editor-datetime div.editor-datetime-iconRight button, -div.editor-datetime div.editor-datetime-iconUp button, -div.editor-datetime div.editor-datetime-iconDown button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; -} -div.editor-datetime div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=="); -} -div.editor-datetime div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconUp { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII="); -} -div.editor-datetime div.editor-datetime-iconDown { - height: 20px; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC"); -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} - -div.DTE div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -div.DTE.processing div.DTE_Processing_Indicator { - display: block; -} -div.DTE.DTE_Inline div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; -} -div.DTE.DTE_Bubble div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; -} -div.DTE div.DTE_Processing_Indicator span, -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba(0, 0, 0, 0.4); - background-color: rgba(0, 0, 0, 0.1); - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; -} -div.DTE div.DTE_Processing_Indicator:before, -div.DTE div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; -} -div.DTE div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; -} -div.DTE div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; -} -div.DTE div.DTE_Processing_Indicator:after { - left: 1em; -} -@-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -@keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } -} -div.DTE div.DTE_Processing_Indicator { - top: 22px; - right: 12px; -} diff --git a/css/csstables/editor.semanticui.min.css b/css/csstables/editor.semanticui.min.css deleted file mode 100644 index 5abb520..0000000 --- a/css/csstables/editor.semanticui.min.css +++ /dev/null @@ -1,5 +0,0 @@ -div.DTE div.DTE_Form_Error{display:none;color:#b11f1f}div.DTE label{padding-top:9px !important;align-self:flex-start;justify-content:flex-end}div.DTE div.eight.wide.field{flex-direction:column}div.DTE div.DTE_Field_InputControl{width:100%;margin:0 !important}div.DTE div.ui.message:empty{display:none}div.DTE_Field div.ui.message{width:100%}div.DTE_Field div.multi-value,div.DTE_Field div.multi-restore{display:none;cursor:pointer;margin-top:0}div.DTE_Field div.multi-value span,div.DTE_Field div.multi-restore span{display:block;color:#666;font-size:0.85em;line-height:1.35em}div.DTE_Field div.multi-value:hover,div.DTE_Field div.multi-restore:hover{background-color:#f1f1f1}div.DTE_Field div.multi-restore{margin-top:0.5em;font-size:0.8em;line-height:1.25em}div.DTE_Field:after{display:block;content:".";height:0;line-height:0;clear:both;visibility:hidden}div.DTE_Inline{position:relative;display:table;width:100%}div.DTE_Inline div.DTE_Inline_Field,div.DTE_Inline div.DTE_Inline_Buttons{display:table-cell;vertical-align:middle}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field{padding:0}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field>label,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field>label{display:none}div.DTE_Inline div.DTE_Inline_Field div.DTE_Field input,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Field input{width:100%}div.DTE_Inline div.DTE_Inline_Field div.DTE_Form_Buttons button,div.DTE_Inline div.DTE_Inline_Buttons div.DTE_Form_Buttons button{margin:-6px 0 -6px 4px;padding:5px}div.DTE_Inline div.DTE_Field input[type="color"],div.DTE_Inline div.DTE_Field input[type="date"],div.DTE_Inline div.DTE_Field input[type="datetime"],div.DTE_Inline div.DTE_Field input[type="datetime-local"],div.DTE_Inline div.DTE_Field input[type="email"],div.DTE_Inline div.DTE_Field input[type="month"],div.DTE_Inline div.DTE_Field input[type="number"],div.DTE_Inline div.DTE_Field input[type="password"],div.DTE_Inline div.DTE_Field input[type="search"],div.DTE_Inline div.DTE_Field input[type="tel"],div.DTE_Inline div.DTE_Field input[type="text"],div.DTE_Inline div.DTE_Field input[type="time"],div.DTE_Inline div.DTE_Field input[type="url"],div.DTE_Inline div.DTE_Field input[type="week"]{margin:-6px 0}div.DTE_Inline div.DTE_Field_Error,div.DTE_Inline div.DTE_Form_Error{font-size:11px;line-height:1.2em;padding:0;margin-top:10px}div.DTE_Inline div.DTE_Field_Error:empty,div.DTE_Inline div.DTE_Form_Error:empty{margin-top:0}span.dtr-data div.DTE_Inline{display:inline-table}div.DTE.DTE_Inline.ui.form label{display:none !important}div.DTE.DTE_Inline.ui.form div.DTE_Field{width:100%;margin:0 !important}div.DTE.DTE_Inline.ui.form div.DTE_Field div.DTE_Field_Input{width:100% !important;box-sizing:border-box}div.DTE.DTE_Inline.ui.form div.DTE_Field>div{width:100%;padding:0}div.DTE.DTE_Inline.ui.form.DTE_Processing:after{top:5px}div.DTE_Bubble{position:absolute;z-index:11;margin-top:-6px;opacity:0}div.DTE_Bubble div.DTE_Bubble_Liner{position:absolute;bottom:0;border:1px solid black;width:300px;margin-left:-150px;background-color:white;box-shadow:2px 2px 7px #555;border-radius:5px;border:2px solid #444;padding:1em;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table{display:table;width:100%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form{display:table-cell}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content{padding:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{position:relative;zoom:1;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field:last-child{margin-bottom:0}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table div.DTE_Form_Buttons{display:table-cell;vertical-align:bottom;padding:0 0 0 0.75em;width:1%}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header{border-top-left-radius:5px;border-top-right-radius:5px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Form_Info,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Header+div.DTE_Bubble_Table{padding-top:42px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Form_Error{float:none;display:none;padding:0;margin-bottom:0.5em}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close{position:absolute;top:-11px;right:-11px;width:22px;height:22px;border:2px solid white;background-color:black;text-align:center;border-radius:15px;cursor:pointer;z-index:12;box-shadow:2px 2px 6px #111}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:after{content:'\00d7';color:white;font-weight:bold;font-size:18px;line-height:22px;font-family:'Courier New', Courier, monospace;padding-left:1px}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Close:hover{background-color:#092079;box-shadow:2px 2px 9px #111}div.DTE_Bubble div.DTE_Bubble_Triangle{position:absolute;height:10px;width:10px;top:-6px;background-color:white;border:2px solid #444;border-top:none;border-right:none;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}div.DTE_Bubble.DTE_Processing div.DTE_Bubble_Liner:after{position:absolute;content:' ';display:block;top:12px;right:18px;height:12px;width:17px;background:url("../images/ajax-loader-small.gif") no-repeat top left}div.DTE_Bubble.below div.DTE_Bubble_Liner{top:10px;bottom:auto}div.DTE_Bubble.below div.DTE_Bubble_Triangle{top:4px;-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)}div.DTE_Bubble_Background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:-ms-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-moz-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-o-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:-webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0,0,0,0.3)), color-stop(1, rgba(0,0,0,0.7)));background:-webkit-radial-gradient(center, ellipse farthest-corner, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}div.DTE_Bubble_Background>div{position:absolute;top:0;right:0;left:0;bottom:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"}div.DTE_Bubble_Background>div:not([dummy]){filter:progid:DXImageTransform.Microsoft.gradient(enabled='false')}div.DTE_Bubble{z-index:1001}div.DTE_Bubble div.DTE_Bubble_Liner{box-shadow:0 5px 10px rgba(0,0,0,0.2);border-radius:6px;padding:1em;border:1px solid rgba(0,0,0,0.2)}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field{flex-direction:column}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label,div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field>div{justify-content:flex-start;width:100% !important;float:none;clear:both;text-align:left}div.DTE_Bubble div.DTE_Bubble_Liner div.DTE_Bubble_Table>form div.DTE_Form_Content div.DTE_Field label{padding-bottom:4px}div.DTE_Bubble div.DTE_Bubble_Triangle{border-bottom:1px solid rgba(0,0,0,0.2);border-left:1px solid rgba(0,0,0,0.2)}div.DTE div.editor_upload{padding-top:4px}div.DTE div.editor_upload div.eu_table{display:table;width:100%}div.DTE div.editor_upload div.row{display:table-row}div.DTE div.editor_upload div.cell{display:table-cell;position:relative;width:50%;vertical-align:top}div.DTE div.editor_upload div.cell+div.cell{padding-left:10px}div.DTE div.editor_upload div.row+div.row div.cell{padding-top:10px}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{width:100%;height:2.3em;font-size:0.8em;text-align:center;line-height:1em}div.DTE div.editor_upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0}div.DTE div.editor_upload div.drop{position:relative;box-sizing:border-box;width:100%;height:100%;border:3px dashed #ccc;border-radius:6px;min-height:4em;color:#999;padding-top:3px;text-align:center}div.DTE div.editor_upload div.drop.over{border:3px dashed #111;color:#111}div.DTE div.editor_upload div.drop span{max-width:75%;font-size:0.85em;line-height:1em}div.DTE div.editor_upload div.rendered img{max-width:8em;margin:0 auto}div.DTE div.editor_upload.noDrop div.drop{display:none}div.DTE div.editor_upload.noDrop div.row.second{display:none}div.DTE div.editor_upload.noDrop div.rendered{margin-top:10px}div.DTE div.editor_upload.noClear div.clearValue button{display:none}div.DTE div.editor_upload.multi div.cell{display:block;width:100%}div.DTE div.editor_upload.multi div.cell div.drop{min-height:0;padding-bottom:5px}div.DTE div.editor_upload.multi div.clearValue{display:none}div.DTE div.editor_upload.multi ul{list-style-type:none;margin:0;padding:0}div.DTE div.editor_upload.multi ul li{position:relative;margin-top:0.5em}div.DTE div.editor_upload.multi ul li:first-child{margin-top:0}div.DTE div.editor_upload.multi ul li img{vertical-align:middle}div.DTE div.editor_upload.multi ul li button{position:absolute;width:40px;right:0;top:50%;margin-top:-1.5em}div.DTE div.editor_upload button.btn,div.DTE div.editor_upload input[type=file]{height:auto}div.DTE div.editor_upload ul li button{padding-bottom:8px}div.editor-datetime{position:absolute;background-color:white;z-index:2050;border:1px solid #ccc;box-shadow:0 5px 15px -5px rgba(0,0,0,0.5);padding-bottom:5px}div.editor-datetime div.editor-datetime-title{text-align:center;padding:5px 0px 3px}div.editor-datetime table{border-spacing:0;margin:6px 13px}div.editor-datetime table th{font-size:0.8em;color:#777;font-weight:normal;width:14.285714286%;padding:0 0 4px 0;text-align:center}div.editor-datetime table td{font-size:0.9em;color:#444;padding:0}div.editor-datetime table td.day{text-align:right;background:#f5f5f5}div.editor-datetime table td.day.disabled{color:#aaa;background:white}div.editor-datetime table td.day.today{background-color:#ddd}div.editor-datetime table td.day.today button{font-weight:bold}div.editor-datetime table td.day.selected button{background:#2185D0;color:white;border-radius:2px}div.editor-datetime table td.day button:hover{background:#ff8000;color:white;border-radius:2px}div.editor-datetime table td.editor-datetime-week{font-size:0.7em}div.editor-datetime table button{width:100%;box-sizing:border-box;border:none;background:transparent;font-size:inherit;color:inherit;text-align:inherit;padding:5px 9px;cursor:pointer;margin:0}div.editor-datetime table.weekNumber th{width:12.5%}div.editor-datetime div.editor-datetime-label{position:relative;display:inline-block;height:30px;padding:5px 6px;border:1px solid transparent;box-sizing:border-box;cursor:pointer}div.editor-datetime div.editor-datetime-label:hover{border:1px solid #ddd;border-radius:2px;background-color:#f5f5f5}div.editor-datetime div.editor-datetime-label select{position:absolute;top:6px;left:0;cursor:pointer;opacity:0;-ms-filter:"alpha(opacity=0)"}div.editor-datetime div.editor-datetime-time{text-align:center}div.editor-datetime div.editor-datetime-time>span{vertical-align:middle}div.editor-datetime div.editor-datetime-time div.editor-datetime-timeblock{display:inline-block;vertical-align:middle}div.editor-datetime div.editor-datetime-iconLeft,div.editor-datetime div.editor-datetime-iconRight,div.editor-datetime div.editor-datetime-iconUp,div.editor-datetime div.editor-datetime-iconDown{width:30px;height:30px;background-position:center;background-repeat:no-repeat;opacity:0.3;overflow:hidden;box-sizing:border-box}div.editor-datetime div.editor-datetime-iconLeft:hover,div.editor-datetime div.editor-datetime-iconRight:hover,div.editor-datetime div.editor-datetime-iconUp:hover,div.editor-datetime div.editor-datetime-iconDown:hover{border:1px solid #ccc;border-radius:2px;background-color:#f0f0f0;opacity:0.6}div.editor-datetime div.editor-datetime-iconLeft button,div.editor-datetime div.editor-datetime-iconRight button,div.editor-datetime div.editor-datetime-iconUp button,div.editor-datetime div.editor-datetime-iconDown button{border:none;background:transparent;text-indent:30px;height:100%;width:100%;cursor:pointer}div.editor-datetime div.editor-datetime-iconLeft{position:absolute;top:5px;left:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==")}div.editor-datetime div.editor-datetime-iconRight{position:absolute;top:5px;right:5px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconUp{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII=")}div.editor-datetime div.editor-datetime-iconDown{height:20px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC")}div.editor-datetime-error{padding:0 1em;max-width:240px;font-size:11px;line-height:1.25em;text-align:center;color:#b11f1f}div.DTE div.DTE_Processing_Indicator{position:absolute;top:17px;right:9px;height:2em;width:2em;z-index:20;font-size:12px;display:none;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}div.DTE.processing div.DTE_Processing_Indicator{display:block}div.DTE.DTE_Inline div.DTE_Processing_Indicator{top:5px;right:6px;font-size:6px}div.DTE.DTE_Bubble div.DTE_Processing_Indicator{top:10px;right:14px;font-size:8px}div.DTE div.DTE_Processing_Indicator span,div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{display:block;background:black;width:0.5em;height:1.5em;border:1px solid rgba(0,0,0,0.4);background-color:rgba(0,0,0,0.1);-webkit-animation:editorProcessing 0.9s infinite ease-in-out;animation:editorProcessing 0.9s infinite ease-in-out}div.DTE div.DTE_Processing_Indicator:before,div.DTE div.DTE_Processing_Indicator:after{position:absolute;top:0;content:''}div.DTE div.DTE_Processing_Indicator:before{left:-1em;-webkit-animation-delay:-0.3s;animation-delay:-0.3s}div.DTE div.DTE_Processing_Indicator span{-webkit-animation-delay:-0.15s;animation-delay:-0.15s}div.DTE div.DTE_Processing_Indicator:after{left:1em}@-webkit-keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}@keyframes editorProcessing{0%, - 80%, - 100%{transform:scale(1, 1)}40%{transform:scale(1, 1.5)}}div.DTE div.DTE_Processing_Indicator{top:22px;right:12px} diff --git a/css/csstables/jquery.dataTables.min.css b/css/csstables/jquery.dataTables.min.css deleted file mode 100644 index cee9549..0000000 --- a/css/csstables/jquery.dataTables.min.css +++ /dev/null @@ -1 +0,0 @@ -table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:700}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px;border-top:1px solid #111}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;*cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url(../images/sort_both.png)}table.dataTable thead .sorting_asc{background-image:url(../images/sort_asc.png)}table.dataTable thead .sorting_desc{background-image:url(../images/sort_desc.png)}table.dataTable thead .sorting_asc_disabled{background-image:url(../images/sort_asc_disabled.png)}table.dataTable thead .sorting_desc_disabled{background-image:url(../images/sort_desc_disabled.png)}table.dataTable tbody tr{background-color:#fff}table.dataTable tbody tr.selected{background-color:#B0BED9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#acbad4}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#aab7d1}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:#f5f5f5}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 17px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:.5em 1em;margin-left:2px;text-align:center;text-decoration:none!important;cursor:pointer;*cursor:hand;color:#333!important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333!important;border:1px solid #979797;background-color:#fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fff),color-stop(100%,#dcdcdc));background:-webkit-linear-gradient(top,#fff 0%,#dcdcdc 100%);background:-moz-linear-gradient(top,#fff 0%,#dcdcdc 100%);background:-ms-linear-gradient(top,#fff 0%,#dcdcdc 100%);background:-o-linear-gradient(top,#fff 0%,#dcdcdc 100%);background:linear-gradient(to bottom,#fff 0%,#dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666!important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:#fff!important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#585858),color-stop(100%,#111));background:-webkit-linear-gradient(top,#585858 0%,#111 100%);background:-moz-linear-gradient(top,#585858 0%,#111 100%);background:-ms-linear-gradient(top,#585858 0%,#111 100%);background:-o-linear-gradient(top,#585858 0%,#111 100%);background:linear-gradient(to bottom,#585858 0%,#111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#2b2b2b),color-stop(100%,#0c0c0c));background:-webkit-linear-gradient(top,#2b2b2b 0%,#0c0c0c 100%);background:-moz-linear-gradient(top,#2b2b2b 0%,#0c0c0c 100%);background:-ms-linear-gradient(top,#2b2b2b 0%,#0c0c0c 100%);background:-o-linear-gradient(top,#2b2b2b 0%,#0c0c0c 100%);background:linear-gradient(to bottom,#2b2b2b 0%,#0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:#fff;background:-webkit-gradient(linear,left top,right top,color-stop(0%,rgba(255,255,255,0)),color-stop(25%,rgba(255,255,255,0.9)),color-stop(75%,rgba(255,255,255,0.9)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left,rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0%,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0!important;padding:0!important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:.5em}} \ No newline at end of file diff --git a/css/csstables/responsive.dataTables.min.css b/css/csstables/responsive.dataTables.min.css deleted file mode 100644 index db2f7d9..0000000 --- a/css/csstables/responsive.dataTables.min.css +++ /dev/null @@ -1 +0,0 @@ -table.dataTable.dtr-inline.collapsed>tbody>tr>td.child,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty{cursor:default !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th.child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>td.dataTables_empty:before{display:none !important}table.dataTable.dtr-inline.collapsed>tbody>tr>td:first-child,table.dataTable.dtr-inline.collapsed>tbody>tr>th:first-child{position:relative;padding-left:30px;cursor:pointer}table.dataTable.dtr-inline.collapsed>tbody>tr>td:first-child:before,table.dataTable.dtr-inline.collapsed>tbody>tr>th:first-child:before{top:9px;left:4px;height:14px;width:14px;display:block;position:absolute;color:white;border:2px solid white;border-radius:14px;box-shadow:0 0 3px #444;box-sizing:content-box;text-align:center;font-family:'Courier New', Courier, monospace;line-height:14px;content:'+';background-color:#31b131}table.dataTable.dtr-inline.collapsed>tbody>tr.parent>td:first-child:before,table.dataTable.dtr-inline.collapsed>tbody>tr.parent>th:first-child:before{content:'-';background-color:#d33333}table.dataTable.dtr-inline.collapsed>tbody>tr.child td:before{display:none}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td:first-child,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th:first-child{padding-left:27px}table.dataTable.dtr-inline.collapsed.compact>tbody>tr>td:first-child:before,table.dataTable.dtr-inline.collapsed.compact>tbody>tr>th:first-child:before{top:5px;left:4px;height:14px;width:14px;border-radius:14px;line-height:14px;text-indent:3px}table.dataTable.dtr-column>tbody>tr>td.control,table.dataTable.dtr-column>tbody>tr>th.control{position:relative;cursor:pointer}table.dataTable.dtr-column>tbody>tr>td.control:before,table.dataTable.dtr-column>tbody>tr>th.control:before{top:50%;left:50%;height:16px;width:16px;margin-top:-10px;margin-left:-10px;display:block;position:absolute;color:white;border:2px solid white;border-radius:14px;box-shadow:0 0 3px #444;box-sizing:content-box;text-align:center;font-family:'Courier New', Courier, monospace;line-height:14px;content:'+';background-color:#31b131}table.dataTable.dtr-column>tbody>tr.parent td.control:before,table.dataTable.dtr-column>tbody>tr.parent th.control:before{content:'-';background-color:#d33333}table.dataTable>tbody>tr.child{padding:0.5em 1em}table.dataTable>tbody>tr.child:hover{background:transparent !important}table.dataTable>tbody>tr.child ul.dtr-details{display:inline-block;list-style-type:none;margin:0;padding:0}table.dataTable>tbody>tr.child ul.dtr-details li{border-bottom:1px solid #efefef;padding:0.5em 0}table.dataTable>tbody>tr.child ul.dtr-details li:first-child{padding-top:0}table.dataTable>tbody>tr.child ul.dtr-details li:last-child{border-bottom:none}table.dataTable>tbody>tr.child span.dtr-title{display:inline-block;min-width:75px;font-weight:bold}div.dtr-modal{position:fixed;box-sizing:border-box;top:0;left:0;height:100%;width:100%;z-index:100;padding:10em 1em}div.dtr-modal div.dtr-modal-display{position:absolute;top:0;left:0;bottom:0;right:0;width:50%;height:50%;overflow:auto;margin:auto;z-index:102;overflow:auto;background-color:#f5f5f7;border:1px solid black;border-radius:0.5em;box-shadow:0 12px 30px rgba(0,0,0,0.6)}div.dtr-modal div.dtr-modal-content{position:relative;padding:1em}div.dtr-modal div.dtr-modal-close{position:absolute;top:6px;right:6px;width:22px;height:22px;border:1px solid #eaeaea;background-color:#f9f9f9;text-align:center;border-radius:3px;cursor:pointer;z-index:12}div.dtr-modal div.dtr-modal-close:hover{background-color:#eaeaea}div.dtr-modal div.dtr-modal-background{position:fixed;top:0;left:0;right:0;bottom:0;z-index:101;background:rgba(0,0,0,0.6)}@media screen and (max-width: 767px){div.dtr-modal div.dtr-modal-display{width:95%}} diff --git a/css/csstables/scss/_notes/dwsync.xml b/css/csstables/scss/_notes/dwsync.xml deleted file mode 100644 index b34acb2..0000000 --- a/css/csstables/scss/_notes/dwsync.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/css/csstables/scss/bubble.scss b/css/csstables/scss/bubble.scss deleted file mode 100644 index 5406720..0000000 --- a/css/csstables/scss/bubble.scss +++ /dev/null @@ -1,130 +0,0 @@ - -// Bubble form editing -// Very similar to the main form, but attached to a particular node and the -// form layout is slightly different with the fields container and buttons -// making up a table of a single row and two columns. This allows the buttons -// to be removed from the display and under this condition the fields will -// take up the full width available. -div.DTE_Bubble { - position: absolute; - z-index: 11; - margin-top: -6px; - opacity: 0; - - div.DTE_Bubble_Liner { - position: absolute; - bottom: 0; - border: 1px solid black; - width: 300px; - margin-left: -150px; - background-color: white; - box-shadow: 2px 2px 7px #555; - border-radius: 5px; - border: 2px solid #444; - padding: 1em; - @include box-sizing(border-box); - - div.DTE_Bubble_Table { - display: table; - width: 100%; - - > form { - display: table-cell; - - div.DTE_Form_Content { - padding: 0; - - div.DTE_Field { - position: relative; - zoom: 1; - margin-bottom: 0.5em; - - &:last-child { - margin-bottom: 0; - } - } - } - } - - div.DTE_Form_Buttons { - display: table-cell; - vertical-align: bottom; - padding: 0 0 0 0.75em; - width: 1%; // browser will resize to a min width - } - } - - div.DTE_Header { - border-top-left-radius: 5px; - border-top-right-radius: 5px; - } - - div.DTE_Header + div.DTE_Form_Info, - div.DTE_Header + div.DTE_Bubble_Table { - padding-top: 42px; - } - - - div.DTE_Form_Error { - float: none; - display: none; - padding: 0; - margin-bottom: 0.5em; - } - - div.DTE_Bubble_Close { - @include close-icon(); - } - } - - div.DTE_Bubble_Triangle { - position: absolute; - height: 10px; - width: 10px; - top: -6px; - background-color: white; - border: 2px solid #444; - border-top: none; - border-right: none; - - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - -o-transform: rotate(-45deg); - transform: rotate(-45deg); - } - - &.DTE_Processing { - div.DTE_Bubble_Liner:after { - position: absolute; - content: ' '; - display: block; - top: 12px; - right: 18px; - height: 12px; - width: 17px; - background: url('../images/ajax-loader-small.gif') no-repeat top left; - } - } - - &.below { - div.DTE_Bubble_Liner { - top: 10px; - bottom: auto; - } - - div.DTE_Bubble_Triangle { - top: 4px; - -webkit-transform: rotate(135deg); - -moz-transform: rotate(135deg); - -ms-transform: rotate(135deg); - -o-transform: rotate(135deg); - transform: rotate(135deg); - } - } -} - -div.DTE_Bubble_Background { - @include overlay-background(); -} - diff --git a/css/csstables/scss/datatable.scss b/css/csstables/scss/datatable.scss deleted file mode 100644 index d5e44bb..0000000 --- a/css/csstables/scss/datatable.scss +++ /dev/null @@ -1,93 +0,0 @@ - -// Row highlighting on edit styles -// -// To change the colour of the highlight, simply modify the variable below and -// recompile the SCSS stylesheet (if you don't have SASS installed, you can use -// the online service at http://sassmeister.com/ . -// -// The DataTables styles below match the default DataTables stylesheet: -// http://next.datatables.net/manual/styling/classes so you can retain the full -// benefits of the DataTables styling options. - -$table-row-highlight: #FFFBCC; - -table.dataTable { - tbody { - tr.highlight { - background-color: $table-row-highlight !important; - } - - tr.highlight, - tr.noHighlight, - tr.highlight td, - tr.noHighlight td { - @include background-transision(); - } - } - - &.stripe tbody, - &.display tbody { - tr.odd { - &.highlight { - background-color: shade($table-row-highlight, 2.35%); - } - } - } - - // Hover classes - add "hover" class to the table to activate - &.hover tbody, - &.display tbody { - tr:hover, - tr.odd:hover, - tr.even:hover { - &.highlight { - background-color: shade($table-row-highlight, 3.6%); - } - } - } - - - // Sort column highlighting - add "hover" class to the table to activate - &.order-column, - &.display { - tbody { - tr.highlight>.sorting_1, - tr.highlight>.sorting_2, - tr.highlight>.sorting_3 { - background-color: shade($table-row-highlight, 2%); - } - } - } - - &.display tbody, - &.order-column.stripe tbody { - tr.odd { - &.highlight { - >.sorting_1 { background-color: shade($table-row-highlight, 5.4%);} - >.sorting_2 { background-color: shade($table-row-highlight, 4.7%);} - >.sorting_3 { background-color: shade($table-row-highlight, 3.9%);} - } - } - - tr.even { - &.highlight { - >.sorting_1 { background-color: shade($table-row-highlight, 2%); } - >.sorting_2 { background-color: shade($table-row-highlight, 1.2%); } - >.sorting_3 { background-color: shade($table-row-highlight, 0.4%); } - } - } - } - - &.display tbody, - &.order-column.hover tbody { - tr:hover, - tr.odd:hover, - tr.even:hover { - &.highlight { - >.sorting_1 { background-color: shade($table-row-highlight, 8.2%); } - >.sorting_2 { background-color: shade($table-row-highlight, 7.5%); } - >.sorting_3 { background-color: shade($table-row-highlight, 6.3%); } - } - } - } -} \ No newline at end of file diff --git a/css/csstables/scss/datetime.scss b/css/csstables/scss/datetime.scss deleted file mode 100644 index 148f1d8..0000000 --- a/css/csstables/scss/datetime.scss +++ /dev/null @@ -1,192 +0,0 @@ - -$editor-datetime-selected: #4E6CA3 !default; - -div.editor-datetime { - position: absolute; - background-color: white; - z-index: 2050; - border: 1px solid #ccc; - box-shadow: 0 5px 15px -5px rgba(0,0,0,.5); - padding-bottom: 5px; - - div.editor-datetime-title { - text-align: center; - padding: 5px 0px 3px; - } - - table { - border-spacing: 0; - margin: 6px 13px; - - th { - font-size: 0.8em; - color: #777; - font-weight: normal; - width: 14.285714286%; - padding: 0 0 4px 0; - text-align: center; - } - - td { - font-size: 0.9em; - color: #444; - padding: 0; - } - - td.day { - text-align: right; - background: #f5f5f5; - - &.disabled { - color: #aaa; - background: white; - } - - &.today { - background-color: #ddd; - - button { - font-weight: bold; - } - } - - &.selected button { - background: $editor-datetime-selected; - color: white; - border-radius: 2px; - } - - button:hover { - background: #ff8000; - color: white; - border-radius: 2px; - } - } - - td.editor-datetime-week { - font-size: 0.7em; - } - - button { - width: 100%; - box-sizing: border-box; - border: none; - background: transparent; - font-size: inherit; - color: inherit; - text-align: inherit; - padding: 5px 9px; - cursor: pointer; - margin: 0; - } - - &.weekNumber th { - width: 12.5%; - } - } - - div.editor-datetime-label { - position: relative; - display: inline-block; - height: 30px; - padding: 5px 6px; - border: 1px solid transparent; - box-sizing: border-box; - cursor: pointer; - - &:hover { - border: 1px solid #ddd; - border-radius: 2px; - background-color: #f5f5f5; - } - - span { - - } - - select { - position: absolute; - top: 6px; - left: 0; - cursor: pointer; - opacity: 0; - -ms-filter: "alpha(opacity=0)"; - } - } - - div.editor-datetime-time { - text-align: center; - - > span { - vertical-align: middle; - } - - div.editor-datetime-timeblock { - display: inline-block; - vertical-align: middle; - } - } - - - div.editor-datetime-iconLeft, - div.editor-datetime-iconRight, - div.editor-datetime-iconUp, - div.editor-datetime-iconDown { - width: 30px; - height: 30px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.3; - overflow: hidden; - box-sizing: border-box; - - &:hover { - border: 1px solid #ccc; - border-radius: 2px; - background-color: #f0f0f0; - opacity: 0.6; - } - - button { - border: none; - background: transparent; - text-indent: 30px; - height: 100%; - width: 100%; - cursor: pointer; - } - } - - div.editor-datetime-iconLeft { - position: absolute; - top: 5px; - left: 5px; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg=='); - } - - div.editor-datetime-iconRight { - position: absolute; - top: 5px; - right: 5px; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII='); - } - - div.editor-datetime-iconUp { - height: 20px; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAL0lEQVR4AWOgJmBhxCvLyopHnpmVjY2VCadeoCxIHrcsWJ4RlyxCHlMWCTBRJxwAjrIBDMWSiM0AAAAASUVORK5CYII='); - } - - div.editor-datetime-iconDown { - height: 20px; - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAALCAMAAABf9c24AAAAFVBMVEX///99fX1+fn57e3t6enoAAAAAAAC73bqPAAAABnRSTlMAYmJkZt92bnysAAAAMElEQVR4AWOgDmBiRQIsmPKMrGxQgJDFlEfIYpoPk8Utz8qM232MYFfhkQfKUg8AANefAQxecJ58AAAAAElFTkSuQmCC'); - } -} - -div.editor-datetime-error { - padding: 0 1em; - max-width: 240px; - font-size: 11px; - line-height: 1.25em; - text-align: center; - color: #b11f1f; -} \ No newline at end of file diff --git a/css/csstables/scss/envelope.scss b/css/csstables/scss/envelope.scss deleted file mode 100644 index 692698d..0000000 --- a/css/csstables/scss/envelope.scss +++ /dev/null @@ -1,91 +0,0 @@ - -/* - * Namespace: DTED - DataTables Editor Display - Envelope - */ - -div.DTED_Envelope_Wrapper { - position: absolute; - top: 0; - bottom: 0; - left: 50%; - height: 100%; - z-index: 11; - display: none; - overflow: hidden; - - // Create a shadow display at the top of the evelope to make it look like it has - // come from under the element that it is attached to/ Left and right to give a - // slight fade and the two ends - div.DTED_Envelope_Shadow { - position: absolute; - top: -10px; - left: 10px; - right: 10px; - height: 10px; - z-index: 10; - box-shadow: 0 0 20px black; - } - - div.DTED_Envelope_Container { - position: absolute; - top: 0; - left: 5%; - width: 90%; - - border-left: 1px solid #777; - border-right: 1px solid #777; - border-bottom: 1px solid #777; - box-shadow: 3px 3px 10px #555; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - background-color: white; - - div.DTE_Processing_Indicator { - right: 36px; - } - - div.DTE_Footer { - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - } - - div.DTED_Envelope_Close { - position: absolute; - top: 16px; - right: 10px; - width: 18px; - height: 18px; - cursor: pointer; - *cursor: hand; - z-index: 12; - text-align: center; - font-size: 12px; - - background: #F8F8F8; - background: -webkit-gradient(linear, center bottom, center top, from(#CCC), to(white)); - background: -moz-linear-gradient(top, white, #CCC); - background: linear-gradient(to bottom, white, #CCC); - text-shadow: 0 1px 0 white; - border: 1px solid #999; - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - box-shadow: 0px 0px 1px #999; - -moz-box-shadow: 0px 0px 1px #999; - -webkit-box-shadow: 0px 0px 1px #999; - } - } -} - - -div.DTED_Envelope_Background { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10; - @include radial-gradient( rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4) ); -} - - diff --git a/css/csstables/scss/fields.scss b/css/csstables/scss/fields.scss deleted file mode 100644 index 9a467d3..0000000 --- a/css/csstables/scss/fields.scss +++ /dev/null @@ -1,159 +0,0 @@ - - -// Generic field styling -div.DTE_Field { - input, - textarea { - box-sizing: border-box; - background-color: white; - -webkit-transition: background-color ease-in-out .15s; - transition: background-color ease-in-out .15s; - } - - input:focus, - textarea:focus { - background-color: #ffffee; - } - - input[type="color"], - input[type="date"], - input[type="datetime"], - input[type="datetime-local"], - input[type="email"], - input[type="month"], - input[type="number"], - input[type="password"], - input[type="search"], - input[type="tel"], - input[type="text"], - input[type="time"], - input[type="url"], - input[type="week"] { - padding: 6px 4px; - width: 100%; - } - - label div.DTE_Label_Info { - font-size: 0.85em; - margin-top: 0.25em; - - &:empty { - margin-top: 0; - } - } - - div.DTE_Field_Info, - div.DTE_Field_Message, - div.DTE_Field_Error { - font-size: 11px; - line-height: 1em; - margin-top: 5px; - - &:empty { - margin-top: 0; - } - } - - div.DTE_Field_Error { - display: none; - color: #b11f1f; - } - - div.multi-value { - display: none; - border: 1px dotted #666; - border-radius: 3px; - padding: 5px; - background-color: #fafafa; - cursor: pointer; - - span { - font-size: 0.8em; - line-height: 1.25em; - display: block; - color: #666; - } - - &.multi-noEdit { - border: 1px solid #ccc; - cursor: auto; - background-color: #fcfcfc; - - &:hover { - background-color: #fcfcfc; - } - } - - &:hover { - background-color: #f1f1f1; - } - } - - &.disabled { - color: grey; - - div.multi-value { - cursor: default; - border: 1px dotted #aaa; - background-color: transparent; - } - } - - div.multi-restore { - display: none; - margin-top: 0.5em; - font-size: 0.8em; - line-height: 1.25em; - color: #3879d9; - - &:hover { - text-decoration: underline; - cursor: pointer; - } - } -} - -// Specific field type styling -div.DTE_Field_Type_textarea { - textarea { - padding: 3px; - width: 100%; - height: 80px; - } -} - -div.DTE_Field.DTE_Field_Type_date { - img { - vertical-align: middle; - cursor: pointer; - *cursor: hand; - } - - input.jqueryui { - width: 87%; - margin-right: 6px; - } -} - -div.DTE_Field_Type_checkbox, -div.DTE_Field_Type_radio { - div.DTE_Field_Input > div > div { - margin-bottom: 0.25em; - - &:last-child { - margin-bottom: 0; - } - - input { - } - - label { - margin-left: 0.75em; - vertical-align: middle; - } - } -} - -div.DTE_Field_Type_select div.DTE_Field_Input { - padding-top: 4px; -} diff --git a/css/csstables/scss/inline.scss b/css/csstables/scss/inline.scss deleted file mode 100644 index 8351177..0000000 --- a/css/csstables/scss/inline.scss +++ /dev/null @@ -1,66 +0,0 @@ - -// Inline form editing -// Hide the label and allow the field to take the full width -div.DTE_Inline { - position: relative; - display: table; - width: 100%; - - div.DTE_Inline_Field, - div.DTE_Inline_Buttons { - display: table-cell; - vertical-align: middle; - - div.DTE_Field { - padding: 0; - - >label { - display: none; - } - - input { - width: 100%; - } - } - - div.DTE_Form_Buttons button { - margin: -6px 0 -6px 4px; - padding: 5px; - } - } - - // Have the input types take up full space, taking into account the cell padding - div.DTE_Field input[type="color"], - div.DTE_Field input[type="date"], - div.DTE_Field input[type="datetime"], - div.DTE_Field input[type="datetime-local"], - div.DTE_Field input[type="email"], - div.DTE_Field input[type="month"], - div.DTE_Field input[type="number"], - div.DTE_Field input[type="password"], - div.DTE_Field input[type="search"], - div.DTE_Field input[type="tel"], - div.DTE_Field input[type="text"], - div.DTE_Field input[type="time"], - div.DTE_Field input[type="url"], - div.DTE_Field input[type="week"] { - margin: -6px 0; - } - - div.DTE_Field_Error, - div.DTE_Form_Error { - font-size: 11px; - line-height: 1.2em; - padding: 0; - margin-top: 10px; - - &:empty { - margin-top: 0; - } - } -} - -// Responsive integration -span.dtr-data div.DTE_Inline { - display: inline-table; -} \ No newline at end of file diff --git a/css/csstables/scss/lightbox.scss b/css/csstables/scss/lightbox.scss deleted file mode 100644 index cc2ec8b..0000000 --- a/css/csstables/scss/lightbox.scss +++ /dev/null @@ -1,147 +0,0 @@ - - -div.DTED_Lightbox_Wrapper { - position: fixed; - top: 0; - left: 50%; - margin-left: -390px; - width: 780px; - height: 100%; - z-index: 11; - - div.DTED_Lightbox_Container { - *position: absolute; /* IE6 */ - *top: 50%; - #position: absolute; /* IE7 */ - #top: 50%; - display: table; - height: 100%; - width: 100%; - - div.DTED_Lightbox_Content_Wrapper { - *position: relative; /* IE6 */ - #position: relative; /* IE7 */ - display: table-cell; - vertical-align: middle; - width: 100%; - - div.DTED_Lightbox_Content { - *top: -50%; /* IE6 */ - #top: -50%; /* IE7 */ - position: relative; - border: 7px solid rgba(220, 220, 220, 0.5); - box-shadow: 2px 2px 10px #555; - border-radius: 10px; - @include box-sizing(border-box); - - div.DTE { - background: white; - border-radius: 6px; - box-shadow: 0 0 5px #555; - border: 2px solid #444; - @include box-sizing(border-box); - - div.DTE_Header { - top: 2px; - left: 2px; - right: 2px; - width: auto; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - } - - div.DTE_Footer { - bottom: 2px; - left: 2px; - right: 2px; - width: auto; - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - } - } - - div.DTED_Lightbox_Close { - @include close-icon(); - } - } - } - } -} - - -div.DTED_Lightbox_Background { - @include overlay-background(); -} - - -body.DTED_Lightbox_Mobile { - div.DTED_Lightbox_Background { - height: 0; - } - - div.DTED_Lightbox_Shown { - display: none; - } - - div.DTED_Lightbox_Wrapper { - position: absolute; - top: 0px; - left: 0px; - right: 0px; - bottom: 0px; - width: auto; - height: auto; - margin-left: 0; - -webkit-overflow-scrolling: touch; - - div.DTED_Lightbox_Container { - display: block; - - div.DTED_Lightbox_Content_Wrapper { - display: block; - - div.DTED_Lightbox_Content { - border: 4px solid rgba(220, 220, 220, 0.5); - border-radius: 0; - - div.DTE { - border-radius: 0; - box-shadow: 0 0 5px #555; - border: 2px solid #444; - - div.DTE_Header { - border-top-left-radius: 0; - border-top-right-radius: 0; - } - - div.DTE_Footer { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - } - - div.DTED_Lightbox_Close { - top: 11px; - right: 15px; - } - } - } - } - } -} - -@media only screen -and (max-width: 780px) { - div.DTED_Lightbox_Wrapper { - position: fixed; - top: 0; - left: 0; - width: 100%; - margin-left: 0; - } -} - - - - - diff --git a/css/csstables/scss/main.scss b/css/csstables/scss/main.scss deleted file mode 100644 index 02fc3dd..0000000 --- a/css/csstables/scss/main.scss +++ /dev/null @@ -1,188 +0,0 @@ - - -// The main form. -// Most of the styles for display of the main form come from the display -// controller (lightbox and envelope are the two built in options). -div.DTE_Body { - padding: 50px 0; // space for hte header and footer which are position: absolute - - div.DTE_Body_Content { - position: relative; - overflow: auto; - - div.DTE_Form_Info { - padding: 1em 1em 0 1em; - margin: 0; - } - - div.DTE_Field { - position: relative; - zoom: 1; - - clear: both; - padding: 5px 20%; - border: 1px solid transparent; - - &:after { - display: block; - content: "."; - height: 0; - line-height: 0; - clear: both; - visibility: hidden; - } - - &:hover { - background-color: #f9f9f9; - border: 1px solid #f3f3f3; - } - - >label { - float: left; - width: 40%; - padding-top: 6px; - } - - >div.DTE_Field_Input { - float: right; - width: 60%; - } - - // Field in error state - &.DTE_Field_StateError { } - - &.full { - padding: 5px 0 5px 20%; - - >label { - width: 30%; - } - - >div.DTE_Field_Input { - width: 70%; - } - } - - &.block { - >div.DTE_Field_Input { - float: none; - clear: both; - width: 100%; - } - } - } - } -} - -html[dir="rtl"] { - div.DTE_Body div.DTE_Body_Content div.DTE_Field { - > label { - float: right; - } - - >div.DTE_Field_Input { - float: left; - } - } - - div.DTE div.DTE_Form_Buttons button { - float: left; - } -} - -// iPad in portrait -@media only screen -and (max-width : 768px) { - div.DTE_Body { - div.DTE_Body_Content { - div.DTE_Field { - padding: 5px 10%; - - &.full { - padding: 5px 0 5px 10%; - - >label { - width: 35.5%; - } - - >div.DTE_Field_Input { - width: 64.5%; - } - } - - &.block { - >div.DTE_Field_Input { - width: 100%; - } - } - } - } - } -} - -@media only screen -and (max-width : 640px) { - div.DTE_Body { - div.DTE_Body_Content { - div.DTE_Field { - padding: 5px 0; - - &.full { - padding: 5px 0%; - - >label { - width: 40%; - } - - >div.DTE_Field_Input { - width: 60%; - } - } - - &.block { - >div.DTE_Field_Input { - width: 100%; - } - } - } - } - } -} - -// For devices with smaller screens, the fields should be shown stacked -@media only screen -and (max-width : 580px) { - div.DTE_Body { - div.DTE_Body_Content { - div.DTE_Field { - position: relative; - zoom: 1; - - clear: both; - padding: 5px 0; - - >label { - float: none; - width: auto; - padding-top: 0; - } - - >div.DTE_Field_Input { - float: none; - width: auto; - } - - &.full, - &.block { - padding: 5px 0; - - >label, - >div.DTE_Field_Input { - width: 100%; - } - } - } - } - } -} - diff --git a/css/csstables/scss/mixins.scss b/css/csstables/scss/mixins.scss deleted file mode 100644 index 60205cb..0000000 --- a/css/csstables/scss/mixins.scss +++ /dev/null @@ -1,176 +0,0 @@ - - -@function tint( $color, $percent ) { - @return mix(white, $color, $percent); -} - -@function shade( $color, $percent ) { - @return mix(black, $color, $percent); -} - - -@mixin border-radius ( $radius ) { - -webkit-border-radius: $radius; - -moz-border-radius: $radius; - -ms-border-radius: $radius; - -o-border-radius: $radius; - border-radius: $radius; -} - -@mixin box-sizing($box-model) { - -webkit-box-sizing: $box-model; // Safari <= 5 - -moz-box-sizing: $box-model; // Firefox <= 19 - box-sizing: $box-model; -} - -@mixin box-shadow($top, $left, $blur, $color, $inset: false) { - @if $inset { - -webkit-box-shadow:inset $top $left $blur $color; - -moz-box-shadow:inset $top $left $blur $color; - box-shadow:inset $top $left $blur $color; - } @else { - -webkit-box-shadow: $top $left $blur $color; - -moz-box-shadow: $top $left $blur $color; - box-shadow: $top $left $blur $color; - } -} - -@mixin three-stop-gradient($fromColor, $middleColor, $toColor) { - background-color: $toColor; /* Fallback */ - background-image: -webkit-linear-gradient(top, $fromColor, $middleColor, $toColor); /* Chrome 10+, Saf5.1+, iOS 5+ */ - background-image: -moz-linear-gradient(top, $fromColor, $middleColor, $toColor); /* FF3.6 */ - background-image: -ms-linear-gradient(top, $fromColor, $middleColor, $toColor); /* IE10 */ - background-image: -o-linear-gradient(top, $fromColor, $middleColor, $toColor); /* Opera 11.10+ */ - background-image: linear-gradient(to bottom, $fromColor, $middleColor, $toColor); - filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#{nth( $fromColor, 1 )}', EndColorStr='#{nth( $toColor, 1 )}'); -} - -@mixin radial-gradient ($fromColor, $toColor ) { - background: $toColor; /* Fallback */ - background: -ms-radial-gradient(center, ellipse farthest-corner, $fromColor 0%, $toColor 100%); /* IE10 Consumer Preview */ - background: -moz-radial-gradient(center, ellipse farthest-corner, $fromColor 0%, $toColor 100%); /* Firefox */ - background: -o-radial-gradient(center, ellipse farthest-corner, $fromColor 0%, $toColor 100%); /* Opera */ - background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, $fromColor), color-stop(1, $toColor)); /* Webkit (Safari/Chrome 10) */ - background: -webkit-radial-gradient(center, ellipse farthest-corner, $fromColor 0%, $toColor 100%); /* Webkit (Chrome 11+) */ - background: radial-gradient(ellipse farthest-corner at center, $fromColor 0%, $toColor 100%); /* W3C Markup, IE10 Release Preview */ -} - -@mixin keyframe ($animation_name) { - @-webkit-keyframes #{$animation_name} { - @content; - } - - @-moz-keyframes #{$animation_name} { - @content; - } - - @-o-keyframes #{$animation_name} { - @content; - } - - @keyframes #{$animation_name} { - @content; - } -} - -@mixin animation ($duration, $animation) { - -webkit-animation-duration: $duration; - -webkit-animation-name: $animation; - -webkit-animation-fill-mode: forwards; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -webkit-animation-direction: alternate; - - -moz-animation-duration: $duration; - -moz-animation-name: $animation; - -moz-animation-fill-mode: forwards; - -moz-animation-iteration-count: infinite; - -moz-animation-timing-function: linear; - -moz-animation-direction: alternate; - - -o-animation-duration: $duration; - -o-animation-name: $animation; - -o-animation-fill-mode: forwards; - -o-animation-iteration-count: infinite; - -o-animation-timing-function: linear; - -o-animation-direction: alternate; - - animation-duration: $duration; - animation-name: $animation; - animation-fill-mode: forwards; - animation-iteration-count: infinite; - animation-timing-function: linear; - animation-direction: alternate; -} - -@mixin close-icon () { - position: absolute; - top: -11px; - right: -11px; - width: 22px; - height: 22px; - border: 2px solid white; - background-color: black; - text-align: center; - border-radius: 15px; - cursor: pointer; - z-index: 12; - box-shadow: 2px 2px 6px #111; - - &:after { - content: '\00d7'; - color: white; - font-weight: bold; - font-size: 18px; - line-height: 22px; - font-family: 'Courier New', Courier, monospace; - padding-left: 1px; - } - - &:hover { - background-color: #092079; - box-shadow: 2px 2px 9px #111; - } -} - -@mixin overlay-background () { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - - @include radial-gradient( rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7) ); - - z-index: 10; - - // IE8- doesn't support RGBA and jQuery uses `filter:` for the fade-in - // animation, so we need a child element that is used just for the display - >div { - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - - // IE7- - filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000); - - // IE8 - -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)"; - } - - // IE9 has both filter and rgba support, so we need a hack to disable the filter - >div:not([dummy]) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled='false'); - } -} - -@mixin background-transision () { - -webkit-transition: background-color 500ms linear; - -moz-transition: background-color 500ms linear; - -ms-transition: background-color 500ms linear; - -o-transition: background-color 500ms linear; - transition: background-color 500ms linear; -} - diff --git a/css/csstables/scss/processing.scss b/css/csstables/scss/processing.scss deleted file mode 100644 index d49df92..0000000 --- a/css/csstables/scss/processing.scss +++ /dev/null @@ -1,96 +0,0 @@ - -div.DTE { - div.DTE_Processing_Indicator { - position: absolute; - top: 17px; - right: 9px; - height: 2em; - width: 2em; - z-index: 20; - font-size: 12px; - display: none; - - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); - } - - &.processing { - div.DTE_Processing_Indicator { - display: block; - } - } - - &.DTE_Inline { - div.DTE_Processing_Indicator { - top: 5px; - right: 6px; - font-size: 6px; - } - } - - &.DTE_Bubble { - div.DTE_Processing_Indicator { - top: 10px; - right: 14px; - font-size: 8px; - } - } - - div.DTE_Processing_Indicator span, - div.DTE_Processing_Indicator:before, - div.DTE_Processing_Indicator:after { - display: block; - background: black; - width: 0.5em; - height: 1.5em; - border: 1px solid rgba( 0, 0, 0, 0.4 ); - background-color: rgba( 0, 0, 0, 0.1 ); - - -webkit-animation: editorProcessing 0.9s infinite ease-in-out; - animation: editorProcessing 0.9s infinite ease-in-out; - } - - div.DTE_Processing_Indicator:before, - div.DTE_Processing_Indicator:after { - position: absolute; - top: 0; - content: ''; - } - - div.DTE_Processing_Indicator:before { - left: -1em; - -webkit-animation-delay: -0.3s; - animation-delay: -0.3s; - } - - div.DTE_Processing_Indicator span { - -webkit-animation-delay: -0.15s; - animation-delay: -0.15s; - } - - div.DTE_Processing_Indicator:after { - left: 1em; - } - - @-webkit-keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } - } - @keyframes editorProcessing { - 0%, - 80%, - 100% { - transform: scale(1, 1); - } - 40% { - transform: scale(1, 1.5); - } - } -} diff --git a/css/csstables/scss/upload.scss b/css/csstables/scss/upload.scss deleted file mode 100644 index f3ba54d..0000000 --- a/css/csstables/scss/upload.scss +++ /dev/null @@ -1,142 +0,0 @@ - -div.DTE div.editor_upload { - padding-top: 4px; - - div.eu_table { - display: table; - width: 100%; - } - - div.row { - display: table-row; - } - - div.cell { - display: table-cell; - position: relative; - width: 50%; - vertical-align: top; - } - - div.cell + div.cell { - padding-left: 10px; - } - - div.row + div.row { - div.cell { - padding-top: 10px; - } - } - - button.btn, - input[type=file] { - width: 100%; - height: 2.3em; - font-size: 0.8em; - text-align: center; - line-height: 1em; - } - - input[type=file] { - position: absolute; - top: 0; - left: 0; - width: 100%; - opacity: 0; - } - - div.drop { - position: relative; - box-sizing: border-box; - width: 100%; - height: 100%; - border: 3px dashed #ccc; - border-radius: 6px; - min-height: 4em; - color: #999; - padding-top: 3px; - text-align: center; - - &.over { - border: 3px dashed #111; - color: #111; - } - - span { - max-width: 75%; - font-size: 0.85em; - line-height: 1em; - } - } - - div.rendered { - img { - max-width: 8em; - margin: 0 auto; - } - } - - &.noDrop { - div.drop { - display: none; - } - - div.row.second { - display: none; - } - - div.rendered { - margin-top: 10px; - } - } - - &.noClear { - div.clearValue button { - display: none; - } - } - - &.multi { - div.cell { - display: block; - width: 100%; - - div.drop { - min-height: 0; - padding-bottom: 5px; - } - } - - div.clearValue { - display: none; - } - - ul { - list-style-type: none; - margin: 0; - padding: 0; - - li { - position: relative; - margin-top: 0.5em; - - &:first-child { - margin-top: 0; - } - - img { - vertical-align: middle; - } - - button { - position: absolute; - width: 40px; - right: 0; - top: 50%; - margin-top: -1.5em; - } - } - } - } -} - diff --git a/css/csstables/select.dataTables.min.css b/css/csstables/select.dataTables.min.css deleted file mode 100644 index 72b4a7a..0000000 --- a/css/csstables/select.dataTables.min.css +++ /dev/null @@ -1 +0,0 @@ -table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#B0BED9}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#acbad4}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#aab7d1}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#a6b4cd}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable td.select-checkbox,table.dataTable th.select-checkbox{position:relative}table.dataTable td.select-checkbox:before,table.dataTable td.select-checkbox:after,table.dataTable th.select-checkbox:before,table.dataTable th.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable td.select-checkbox:before,table.dataTable th.select-checkbox:before{content:' ';margin-top:-6px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after,table.dataTable tr.selected th.select-checkbox:after{content:'\2714';margin-top:-11px;margin-left:-4px;text-align:center;text-shadow:1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}} diff --git a/css/flat-icon/_notes/dwsync.xml b/css/flat-icon/_notes/dwsync.xml deleted file mode 100644 index 36270ff..0000000 --- a/css/flat-icon/_notes/dwsync.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/css/flat-icon/flaticon.css b/css/flat-icon/flaticon.css deleted file mode 100644 index fc1710d..0000000 --- a/css/flat-icon/flaticon.css +++ /dev/null @@ -1,44 +0,0 @@ -@font-face { - font-family: "Flaticon"; - src: url("flaticon.eot"); - src: url("flaticon.eot#iefix") format("embedded-opentype"), - url("flaticon.woff") format("woff"), - url("flaticon.ttf") format("truetype"), - url("flaticon.svg") format("svg"); - font-weight: normal; - font-style: normal; -} -[class^="flaticon-"]:before, [class*=" flaticon-"]:before, -[class^="flaticon-"]:after, [class*=" flaticon-"]:after { - font-family: Flaticon; - font-size: 25px; -font-style: normal; -margin-left: 15px; -padding-right:5px; -}.flaticon-add58:before { - content: "\e000"; -} -.flaticon-businessman22:before { - content: "\e001"; -} -.flaticon-businessman63:before { - content: "\e002"; -} -.flaticon-dashboard1:before { - content: "\e003"; -} -.flaticon-forms:before { - content: "\e004"; -} -.flaticon-incoming4:before { - content: "\e005"; -} -.flaticon-invoice1:before { - content: "\e006"; -} -.flaticon-mailbox10:before { - content: "\e007"; -} -.flaticon-profile4:before { - content: "\e008"; -} diff --git a/css/flat-icon/flaticon.eot b/css/flat-icon/flaticon.eot deleted file mode 100644 index ac66941..0000000 Binary files a/css/flat-icon/flaticon.eot and /dev/null differ diff --git a/css/flat-icon/flaticon.svg b/css/flat-icon/flaticon.svg deleted file mode 100644 index cfaa699..0000000 --- a/css/flat-icon/flaticon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/css/flat-icon/flaticon.ttf b/css/flat-icon/flaticon.ttf deleted file mode 100644 index 9f40c8a..0000000 Binary files a/css/flat-icon/flaticon.ttf and /dev/null differ diff --git a/css/flat-icon/flaticon.woff b/css/flat-icon/flaticon.woff deleted file mode 100644 index d1486ab..0000000 Binary files a/css/flat-icon/flaticon.woff and /dev/null differ diff --git a/css/foundation-datepicker.css b/css/foundation-datepicker.css deleted file mode 100644 index c29bdad..0000000 --- a/css/foundation-datepicker.css +++ /dev/null @@ -1,150 +0,0 @@ -.datepicker { - display: none; - position: absolute; - padding: 4px; - margin-top: 1px; - direction: ltr; } - .datepicker.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - float: left; - display: none; - min-width: 160px; - list-style: none; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.2); - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding; - background-clip: padding-box; - *border-right-width: 2px; - *border-bottom-width: 2px; - color: #333; - font-size: 13px; - line-height: 18px; } - .datepicker.dropdown-menu th { - padding: 4px 5px; } - .datepicker.dropdown-menu td { - padding: 4px 5px; } - .datepicker table { - border: 0; - margin: 0; - width: auto; } - .datepicker table tr td span { - display: block; - width: 23%; - height: 54px; - line-height: 54px; - float: left; - margin: 1%; - cursor: pointer; } - .datepicker td { - text-align: center; - width: 20px; - height: 20px; - border: 0; - font-size: 12px; - padding: 4px 8px; - background: #fff; - cursor: pointer; } - .datepicker td.active.day, .datepicker td.active.year { - background: #2ba6cb; } - .datepicker td.old, .datepicker td.new { - color: #999; } - .datepicker td span.active { - background: #2ba6cb; } - .datepicker td.day.disabled { - color: #eee; } - .datepicker td span.month.disabled, .datepicker td span.year.disabled { - color: #eee; } - .datepicker th { - text-align: center; - width: 20px; - height: 20px; - border: 0; - font-size: 12px; - padding: 4px 8px; - background: #fff; - cursor: pointer; } - .datepicker th.active.day, .datepicker th.active.year { - background: #2ba6cb; } - .datepicker th.date-switch { - width: 145px; } - .datepicker th span.active { - background: #2ba6cb; } - .datepicker .cw { - font-size: 10px; - width: 12px; - padding: 0 2px 0 5px; - vertical-align: middle; } - .datepicker.days div.datepicker-days { - display: block; } - .datepicker.months div.datepicker-months { - display: block; } - .datepicker.years div.datepicker-years { - display: block; } - .datepicker thead tr:first-child th { - cursor: pointer; } - .datepicker thead tr:first-child th.cw { - cursor: default; - background-color: transparent; } - .datepicker tfoot tr:first-child th { - cursor: pointer; } - -.datepicker-inline { - width: 220px; } - -.datepicker-rtl { - direction: rtl; } - .datepicker-rtl table tr td span { - float: right; } - -.datepicker-dropdown { - top: 0; - left: 0; } - .datepicker-dropdown:before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-bottom-color: 1px solid rgba(0, 0, 0, 0.2); - position: absolute; - top: -7px; - left: 6px; } - .datepicker-dropdown:after { - content: ''; - display: inline-block; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid #fff; - position: absolute; - top: -6px; - left: 7px; } - -.datepicker > div, -.datepicker-dropdown::before, -.datepicker-dropdown::after { - display: none; } - -.datepicker-close { - position: absolute; - top: -30px; - right: 0; - width: 15px; - height: 30px; - padding: 0; - display: none; } - -.table-striped .datepicker table tr td, -.table-striped .datepicker table tr th { - background-color: transparent; } - -/*# sourceMappingURL=foundation-datepicker.css.map */ diff --git a/css/foundation-datepicker.min.css b/css/foundation-datepicker.min.css deleted file mode 100644 index 8c819e4..0000000 --- a/css/foundation-datepicker.min.css +++ /dev/null @@ -1 +0,0 @@ -.datepicker{display:none;position:absolute;padding:4px;margin-top:1px;direction:ltr}.datepicker.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;list-style:none;background-color:#fff;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;color:#333;font-size:13px;line-height:18px}.datepicker.dropdown-menu td,.datepicker.dropdown-menu th{padding:4px 5px}.datepicker table{border:0;margin:0;width:auto}.datepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer}.datepicker td,.datepicker th{text-align:center;width:20px;height:20px;border:0;font-size:12px;padding:4px 8px;cursor:pointer}.datepicker td{background:#fff}.datepicker td span.active,.datepicker td.active.day,.datepicker td.active.year{background:#2ba6cb}.datepicker td.new,.datepicker td.old{color:#999}.datepicker td span.month.disabled,.datepicker td span.year.disabled,.datepicker td.day.disabled{color:#eee}.datepicker th{background:#fff}.datepicker th span.active,.datepicker th.active.day,.datepicker th.active.year{background:#2ba6cb}.datepicker th.date-switch{width:145px}.datepicker .cw{font-size:10px;width:12px;padding:0 2px 0 5px;vertical-align:middle}.datepicker.days div.datepicker-days,.datepicker.months div.datepicker-months,.datepicker.years div.datepicker-years{display:block}.datepicker-dropdown:after,.datepicker-dropdown:before{content:'';display:inline-block;position:absolute}.datepicker thead tr:first-child th{cursor:pointer}.datepicker thead tr:first-child th.cw{cursor:default;background-color:transparent}.datepicker tfoot tr:first-child th{cursor:pointer}.datepicker-inline{width:220px}.datepicker-rtl{direction:rtl}.datepicker-rtl table tr td span{float:right}.datepicker-dropdown{top:0;left:0}.datepicker-dropdown:before{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:1px solid rgba(0,0,0,.2);top:-7px;left:6px}.datepicker-dropdown:after{border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;top:-6px;left:7px}.datepicker-dropdown::after,.datepicker-dropdown::before,.datepicker>div{display:none}.datepicker-close{position:absolute;top:-30px;right:0;width:15px;height:30px;padding:0;display:none}.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{background-color:transparent} \ No newline at end of file diff --git a/css/foundation-icons/_notes/dwsync.xml b/css/foundation-icons/_notes/dwsync.xml deleted file mode 100644 index 6fb76f4..0000000 --- a/css/foundation-icons/_notes/dwsync.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/css/foundation-icons/foundation-icons-.eot b/css/foundation-icons/foundation-icons-.eot deleted file mode 100644 index 1746ad4..0000000 Binary files a/css/foundation-icons/foundation-icons-.eot and /dev/null differ diff --git a/css/foundation-icons/foundation-icons.css b/css/foundation-icons/foundation-icons.css deleted file mode 100644 index c0dce09..0000000 --- a/css/foundation-icons/foundation-icons.css +++ /dev/null @@ -1,596 +0,0 @@ -/* - * Foundation Icons v 3.0 - * Made by ZURB 2013 http://zurb.com/playground/foundation-icon-fonts-3 - * MIT License - */ - -@font-face { - font-family: "foundation-icons"; - src: url("foundation-icons.eot"); - src: url("foundation-icons-.eot#iefix") format("embedded-opentype"), - url("foundation-icons.woff") format("woff"), - url("foundation-icons.ttf") format("truetype"), - url("foundation-icons.svg#fontcustom") format("svg"); - font-weight: normal; - font-style: normal; -} - -.fi-address-book:before, -.fi-alert:before, -.fi-align-center:before, -.fi-align-justify:before, -.fi-align-left:before, -.fi-align-right:before, -.fi-anchor:before, -.fi-annotate:before, -.fi-archive:before, -.fi-arrow-down:before, -.fi-arrow-left:before, -.fi-arrow-right:before, -.fi-arrow-up:before, -.fi-arrows-compress:before, -.fi-arrows-expand:before, -.fi-arrows-in:before, -.fi-arrows-out:before, -.fi-asl:before, -.fi-asterisk:before, -.fi-at-sign:before, -.fi-background-color:before, -.fi-battery-empty:before, -.fi-battery-full:before, -.fi-battery-half:before, -.fi-bitcoin-circle:before, -.fi-bitcoin:before, -.fi-blind:before, -.fi-bluetooth:before, -.fi-bold:before, -.fi-book-bookmark:before, -.fi-book:before, -.fi-bookmark:before, -.fi-braille:before, -.fi-burst-new:before, -.fi-burst-sale:before, -.fi-burst:before, -.fi-calendar:before, -.fi-camera:before, -.fi-check:before, -.fi-checkbox:before, -.fi-clipboard-notes:before, -.fi-clipboard-pencil:before, -.fi-clipboard:before, -.fi-clock:before, -.fi-closed-caption:before, -.fi-cloud:before, -.fi-comment-minus:before, -.fi-comment-quotes:before, -.fi-comment-video:before, -.fi-comment:before, -.fi-comments:before, -.fi-compass:before, -.fi-contrast:before, -.fi-credit-card:before, -.fi-crop:before, -.fi-crown:before, -.fi-css3:before, -.fi-database:before, -.fi-die-five:before, -.fi-die-four:before, -.fi-die-one:before, -.fi-die-six:before, -.fi-die-three:before, -.fi-die-two:before, -.fi-dislike:before, -.fi-dollar-bill:before, -.fi-dollar:before, -.fi-download:before, -.fi-eject:before, -.fi-elevator:before, -.fi-euro:before, -.fi-eye:before, -.fi-fast-forward:before, -.fi-female-symbol:before, -.fi-female:before, -.fi-filter:before, -.fi-first-aid:before, -.fi-flag:before, -.fi-folder-add:before, -.fi-folder-lock:before, -.fi-folder:before, -.fi-foot:before, -.fi-foundation:before, -.fi-graph-bar:before, -.fi-graph-horizontal:before, -.fi-graph-pie:before, -.fi-graph-trend:before, -.fi-guide-dog:before, -.fi-hearing-aid:before, -.fi-heart:before, -.fi-home:before, -.fi-html5:before, -.fi-indent-less:before, -.fi-indent-more:before, -.fi-info:before, -.fi-italic:before, -.fi-key:before, -.fi-laptop:before, -.fi-layout:before, -.fi-lightbulb:before, -.fi-like:before, -.fi-link:before, -.fi-list-bullet:before, -.fi-list-number:before, -.fi-list-thumbnails:before, -.fi-list:before, -.fi-lock:before, -.fi-loop:before, -.fi-magnifying-glass:before, -.fi-mail:before, -.fi-male-female:before, -.fi-male-symbol:before, -.fi-male:before, -.fi-map:before, -.fi-marker:before, -.fi-megaphone:before, -.fi-microphone:before, -.fi-minus-circle:before, -.fi-minus:before, -.fi-mobile-signal:before, -.fi-mobile:before, -.fi-monitor:before, -.fi-mountains:before, -.fi-music:before, -.fi-next:before, -.fi-no-dogs:before, -.fi-no-smoking:before, -.fi-page-add:before, -.fi-page-copy:before, -.fi-page-csv:before, -.fi-page-delete:before, -.fi-page-doc:before, -.fi-page-edit:before, -.fi-page-export-csv:before, -.fi-page-export-doc:before, -.fi-page-export-pdf:before, -.fi-page-export:before, -.fi-page-filled:before, -.fi-page-multiple:before, -.fi-page-pdf:before, -.fi-page-remove:before, -.fi-page-search:before, -.fi-page:before, -.fi-paint-bucket:before, -.fi-paperclip:before, -.fi-pause:before, -.fi-paw:before, -.fi-paypal:before, -.fi-pencil:before, -.fi-photo:before, -.fi-play-circle:before, -.fi-play-video:before, -.fi-play:before, -.fi-plus:before, -.fi-pound:before, -.fi-power:before, -.fi-previous:before, -.fi-price-tag:before, -.fi-pricetag-multiple:before, -.fi-print:before, -.fi-prohibited:before, -.fi-projection-screen:before, -.fi-puzzle:before, -.fi-quote:before, -.fi-record:before, -.fi-refresh:before, -.fi-results-demographics:before, -.fi-results:before, -.fi-rewind-ten:before, -.fi-rewind:before, -.fi-rss:before, -.fi-safety-cone:before, -.fi-save:before, -.fi-share:before, -.fi-sheriff-badge:before, -.fi-shield:before, -.fi-shopping-bag:before, -.fi-shopping-cart:before, -.fi-shuffle:before, -.fi-skull:before, -.fi-social-500px:before, -.fi-social-adobe:before, -.fi-social-amazon:before, -.fi-social-android:before, -.fi-social-apple:before, -.fi-social-behance:before, -.fi-social-bing:before, -.fi-social-blogger:before, -.fi-social-delicious:before, -.fi-social-designer-news:before, -.fi-social-deviant-art:before, -.fi-social-digg:before, -.fi-social-dribbble:before, -.fi-social-drive:before, -.fi-social-dropbox:before, -.fi-social-evernote:before, -.fi-social-facebook:before, -.fi-social-flickr:before, -.fi-social-forrst:before, -.fi-social-foursquare:before, -.fi-social-game-center:before, -.fi-social-github:before, -.fi-social-google-plus:before, -.fi-social-hacker-news:before, -.fi-social-hi5:before, -.fi-social-instagram:before, -.fi-social-joomla:before, -.fi-social-lastfm:before, -.fi-social-linkedin:before, -.fi-social-medium:before, -.fi-social-myspace:before, -.fi-social-orkut:before, -.fi-social-path:before, -.fi-social-picasa:before, -.fi-social-pinterest:before, -.fi-social-rdio:before, -.fi-social-reddit:before, -.fi-social-skillshare:before, -.fi-social-skype:before, -.fi-social-smashing-mag:before, -.fi-social-snapchat:before, -.fi-social-spotify:before, -.fi-social-squidoo:before, -.fi-social-stack-overflow:before, -.fi-social-steam:before, -.fi-social-stumbleupon:before, -.fi-social-treehouse:before, -.fi-social-tumblr:before, -.fi-social-twitter:before, -.fi-social-vimeo:before, -.fi-social-windows:before, -.fi-social-xbox:before, -.fi-social-yahoo:before, -.fi-social-yelp:before, -.fi-social-youtube:before, -.fi-social-zerply:before, -.fi-social-zurb:before, -.fi-sound:before, -.fi-star:before, -.fi-stop:before, -.fi-strikethrough:before, -.fi-subscript:before, -.fi-superscript:before, -.fi-tablet-landscape:before, -.fi-tablet-portrait:before, -.fi-target-two:before, -.fi-target:before, -.fi-telephone-accessible:before, -.fi-telephone:before, -.fi-text-color:before, -.fi-thumbnails:before, -.fi-ticket:before, -.fi-torso-business:before, -.fi-torso-female:before, -.fi-torso:before, -.fi-torsos-all-female:before, -.fi-torsos-all:before, -.fi-torsos-female-male:before, -.fi-torsos-male-female:before, -.fi-torsos:before, -.fi-trash:before, -.fi-trees:before, -.fi-trophy:before, -.fi-underline:before, -.fi-universal-access:before, -.fi-unlink:before, -.fi-unlock:before, -.fi-upload-cloud:before, -.fi-upload:before, -.fi-usb:before, -.fi-video:before, -.fi-volume-none:before, -.fi-volume-strike:before, -.fi-volume:before, -.fi-web:before, -.fi-wheelchair:before, -.fi-widget:before, -.fi-wrench:before, -.fi-x-circle:before, -.fi-x:before, -.fi-yen:before, -.fi-zoom-in:before, -.fi-zoom-out:before { - font-family: "foundation-icons"; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - -webkit-font-smoothing: antialiased; - display: inline-block; - text-decoration: inherit; -} - -.fi-address-book:before { content: "\f100"; } -.fi-alert:before { content: "\f101"; } -.fi-align-center:before { content: "\f102"; } -.fi-align-justify:before { content: "\f103"; } -.fi-align-left:before { content: "\f104"; } -.fi-align-right:before { content: "\f105"; } -.fi-anchor:before { content: "\f106"; } -.fi-annotate:before { content: "\f107"; } -.fi-archive:before { content: "\f108"; } -.fi-arrow-down:before { content: "\f109"; } -.fi-arrow-left:before { content: "\f10a"; } -.fi-arrow-right:before { content: "\f10b"; } -.fi-arrow-up:before { content: "\f10c"; } -.fi-arrows-compress:before { content: "\f10d"; } -.fi-arrows-expand:before { content: "\f10e"; } -.fi-arrows-in:before { content: "\f10f"; } -.fi-arrows-out:before { content: "\f110"; } -.fi-asl:before { content: "\f111"; } -.fi-asterisk:before { content: "\f112"; } -.fi-at-sign:before { content: "\f113"; } -.fi-background-color:before { content: "\f114"; } -.fi-battery-empty:before { content: "\f115"; } -.fi-battery-full:before { content: "\f116"; } -.fi-battery-half:before { content: "\f117"; } -.fi-bitcoin-circle:before { content: "\f118"; } -.fi-bitcoin:before { content: "\f119"; } -.fi-blind:before { content: "\f11a"; } -.fi-bluetooth:before { content: "\f11b"; } -.fi-bold:before { content: "\f11c"; } -.fi-book-bookmark:before { content: "\f11d"; } -.fi-book:before { content: "\f11e"; } -.fi-bookmark:before { content: "\f11f"; } -.fi-braille:before { content: "\f120"; } -.fi-burst-new:before { content: "\f121"; } -.fi-burst-sale:before { content: "\f122"; } -.fi-burst:before { content: "\f123"; } -.fi-calendar:before { content: "\f124"; } -.fi-camera:before { content: "\f125"; } -.fi-check:before { content: "\f126"; } -.fi-checkbox:before { content: "\f127"; } -.fi-clipboard-notes:before { content: "\f128"; } -.fi-clipboard-pencil:before { content: "\f129"; } -.fi-clipboard:before { content: "\f12a"; } -.fi-clock:before { content: "\f12b"; } -.fi-closed-caption:before { content: "\f12c"; } -.fi-cloud:before { content: "\f12d"; } -.fi-comment-minus:before { content: "\f12e"; } -.fi-comment-quotes:before { content: "\f12f"; } -.fi-comment-video:before { content: "\f130"; } -.fi-comment:before { content: "\f131"; } -.fi-comments:before { content: "\f132"; } -.fi-compass:before { content: "\f133"; } -.fi-contrast:before { content: "\f134"; } -.fi-credit-card:before { content: "\f135"; } -.fi-crop:before { content: "\f136"; } -.fi-crown:before { content: "\f137"; } -.fi-css3:before { content: "\f138"; } -.fi-database:before { content: "\f139"; } -.fi-die-five:before { content: "\f13a"; } -.fi-die-four:before { content: "\f13b"; } -.fi-die-one:before { content: "\f13c"; } -.fi-die-six:before { content: "\f13d"; } -.fi-die-three:before { content: "\f13e"; } -.fi-die-two:before { content: "\f13f"; } -.fi-dislike:before { content: "\f140"; } -.fi-dollar-bill:before { content: "\f141"; } -.fi-dollar:before { content: "\f142"; } -.fi-download:before { content: "\f143"; } -.fi-eject:before { content: "\f144"; } -.fi-elevator:before { content: "\f145"; } -.fi-euro:before { content: "\f146"; } -.fi-eye:before { content: "\f147"; } -.fi-fast-forward:before { content: "\f148"; } -.fi-female-symbol:before { content: "\f149"; } -.fi-female:before { content: "\f14a"; } -.fi-filter:before { content: "\f14b"; } -.fi-first-aid:before { content: "\f14c"; } -.fi-flag:before { content: "\f14d"; } -.fi-folder-add:before { content: "\f14e"; } -.fi-folder-lock:before { content: "\f14f"; } -.fi-folder:before { content: "\f150"; } -.fi-foot:before { content: "\f151"; } -.fi-foundation:before { content: "\f152"; } -.fi-graph-bar:before { content: "\f153"; } -.fi-graph-horizontal:before { content: "\f154"; } -.fi-graph-pie:before { content: "\f155"; } -.fi-graph-trend:before { content: "\f156"; } -.fi-guide-dog:before { content: "\f157"; } -.fi-hearing-aid:before { content: "\f158"; } -.fi-heart:before { content: "\f159"; } -.fi-home:before { content: "\f15a"; } -.fi-html5:before { content: "\f15b"; } -.fi-indent-less:before { content: "\f15c"; } -.fi-indent-more:before { content: "\f15d"; } -.fi-info:before { content: "\f15e"; } -.fi-italic:before { content: "\f15f"; } -.fi-key:before { content: "\f160"; } -.fi-laptop:before { content: "\f161"; } -.fi-layout:before { content: "\f162"; } -.fi-lightbulb:before { content: "\f163"; } -.fi-like:before { content: "\f164"; } -.fi-link:before { content: "\f165"; } -.fi-list-bullet:before { content: "\f166"; } -.fi-list-number:before { content: "\f167"; } -.fi-list-thumbnails:before { content: "\f168"; } -.fi-list:before { content: "\f169"; } -.fi-lock:before { content: "\f16a"; } -.fi-loop:before { content: "\f16b"; } -.fi-magnifying-glass:before { content: "\f16c"; } -.fi-mail:before { content: "\f16d"; } -.fi-male-female:before { content: "\f16e"; } -.fi-male-symbol:before { content: "\f16f"; } -.fi-male:before { content: "\f170"; } -.fi-map:before { content: "\f171"; } -.fi-marker:before { content: "\f172"; } -.fi-megaphone:before { content: "\f173"; } -.fi-microphone:before { content: "\f174"; } -.fi-minus-circle:before { content: "\f175"; } -.fi-minus:before { content: "\f176"; } -.fi-mobile-signal:before { content: "\f177"; } -.fi-mobile:before { content: "\f178"; } -.fi-monitor:before { content: "\f179"; } -.fi-mountains:before { content: "\f17a"; } -.fi-music:before { content: "\f17b"; } -.fi-next:before { content: "\f17c"; } -.fi-no-dogs:before { content: "\f17d"; } -.fi-no-smoking:before { content: "\f17e"; } -.fi-page-add:before { content: "\f17f"; } -.fi-page-copy:before { content: "\f180"; } -.fi-page-csv:before { content: "\f181"; } -.fi-page-delete:before { content: "\f182"; } -.fi-page-doc:before { content: "\f183"; } -.fi-page-edit:before { content: "\f184"; } -.fi-page-export-csv:before { content: "\f185"; } -.fi-page-export-doc:before { content: "\f186"; } -.fi-page-export-pdf:before { content: "\f187"; } -.fi-page-export:before { content: "\f188"; } -.fi-page-filled:before { content: "\f189"; } -.fi-page-multiple:before { content: "\f18a"; } -.fi-page-pdf:before { content: "\f18b"; } -.fi-page-remove:before { content: "\f18c"; } -.fi-page-search:before { content: "\f18d"; } -.fi-page:before { content: "\f18e"; } -.fi-paint-bucket:before { content: "\f18f"; } -.fi-paperclip:before { content: "\f190"; } -.fi-pause:before { content: "\f191"; } -.fi-paw:before { content: "\f192"; } -.fi-paypal:before { content: "\f193"; } -.fi-pencil:before { content: "\f194"; } -.fi-photo:before { content: "\f195"; } -.fi-play-circle:before { content: "\f196"; } -.fi-play-video:before { content: "\f197"; } -.fi-play:before { content: "\f198"; } -.fi-plus:before { content: "\f199"; } -.fi-pound:before { content: "\f19a"; } -.fi-power:before { content: "\f19b"; } -.fi-previous:before { content: "\f19c"; } -.fi-price-tag:before { content: "\f19d"; } -.fi-pricetag-multiple:before { content: "\f19e"; } -.fi-print:before { content: "\f19f"; } -.fi-prohibited:before { content: "\f1a0"; } -.fi-projection-screen:before { content: "\f1a1"; } -.fi-puzzle:before { content: "\f1a2"; } -.fi-quote:before { content: "\f1a3"; } -.fi-record:before { content: "\f1a4"; } -.fi-refresh:before { content: "\f1a5"; } -.fi-results-demographics:before { content: "\f1a6"; } -.fi-results:before { content: "\f1a7"; } -.fi-rewind-ten:before { content: "\f1a8"; } -.fi-rewind:before { content: "\f1a9"; } -.fi-rss:before { content: "\f1aa"; } -.fi-safety-cone:before { content: "\f1ab"; } -.fi-save:before { content: "\f1ac"; } -.fi-share:before { content: "\f1ad"; } -.fi-sheriff-badge:before { content: "\f1ae"; } -.fi-shield:before { content: "\f1af"; } -.fi-shopping-bag:before { content: "\f1b0"; } -.fi-shopping-cart:before { content: "\f1b1"; } -.fi-shuffle:before { content: "\f1b2"; } -.fi-skull:before { content: "\f1b3"; } -.fi-social-500px:before { content: "\f1b4"; } -.fi-social-adobe:before { content: "\f1b5"; } -.fi-social-amazon:before { content: "\f1b6"; } -.fi-social-android:before { content: "\f1b7"; } -.fi-social-apple:before { content: "\f1b8"; } -.fi-social-behance:before { content: "\f1b9"; } -.fi-social-bing:before { content: "\f1ba"; } -.fi-social-blogger:before { content: "\f1bb"; } -.fi-social-delicious:before { content: "\f1bc"; } -.fi-social-designer-news:before { content: "\f1bd"; } -.fi-social-deviant-art:before { content: "\f1be"; } -.fi-social-digg:before { content: "\f1bf"; } -.fi-social-dribbble:before { content: "\f1c0"; } -.fi-social-drive:before { content: "\f1c1"; } -.fi-social-dropbox:before { content: "\f1c2"; } -.fi-social-evernote:before { content: "\f1c3"; } -.fi-social-facebook:before { content: "\f1c4"; } -.fi-social-flickr:before { content: "\f1c5"; } -.fi-social-forrst:before { content: "\f1c6"; } -.fi-social-foursquare:before { content: "\f1c7"; } -.fi-social-game-center:before { content: "\f1c8"; } -.fi-social-github:before { content: "\f1c9"; } -.fi-social-google-plus:before { content: "\f1ca"; } -.fi-social-hacker-news:before { content: "\f1cb"; } -.fi-social-hi5:before { content: "\f1cc"; } -.fi-social-instagram:before { content: "\f1cd"; } -.fi-social-joomla:before { content: "\f1ce"; } -.fi-social-lastfm:before { content: "\f1cf"; } -.fi-social-linkedin:before { content: "\f1d0"; } -.fi-social-medium:before { content: "\f1d1"; } -.fi-social-myspace:before { content: "\f1d2"; } -.fi-social-orkut:before { content: "\f1d3"; } -.fi-social-path:before { content: "\f1d4"; } -.fi-social-picasa:before { content: "\f1d5"; } -.fi-social-pinterest:before { content: "\f1d6"; } -.fi-social-rdio:before { content: "\f1d7"; } -.fi-social-reddit:before { content: "\f1d8"; } -.fi-social-skillshare:before { content: "\f1d9"; } -.fi-social-skype:before { content: "\f1da"; } -.fi-social-smashing-mag:before { content: "\f1db"; } -.fi-social-snapchat:before { content: "\f1dc"; } -.fi-social-spotify:before { content: "\f1dd"; } -.fi-social-squidoo:before { content: "\f1de"; } -.fi-social-stack-overflow:before { content: "\f1df"; } -.fi-social-steam:before { content: "\f1e0"; } -.fi-social-stumbleupon:before { content: "\f1e1"; } -.fi-social-treehouse:before { content: "\f1e2"; } -.fi-social-tumblr:before { content: "\f1e3"; } -.fi-social-twitter:before { content: "\f1e4"; } -.fi-social-vimeo:before { content: "\f1e5"; } -.fi-social-windows:before { content: "\f1e6"; } -.fi-social-xbox:before { content: "\f1e7"; } -.fi-social-yahoo:before { content: "\f1e8"; } -.fi-social-yelp:before { content: "\f1e9"; } -.fi-social-youtube:before { content: "\f1ea"; } -.fi-social-zerply:before { content: "\f1eb"; } -.fi-social-zurb:before { content: "\f1ec"; } -.fi-sound:before { content: "\f1ed"; } -.fi-star:before { content: "\f1ee"; } -.fi-stop:before { content: "\f1ef"; } -.fi-strikethrough:before { content: "\f1f0"; } -.fi-subscript:before { content: "\f1f1"; } -.fi-superscript:before { content: "\f1f2"; } -.fi-tablet-landscape:before { content: "\f1f3"; } -.fi-tablet-portrait:before { content: "\f1f4"; } -.fi-target-two:before { content: "\f1f5"; } -.fi-target:before { content: "\f1f6"; } -.fi-telephone-accessible:before { content: "\f1f7"; } -.fi-telephone:before { content: "\f1f8"; } -.fi-text-color:before { content: "\f1f9"; } -.fi-thumbnails:before { content: "\f1fa"; } -.fi-ticket:before { content: "\f1fb"; } -.fi-torso-business:before { content: "\f1fc"; } -.fi-torso-female:before { content: "\f1fd"; } -.fi-torso:before { content: "\f1fe"; } -.fi-torsos-all-female:before { content: "\f1ff"; } -.fi-torsos-all:before { content: "\f200"; } -.fi-torsos-female-male:before { content: "\f201"; } -.fi-torsos-male-female:before { content: "\f202"; } -.fi-torsos:before { content: "\f203"; } -.fi-trash:before { content: "\f204"; } -.fi-trees:before { content: "\f205"; } -.fi-trophy:before { content: "\f206"; } -.fi-underline:before { content: "\f207"; } -.fi-universal-access:before { content: "\f208"; } -.fi-unlink:before { content: "\f209"; } -.fi-unlock:before { content: "\f20a"; } -.fi-upload-cloud:before { content: "\f20b"; } -.fi-upload:before { content: "\f20c"; } -.fi-usb:before { content: "\f20d"; } -.fi-video:before { content: "\f20e"; } -.fi-volume-none:before { content: "\f20f"; } -.fi-volume-strike:before { content: "\f210"; } -.fi-volume:before { content: "\f211"; } -.fi-web:before { content: "\f212"; } -.fi-wheelchair:before { content: "\f213"; } -.fi-widget:before { content: "\f214"; } -.fi-wrench:before { content: "\f215"; } -.fi-x-circle:before { content: "\f216"; } -.fi-x:before { content: "\f217"; } -.fi-yen:before { content: "\f218"; } -.fi-zoom-in:before { content: "\f219"; } -.fi-zoom-out:before { content: "\f21a"; } - -/* Localized */ \ No newline at end of file diff --git a/css/foundation-icons/foundation-icons.eot b/css/foundation-icons/foundation-icons.eot deleted file mode 100644 index 1746ad4..0000000 Binary files a/css/foundation-icons/foundation-icons.eot and /dev/null differ diff --git a/css/foundation-icons/foundation-icons.svg b/css/foundation-icons/foundation-icons.svg deleted file mode 100644 index 4e014ff..0000000 --- a/css/foundation-icons/foundation-icons.svg +++ /dev/null @@ -1,970 +0,0 @@ - - - - - -Created by FontForge 20120731 at Fri Aug 23 09:25:55 2013 - By Jordan Humphreys -Created by Jordan Humphreys with FontForge 2.0 (http://fontforge.sf.net) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/css/foundation-icons/foundation-icons.ttf b/css/foundation-icons/foundation-icons.ttf deleted file mode 100644 index 6cce217..0000000 Binary files a/css/foundation-icons/foundation-icons.ttf and /dev/null differ diff --git a/css/foundation-icons/foundation-icons.woff b/css/foundation-icons/foundation-icons.woff deleted file mode 100644 index e2cfe25..0000000 Binary files a/css/foundation-icons/foundation-icons.woff and /dev/null differ diff --git a/css/foundation.css b/css/foundation.css deleted file mode 100644 index b2d8622..0000000 --- a/css/foundation.css +++ /dev/null @@ -1,5263 +0,0 @@ -meta.foundation-version { - font-family: "/5.2.2/"; } - -meta.foundation-mq-small { - font-family: "/only screen/"; - width: 0em; } - -meta.foundation-mq-medium { - font-family: "/only screen and (min-width:40.063em)/"; - width: 40.063em; } - -meta.foundation-mq-large { - font-family: "/only screen and (min-width:64.063em)/"; - width: 64.063em; } - -meta.foundation-mq-xlarge { - font-family: "/only screen and (min-width:90.063em)/"; - width: 90.063em; } - -meta.foundation-mq-xxlarge { - font-family: "/only screen and (min-width:120.063em)/"; - width: 120.063em; } - -meta.foundation-data-attribute-namespace { - font-family: false; } - -html, body { - height: 100%; } - -*, -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; } - -html, -body { - font-size: 100%; } - -body { - background: white; - color: #222222; - padding: 0; - margin: 0; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - font-style: normal; - line-height: 1; - position: relative; - cursor: default; } - -a:hover { - cursor: pointer; } - -img { - max-width: 100%; - height: auto; } - -img { - -ms-interpolation-mode: bicubic; } - -#map_canvas img, -#map_canvas embed, -#map_canvas object, -.map_canvas img, -.map_canvas embed, -.map_canvas object { - max-width: none !important; } - -.left { - float: left !important; } - -.right { - float: right !important; } - -.clearfix { - *zoom: 1; } - .clearfix:before, .clearfix:after { - content: " "; - display: table; } - .clearfix:after { - clear: both; } - -.hide { - display: none; } - -.antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } - -img { - display: inline-block; - vertical-align: middle; } - -textarea { - height: auto; - min-height: 50px; } - -select { - width: 100%; } - -.row { - width: 100%; - margin-left: auto; - margin-right: auto; - margin-top: 0; - margin-bottom: 0; - max-width: 62.5rem; - *zoom: 1; } - .row:before, .row:after { - content: " "; - display: table; } - .row:after { - clear: both; } - .row.collapse > .column, - .row.collapse > .columns { - padding-left: 0; - padding-right: 0; } - .row.collapse .row { - margin-left: 0; - margin-right: 0; } - .row .row { - width: auto; - margin-left: -0.9375rem; - margin-right: -0.9375rem; - margin-top: 0; - margin-bottom: 0; - max-width: none; - *zoom: 1; } - .row .row:before, .row .row:after { - content: " "; - display: table; } - .row .row:after { - clear: both; } - .row .row.collapse { - width: auto; - margin: 0; - max-width: none; - *zoom: 1; } - .row .row.collapse:before, .row .row.collapse:after { - content: " "; - display: table; } - .row .row.collapse:after { - clear: both; } - -.column, -.columns { - padding-left: 0.9375rem; - padding-right: 0.9375rem; - width: 100%; - float: left; } - -@media only screen { - .small-push-0 { - position: relative; - left: 0%; - right: auto; } - - .small-pull-0 { - position: relative; - right: 0%; - left: auto; } - - .small-push-1 { - position: relative; - left: 8.33333%; - right: auto; } - - .small-pull-1 { - position: relative; - right: 8.33333%; - left: auto; } - - .small-push-2 { - position: relative; - left: 16.66667%; - right: auto; } - - .small-pull-2 { - position: relative; - right: 16.66667%; - left: auto; } - - .small-push-3 { - position: relative; - left: 25%; - right: auto; } - - .small-pull-3 { - position: relative; - right: 25%; - left: auto; } - - .small-push-4 { - position: relative; - left: 33.33333%; - right: auto; } - - .small-pull-4 { - position: relative; - right: 33.33333%; - left: auto; } - - .small-push-5 { - position: relative; - left: 41.66667%; - right: auto; } - - .small-pull-5 { - position: relative; - right: 41.66667%; - left: auto; } - - .small-push-6 { - position: relative; - left: 50%; - right: auto; } - - .small-pull-6 { - position: relative; - right: 50%; - left: auto; } - - .small-push-7 { - position: relative; - left: 58.33333%; - right: auto; } - - .small-pull-7 { - position: relative; - right: 58.33333%; - left: auto; } - - .small-push-8 { - position: relative; - left: 66.66667%; - right: auto; } - - .small-pull-8 { - position: relative; - right: 66.66667%; - left: auto; } - - .small-push-9 { - position: relative; - left: 75%; - right: auto; } - - .small-pull-9 { - position: relative; - right: 75%; - left: auto; } - - .small-push-10 { - position: relative; - left: 83.33333%; - right: auto; } - - .small-pull-10 { - position: relative; - right: 83.33333%; - left: auto; } - - .small-push-11 { - position: relative; - left: 91.66667%; - right: auto; } - - .small-pull-11 { - position: relative; - right: 91.66667%; - left: auto; } - - .column, - .columns { - position: relative; - padding-left: 0.9375rem; - padding-right: 0.9375rem; - float: left; } - - .small-1 { - width: 8.33333%; } - - .small-2 { - width: 16.66667%; } - - .small-3 { - width: 25%; } - - .small-4 { - width: 33.33333%; } - - .small-5 { - width: 41.66667%; } - - .small-6 { - width: 50%; } - - .small-7 { - width: 58.33333%; } - - .small-8 { - width: 66.66667%; } - - .small-9 { - width: 75%; } - - .small-10 { - width: 83.33333%; } - - .small-11 { - width: 91.66667%; } - - .small-12 { - width: 100%; } - - [class*="column"] + [class*="column"]:last-child { - float: right; } - - [class*="column"] + [class*="column"].end { - float: left; } - - .small-offset-0 { - margin-left: 0% !important; } - - .small-offset-1 { - margin-left: 8.33333% !important; } - - .small-offset-2 { - margin-left: 16.66667% !important; } - - .small-offset-3 { - margin-left: 25% !important; } - - .small-offset-4 { - margin-left: 33.33333% !important; } - - .small-offset-5 { - margin-left: 41.66667% !important; } - - .small-offset-6 { - margin-left: 50% !important; } - - .small-offset-7 { - margin-left: 58.33333% !important; } - - .small-offset-8 { - margin-left: 66.66667% !important; } - - .small-offset-9 { - margin-left: 75% !important; } - - .small-offset-10 { - margin-left: 83.33333% !important; } - - .small-offset-11 { - margin-left: 91.66667% !important; } - - .small-reset-order, - .small-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; } - - .column.small-centered, - .columns.small-centered { - margin-left: auto; - margin-right: auto; - float: none; } - - .column.small-uncentered, - .columns.small-uncentered { - margin-left: 0; - margin-right: 0; - float: left !important; } - - .column.small-uncentered.opposite, - .columns.small-uncentered.opposite { - float: right; } } -@media only screen and (min-width: 40.063em) { - .medium-push-0 { - position: relative; - left: 0%; - right: auto; } - - .medium-pull-0 { - position: relative; - right: 0%; - left: auto; } - - .medium-push-1 { - position: relative; - left: 8.33333%; - right: auto; } - - .medium-pull-1 { - position: relative; - right: 8.33333%; - left: auto; } - - .medium-push-2 { - position: relative; - left: 16.66667%; - right: auto; } - - .medium-pull-2 { - position: relative; - right: 16.66667%; - left: auto; } - - .medium-push-3 { - position: relative; - left: 25%; - right: auto; } - - .medium-pull-3 { - position: relative; - right: 25%; - left: auto; } - - .medium-push-4 { - position: relative; - left: 33.33333%; - right: auto; } - - .medium-pull-4 { - position: relative; - right: 33.33333%; - left: auto; } - - .medium-push-5 { - position: relative; - left: 41.66667%; - right: auto; } - - .medium-pull-5 { - position: relative; - right: 41.66667%; - left: auto; } - - .medium-push-6 { - position: relative; - left: 50%; - right: auto; } - - .medium-pull-6 { - position: relative; - right: 50%; - left: auto; } - - .medium-push-7 { - position: relative; - left: 58.33333%; - right: auto; } - - .medium-pull-7 { - position: relative; - right: 58.33333%; - left: auto; } - - .medium-push-8 { - position: relative; - left: 66.66667%; - right: auto; } - - .medium-pull-8 { - position: relative; - right: 66.66667%; - left: auto; } - - .medium-push-9 { - position: relative; - left: 75%; - right: auto; } - - .medium-pull-9 { - position: relative; - right: 75%; - left: auto; } - - .medium-push-10 { - position: relative; - left: 83.33333%; - right: auto; } - - .medium-pull-10 { - position: relative; - right: 83.33333%; - left: auto; } - - .medium-push-11 { - position: relative; - left: 91.66667%; - right: auto; } - - .medium-pull-11 { - position: relative; - right: 91.66667%; - left: auto; } - - .column, - .columns { - position: relative; - padding-left: 0.9375rem; - padding-right: 0.9375rem; - float: left; } - - .medium-1 { - width: 8.33333%; } - - .medium-2 { - width: 16.66667%; } - - .medium-3 { - width: 25%; } - - .medium-4 { - width: 33.33333%; } - - .medium-5 { - width: 41.66667%; } - - .medium-6 { - width: 50%; } - - .medium-7 { - width: 58.33333%; } - - .medium-8 { - width: 66.66667%; } - - .medium-9 { - width: 75%; } - - .medium-10 { - width: 83.33333%; } - - .medium-11 { - width: 91.66667%; } - - .medium-12 { - width: 100%; } - - [class*="column"] + [class*="column"]:last-child { - float: right; } - - [class*="column"] + [class*="column"].end { - float: left; } - - .medium-offset-0 { - margin-left: 0% !important; } - - .medium-offset-1 { - margin-left: 8.33333% !important; } - - .medium-offset-2 { - margin-left: 16.66667% !important; } - - .medium-offset-3 { - margin-left: 25% !important; } - - .medium-offset-4 { - margin-left: 33.33333% !important; } - - .medium-offset-5 { - margin-left: 41.66667% !important; } - - .medium-offset-6 { - margin-left: 50% !important; } - - .medium-offset-7 { - margin-left: 58.33333% !important; } - - .medium-offset-8 { - margin-left: 66.66667% !important; } - - .medium-offset-9 { - margin-left: 75% !important; } - - .medium-offset-10 { - margin-left: 83.33333% !important; } - - .medium-offset-11 { - margin-left: 91.66667% !important; } - - .medium-reset-order, - .medium-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; } - - .column.medium-centered, - .columns.medium-centered { - margin-left: auto; - margin-right: auto; - float: none; } - - .column.medium-uncentered, - .columns.medium-uncentered { - margin-left: 0; - margin-right: 0; - float: left !important; } - - .column.medium-uncentered.opposite, - .columns.medium-uncentered.opposite { - float: right; } - - .push-0 { - position: relative; - left: 0%; - right: auto; } - - .pull-0 { - position: relative; - right: 0%; - left: auto; } - - .push-1 { - position: relative; - left: 8.33333%; - right: auto; } - - .pull-1 { - position: relative; - right: 8.33333%; - left: auto; } - - .push-2 { - position: relative; - left: 16.66667%; - right: auto; } - - .pull-2 { - position: relative; - right: 16.66667%; - left: auto; } - - .push-3 { - position: relative; - left: 25%; - right: auto; } - - .pull-3 { - position: relative; - right: 25%; - left: auto; } - - .push-4 { - position: relative; - left: 33.33333%; - right: auto; } - - .pull-4 { - position: relative; - right: 33.33333%; - left: auto; } - - .push-5 { - position: relative; - left: 41.66667%; - right: auto; } - - .pull-5 { - position: relative; - right: 41.66667%; - left: auto; } - - .push-6 { - position: relative; - left: 50%; - right: auto; } - - .pull-6 { - position: relative; - right: 50%; - left: auto; } - - .push-7 { - position: relative; - left: 58.33333%; - right: auto; } - - .pull-7 { - position: relative; - right: 58.33333%; - left: auto; } - - .push-8 { - position: relative; - left: 66.66667%; - right: auto; } - - .pull-8 { - position: relative; - right: 66.66667%; - left: auto; } - - .push-9 { - position: relative; - left: 75%; - right: auto; } - - .pull-9 { - position: relative; - right: 75%; - left: auto; } - - .push-10 { - position: relative; - left: 83.33333%; - right: auto; } - - .pull-10 { - position: relative; - right: 83.33333%; - left: auto; } - - .push-11 { - position: relative; - left: 91.66667%; - right: auto; } - - .pull-11 { - position: relative; - right: 91.66667%; - left: auto; } } -@media only screen and (min-width: 64.063em) { - .large-push-0 { - position: relative; - left: 0%; - right: auto; } - - .large-pull-0 { - position: relative; - right: 0%; - left: auto; } - - .large-push-1 { - position: relative; - left: 8.33333%; - right: auto; } - - .large-pull-1 { - position: relative; - right: 8.33333%; - left: auto; } - - .large-push-2 { - position: relative; - left: 16.66667%; - right: auto; } - - .large-pull-2 { - position: relative; - right: 16.66667%; - left: auto; } - - .large-push-3 { - position: relative; - left: 25%; - right: auto; } - - .large-pull-3 { - position: relative; - right: 25%; - left: auto; } - - .large-push-4 { - position: relative; - left: 33.33333%; - right: auto; } - - .large-pull-4 { - position: relative; - right: 33.33333%; - left: auto; } - - .large-push-5 { - position: relative; - left: 41.66667%; - right: auto; } - - .large-pull-5 { - position: relative; - right: 41.66667%; - left: auto; } - - .large-push-6 { - position: relative; - left: 50%; - right: auto; } - - .large-pull-6 { - position: relative; - right: 50%; - left: auto; } - - .large-push-7 { - position: relative; - left: 58.33333%; - right: auto; } - - .large-pull-7 { - position: relative; - right: 58.33333%; - left: auto; } - - .large-push-8 { - position: relative; - left: 66.66667%; - right: auto; } - - .large-pull-8 { - position: relative; - right: 66.66667%; - left: auto; } - - .large-push-9 { - position: relative; - left: 75%; - right: auto; } - - .large-pull-9 { - position: relative; - right: 75%; - left: auto; } - - .large-push-10 { - position: relative; - left: 83.33333%; - right: auto; } - - .large-pull-10 { - position: relative; - right: 83.33333%; - left: auto; } - - .large-push-11 { - position: relative; - left: 91.66667%; - right: auto; } - - .large-pull-11 { - position: relative; - right: 91.66667%; - left: auto; } - - .column, - .columns { - position: relative; - padding-left: 0.9375rem; - padding-right: 0.9375rem; - float: left; } - - .large-1 { - width: 8.33333%; } - - .large-2 { - width: 16.66667%; } - - .large-3 { - width: 25%; } - - .large-4 { - width: 33.33333%; } - - .large-5 { - width: 41.66667%; } - - .large-6 { - width: 50%; } - - .large-7 { - width: 58.33333%; } - - .large-8 { - width: 66.66667%; } - - .large-9 { - width: 75%; } - - .large-10 { - width: 83.33333%; } - - .large-11 { - width: 91.66667%; } - - .large-12 { - width: 100%; } - - [class*="column"] + [class*="column"]:last-child { - float: right; } - - [class*="column"] + [class*="column"].end { - float: left; } - - .large-offset-0 { - margin-left: 0% !important; } - - .large-offset-1 { - margin-left: 8.33333% !important; } - - .large-offset-2 { - margin-left: 16.66667% !important; } - - .large-offset-3 { - margin-left: 25% !important; } - - .large-offset-4 { - margin-left: 33.33333% !important; } - - .large-offset-5 { - margin-left: 41.66667% !important; } - - .large-offset-6 { - margin-left: 50% !important; } - - .large-offset-7 { - margin-left: 58.33333% !important; } - - .large-offset-8 { - margin-left: 66.66667% !important; } - - .large-offset-9 { - margin-left: 75% !important; } - - .large-offset-10 { - margin-left: 83.33333% !important; } - - .large-offset-11 { - margin-left: 91.66667% !important; } - - .large-reset-order, - .large-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; } - - .column.large-centered, - .columns.large-centered { - margin-left: auto; - margin-right: auto; - float: none; } - - .column.large-uncentered, - .columns.large-uncentered { - margin-left: 0; - margin-right: 0; - float: left !important; } - - .column.large-uncentered.opposite, - .columns.large-uncentered.opposite { - float: right; } - - .push-0 { - position: relative; - left: 0%; - right: auto; } - - .pull-0 { - position: relative; - right: 0%; - left: auto; } - - .push-1 { - position: relative; - left: 8.33333%; - right: auto; } - - .pull-1 { - position: relative; - right: 8.33333%; - left: auto; } - - .push-2 { - position: relative; - left: 16.66667%; - right: auto; } - - .pull-2 { - position: relative; - right: 16.66667%; - left: auto; } - - .push-3 { - position: relative; - left: 25%; - right: auto; } - - .pull-3 { - position: relative; - right: 25%; - left: auto; } - - .push-4 { - position: relative; - left: 33.33333%; - right: auto; } - - .pull-4 { - position: relative; - right: 33.33333%; - left: auto; } - - .push-5 { - position: relative; - left: 41.66667%; - right: auto; } - - .pull-5 { - position: relative; - right: 41.66667%; - left: auto; } - - .push-6 { - position: relative; - left: 50%; - right: auto; } - - .pull-6 { - position: relative; - right: 50%; - left: auto; } - - .push-7 { - position: relative; - left: 58.33333%; - right: auto; } - - .pull-7 { - position: relative; - right: 58.33333%; - left: auto; } - - .push-8 { - position: relative; - left: 66.66667%; - right: auto; } - - .pull-8 { - position: relative; - right: 66.66667%; - left: auto; } - - .push-9 { - position: relative; - left: 75%; - right: auto; } - - .pull-9 { - position: relative; - right: 75%; - left: auto; } - - .push-10 { - position: relative; - left: 83.33333%; - right: auto; } - - .pull-10 { - position: relative; - right: 83.33333%; - left: auto; } - - .push-11 { - position: relative; - left: 91.66667%; - right: auto; } - - .pull-11 { - position: relative; - right: 91.66667%; - left: auto; } } -button, .button { - border-style: solid; - border-width: 0px; - cursor: pointer; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - line-height: normal; - margin: 0 0 1.25rem; - position: relative; - text-decoration: none; - text-align: center; - -webkit-appearance: none; - -webkit-border-radius: 0; - display: inline-block; - padding-top: 1rem; - padding-right: 2rem; - padding-bottom: 1.0625rem; - padding-left: 2rem; - font-size: 1rem; - background-color: #008cba; - border-color: #007095; - color: white; - transition: background-color 300ms ease-out; } - button:hover, button:focus, .button:hover, .button:focus { - background-color: #007095; } - button:hover, button:focus, .button:hover, .button:focus { - color: white; } - button.secondary, .button.secondary { - background-color: #e7e7e7; - border-color: #b9b9b9; - color: #333333; } - button.secondary:hover, button.secondary:focus, .button.secondary:hover, .button.secondary:focus { - background-color: #b9b9b9; } - button.secondary:hover, button.secondary:focus, .button.secondary:hover, .button.secondary:focus { - color: #333333; } - button.success, .button.success { - background-color: #43ac6a; - border-color: #368a55; - color: white; } - button.success:hover, button.success:focus, .button.success:hover, .button.success:focus { - background-color: #368a55; } - button.success:hover, button.success:focus, .button.success:hover, .button.success:focus { - color: white; } - button.alert, .button.alert { - background-color: #f04124; - border-color: #cf2a0e; - color: white; } - button.alert:hover, button.alert:focus, .button.alert:hover, .button.alert:focus { - background-color: #cf2a0e; } - button.alert:hover, button.alert:focus, .button.alert:hover, .button.alert:focus { - color: white; } - button.large, .button.large { - padding-top: 1.125rem; - padding-right: 2.25rem; - padding-bottom: 1.1875rem; - padding-left: 2.25rem; - font-size: 1.25rem; } - button.small, .button.small { - padding-top: 0.875rem; - padding-right: 1.75rem; - padding-bottom: 0.9375rem; - padding-left: 1.75rem; - font-size: 0.8125rem; } - button.tiny, .button.tiny { - padding-top: 0.625rem; - padding-right: 1.25rem; - padding-bottom: 0.6875rem; - padding-left: 1.25rem; - font-size: 0.6875rem; } - button.expand, .button.expand { - padding-right: 0; - padding-left: 0; - width: 100%; } - button.left-align, .button.left-align { - text-align: left; - text-indent: 0.75rem; } - button.right-align, .button.right-align { - text-align: right; - padding-right: 0.75rem; } - button.radius, .button.radius { - border-radius: 3px; } - button.round, .button.round { - border-radius: 1000px; } - button.disabled, button[disabled], .button.disabled, .button[disabled] { - background-color: #008cba; - border-color: #007095; - color: white; - cursor: default; - opacity: 0.7; - box-shadow: none; } - button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { - background-color: #007095; } - button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { - color: white; } - button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { - background-color: #008cba; } - button.disabled.secondary, button[disabled].secondary, .button.disabled.secondary, .button[disabled].secondary { - background-color: #e7e7e7; - border-color: #b9b9b9; - color: #333333; - cursor: default; - opacity: 0.7; - box-shadow: none; } - button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { - background-color: #b9b9b9; } - button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { - color: #333333; } - button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { - background-color: #e7e7e7; } - button.disabled.success, button[disabled].success, .button.disabled.success, .button[disabled].success { - background-color: #43ac6a; - border-color: #368a55; - color: white; - cursor: default; - opacity: 0.7; - box-shadow: none; } - button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { - background-color: #368a55; } - button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { - color: white; } - button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { - background-color: #43ac6a; } - button.disabled.alert, button[disabled].alert, .button.disabled.alert, .button[disabled].alert { - background-color: #f04124; - border-color: #cf2a0e; - color: white; - cursor: default; - opacity: 0.7; - box-shadow: none; } - button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { - background-color: #cf2a0e; } - button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { - color: white; } - button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { - background-color: #f04124; } - -@media only screen and (min-width: 40.063em) { - button, .button { - display: inline-block; } } -meta.foundation-mq-topbar { - font-family: "/only screen and (min-width:40.063em)/"; - width: 40.063em; } - -/* Wrapped around .top-bar to contain to grid width */ -.contain-to-grid { - width: 100%; - background: #333333; } - .contain-to-grid .top-bar { - margin-bottom: 0; } - -.fixed { - width: 100%; - left: 0; - position: fixed; - top: 0; - z-index: 99; } - .fixed.expanded:not(.top-bar) { - overflow-y: auto; - height: auto; - width: 100%; - max-height: 100%; } - .fixed.expanded:not(.top-bar) .title-area { - position: fixed; - width: 100%; - z-index: 99; } - .fixed.expanded:not(.top-bar) .top-bar-section { - z-index: 98; - margin-top: 45px; } - -.top-bar { - overflow: hidden; - height: 45px; - line-height: 45px; - position: relative; - background: #333333; - margin-bottom: 0; } - .top-bar ul { - margin-bottom: 0; - list-style: none; } - .top-bar .row { - max-width: none; } - .top-bar form, - .top-bar input { - margin-bottom: 0; } - .top-bar input { - height: auto; - padding-top: .35rem; - padding-bottom: .35rem; - font-size: 0.75rem; } - .top-bar .button, .top-bar button { - padding-top: .45rem; - padding-bottom: .35rem; - margin-bottom: 0; - font-size: 0.75rem; } - .top-bar .title-area { - position: relative; - margin: 0; } - .top-bar .name { - height: 45px; - margin: 0; - font-size: 16px; } - .top-bar .name h1 { - line-height: 45px; - font-size: 1.0625rem; - margin: 0; } - .top-bar .name h1 a { - font-weight: normal; - color: white; - width: 75%; - display: block; - padding: 0 15px; } - .top-bar .toggle-topbar { - position: absolute; - right: 0; - top: 0; } - .top-bar .toggle-topbar a { - color: white; - text-transform: uppercase; - font-size: 0.8125rem; - font-weight: bold; - position: relative; - display: block; - padding: 0 15px; - height: 45px; - line-height: 45px; } - .top-bar .toggle-topbar.menu-icon { - right: 15px; - top: 50%; - margin-top: -16px; - padding-left: 40px; } - .top-bar .toggle-topbar.menu-icon a { - height: 34px; - line-height: 33px; - padding: 0; - padding-right: 25px; - color: white; - position: relative; } - .top-bar .toggle-topbar.menu-icon a::after { - content: ""; - position: absolute; - right: 0; - display: block; - width: 16px; - top: 0; - height: 0; - box-shadow: 0 10px 0 1px white, 0 16px 0 1px white, 0 22px 0 1px white; } - .top-bar.expanded { - height: auto; - background: transparent; } - .top-bar.expanded .title-area { - background: #333333; } - .top-bar.expanded .toggle-topbar a { - color: #888888; } - .top-bar.expanded .toggle-topbar a::after { - box-shadow: 0 10px 0 1px #888888, 0 16px 0 1px #888888, 0 22px 0 1px #888888; } - -.top-bar-section { - left: 0; - position: relative; - width: auto; - transition: left 300ms ease-out; } - .top-bar-section ul { - width: 100%; - height: auto; - display: block; - background: #333333; - font-size: 16px; - margin: 0; } - .top-bar-section .divider, - .top-bar-section [role="separator"] { - border-top: solid 1px #1a1a1a; - clear: both; - height: 1px; - width: 100%; } - .top-bar-section ul li > a { - display: block; - width: 100%; - color: white; - padding: 12px 0 12px 0; - padding-left: 15px; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-size: 0.8125rem; - font-weight: normal; - text-transform: none; - background: #333333; } - .top-bar-section ul li > a.button { - font-size: 0.8125rem; - padding-right: 15px; - padding-left: 15px; - background-color: #008cba; - border-color: #007095; - color: white; } - .top-bar-section ul li > a.button:hover, .top-bar-section ul li > a.button:focus { - background-color: #007095; } - .top-bar-section ul li > a.button:hover, .top-bar-section ul li > a.button:focus { - color: white; } - .top-bar-section ul li > a.button.secondary { - background-color: #e7e7e7; - border-color: #b9b9b9; - color: #333333; } - .top-bar-section ul li > a.button.secondary:hover, .top-bar-section ul li > a.button.secondary:focus { - background-color: #b9b9b9; } - .top-bar-section ul li > a.button.secondary:hover, .top-bar-section ul li > a.button.secondary:focus { - color: #333333; } - .top-bar-section ul li > a.button.success { - background-color: #43ac6a; - border-color: #368a55; - color: white; } - .top-bar-section ul li > a.button.success:hover, .top-bar-section ul li > a.button.success:focus { - background-color: #368a55; } - .top-bar-section ul li > a.button.success:hover, .top-bar-section ul li > a.button.success:focus { - color: white; } - .top-bar-section ul li > a.button.alert { - background-color: #f04124; - border-color: #cf2a0e; - color: white; } - .top-bar-section ul li > a.button.alert:hover, .top-bar-section ul li > a.button.alert:focus { - background-color: #cf2a0e; } - .top-bar-section ul li > a.button.alert:hover, .top-bar-section ul li > a.button.alert:focus { - color: white; } - .top-bar-section ul li > button { - font-size: 0.8125rem; - padding-right: 15px; - padding-left: 15px; - background-color: #008cba; - border-color: #007095; - color: white; } - .top-bar-section ul li > button:hover, .top-bar-section ul li > button:focus { - background-color: #007095; } - .top-bar-section ul li > button:hover, .top-bar-section ul li > button:focus { - color: white; } - .top-bar-section ul li > button.secondary { - background-color: #e7e7e7; - border-color: #b9b9b9; - color: #333333; } - .top-bar-section ul li > button.secondary:hover, .top-bar-section ul li > button.secondary:focus { - background-color: #b9b9b9; } - .top-bar-section ul li > button.secondary:hover, .top-bar-section ul li > button.secondary:focus { - color: #333333; } - .top-bar-section ul li > button.success { - background-color: #43ac6a; - border-color: #368a55; - color: white; } - .top-bar-section ul li > button.success:hover, .top-bar-section ul li > button.success:focus { - background-color: #368a55; } - .top-bar-section ul li > button.success:hover, .top-bar-section ul li > button.success:focus { - color: white; } - .top-bar-section ul li > button.alert { - background-color: #f04124; - border-color: #cf2a0e; - color: white; } - .top-bar-section ul li > button.alert:hover, .top-bar-section ul li > button.alert:focus { - background-color: #cf2a0e; } - .top-bar-section ul li > button.alert:hover, .top-bar-section ul li > button.alert:focus { - color: white; } - .top-bar-section ul li:hover:not(.has-form) > a { - background: #272727; - color: white; } - .top-bar-section ul li.active > a { - background: #008cba; - color: white; } - .top-bar-section ul li.active > a:hover { - background: #0078a0; - color: white; } - .top-bar-section .has-form { - padding: 15px; } - .top-bar-section .has-dropdown { - position: relative; } - .top-bar-section .has-dropdown > a:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 5px; - border-color: transparent transparent transparent rgba(255, 255, 255, 0.4); - border-left-style: solid; - margin-right: 15px; - margin-top: -4.5px; - position: absolute; - top: 50%; - right: 0; } - .top-bar-section .has-dropdown.moved { - position: static; } - .top-bar-section .has-dropdown.moved > .dropdown { - display: block; - position: static !important; - height: auto; - width: auto; - overflow: visible; - clip: auto; - position: absolute !important; - width: 100%; } - .top-bar-section .has-dropdown.moved > a:after { - display: none; } - .top-bar-section .dropdown { - position: absolute; - left: 100%; - top: 0; - z-index: 99; - display: block; - position: absolute !important; - height: 1px; - width: 1px; - overflow: hidden; - clip: rect(1px, 1px, 1px, 1px); } - .top-bar-section .dropdown li { - width: 100%; - height: auto; } - .top-bar-section .dropdown li a { - font-weight: normal; - padding: 8px 15px; } - .top-bar-section .dropdown li a.parent-link { - font-weight: normal; } - .top-bar-section .dropdown li.title h5 { - margin-bottom: 0; } - .top-bar-section .dropdown li.title h5 a { - color: white; - line-height: 22.5px; - display: block; } - .top-bar-section .dropdown li.has-form { - padding: 8px 15px; } - .top-bar-section .dropdown li .button, .top-bar-section .dropdown li button { - top: auto; } - .top-bar-section .dropdown label { - padding: 8px 15px 2px; - margin-bottom: 0; - text-transform: uppercase; - color: #777777; - font-weight: bold; - font-size: 0.625rem; } - -.js-generated { - display: block; } - -@media only screen and (min-width: 40.063em) { - .top-bar { - background: #333333; - *zoom: 1; - overflow: visible; } - .top-bar:before, .top-bar:after { - content: " "; - display: table; } - .top-bar:after { - clear: both; } - .top-bar .toggle-topbar { - display: none; } - .top-bar .title-area { - float: left; } - .top-bar .name h1 a { - width: auto; } - .top-bar input, - .top-bar .button, - .top-bar button { - font-size: 0.875rem; - position: relative; - top: 7px; } - .top-bar.expanded { - background: #333333; } - - .contain-to-grid .top-bar { - max-width: 62.5rem; - margin: 0 auto; - margin-bottom: 0; } - - .top-bar-section { - transition: none 0 0; - left: 0 !important; } - .top-bar-section ul { - width: auto; - height: auto !important; - display: inline; } - .top-bar-section ul li { - float: left; } - .top-bar-section ul li .js-generated { - display: none; } - .top-bar-section li.hover > a:not(.button) { - background: #272727; - color: white; } - .top-bar-section li:not(.has-form) a:not(.button) { - padding: 0 15px; - line-height: 45px; - background: #333333; } - .top-bar-section li:not(.has-form) a:not(.button):hover { - background: #272727; } - .top-bar-section li.active:not(.has-form) a:not(.button) { - padding: 0 15px; - line-height: 45px; - color: white; - background: #008cba; } - .top-bar-section li.active:not(.has-form) a:not(.button):hover { - background: #0078a0; } - .top-bar-section .has-dropdown > a { - padding-right: 35px !important; } - .top-bar-section .has-dropdown > a:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 5px; - border-color: rgba(255, 255, 255, 0.4) transparent transparent transparent; - border-top-style: solid; - margin-top: -2.5px; - top: 22.5px; } - .top-bar-section .has-dropdown.moved { - position: relative; } - .top-bar-section .has-dropdown.moved > .dropdown { - display: block; - position: absolute !important; - height: 1px; - width: 1px; - overflow: hidden; - clip: rect(1px, 1px, 1px, 1px); } - .top-bar-section .has-dropdown.hover > .dropdown, .top-bar-section .has-dropdown.not-click:hover > .dropdown { - display: block; - position: static !important; - height: auto; - width: auto; - overflow: visible; - clip: auto; - position: absolute !important; } - .top-bar-section .has-dropdown .dropdown li.has-dropdown > a:after { - border: none; - content: "\00bb"; - top: 1rem; - margin-top: -1px; - right: 5px; - line-height: 1.2; } - .top-bar-section .dropdown { - left: 0; - top: auto; - background: transparent; - min-width: 100%; } - .top-bar-section .dropdown li a { - color: white; - line-height: 1; - white-space: nowrap; - padding: 12px 15px; - background: #333333; } - .top-bar-section .dropdown li:not(.has-form) a:not(.button) { - color: white; - background: #333333; } - .top-bar-section .dropdown li:not(.has-form):hover > a:not(.button) { - color: white; - background: #272727; } - .top-bar-section .dropdown li label { - white-space: nowrap; - background: #333333; } - .top-bar-section .dropdown li .dropdown { - left: 100%; - top: 0; } - .top-bar-section > ul > .divider, .top-bar-section > ul > [role="separator"] { - border-bottom: none; - border-top: none; - border-right: solid 1px #4e4e4e; - clear: none; - height: 45px; - width: 0; } - .top-bar-section .has-form { - background: #333333; - padding: 0 15px; - height: 45px; } - .top-bar-section .right li .dropdown { - left: auto; - right: 0; } - .top-bar-section .right li .dropdown li .dropdown { - right: 100%; } - .top-bar-section .left li .dropdown { - right: auto; - left: 0; } - .top-bar-section .left li .dropdown li .dropdown { - left: 100%; } - - .no-js .top-bar-section ul li:hover > a { - background: #272727; - color: white; } - .no-js .top-bar-section ul li:active > a { - background: #008cba; - color: white; } - .no-js .top-bar-section .has-dropdown:hover > .dropdown { - display: block; - position: static !important; - height: auto; - width: auto; - overflow: visible; - clip: auto; - position: absolute !important; } } -.breadcrumbs { - display: block; - padding: 0.5625rem 0.875rem 0.5625rem; - overflow: hidden; - margin-left: 0; - list-style: none; - border-style: solid; - border-width: 1px; - background-color: #f4f4f4; - border-color: gainsboro; - border-radius: 3px; } - .breadcrumbs > * { - margin: 0; - float: left; - font-size: 0.6875rem; - line-height: 0.6875rem; - text-transform: uppercase; - color: #008cba; } - .breadcrumbs > *:hover a, .breadcrumbs > *:focus a { - text-decoration: underline; } - .breadcrumbs > * a { - color: #008cba; } - .breadcrumbs > *.current { - cursor: default; - color: #333333; } - .breadcrumbs > *.current a { - cursor: default; - color: #333333; } - .breadcrumbs > *.current:hover, .breadcrumbs > *.current:hover a, .breadcrumbs > *.current:focus, .breadcrumbs > *.current:focus a { - text-decoration: none; } - .breadcrumbs > *.unavailable { - color: #999999; } - .breadcrumbs > *.unavailable a { - color: #999999; } - .breadcrumbs > *.unavailable:hover, .breadcrumbs > *.unavailable:hover a, .breadcrumbs > *.unavailable:focus, - .breadcrumbs > *.unavailable a:focus { - text-decoration: none; - color: #999999; - cursor: default; } - .breadcrumbs > *:before { - content: "/"; - color: #aaaaaa; - margin: 0 0.75rem; - position: relative; - top: 1px; } - .breadcrumbs > *:first-child:before { - content: " "; - margin: 0; } - -.alert-box { - border-style: solid; - border-width: 1px; - display: block; - font-weight: normal; - margin-bottom: 1.25rem; - position: relative; - padding: 0.875rem 1.5rem 0.875rem 0.875rem; - font-size: 0.8125rem; - transition: opacity 300ms ease-out; - background-color: #008cba; - border-color: #0078a0; - color: white; } - .alert-box .close { - font-size: 1.375rem; - padding: 9px 6px 4px; - line-height: 0; - position: absolute; - top: 50%; - margin-top: -0.6875rem; - right: 0.25rem; - color: #333333; - opacity: 0.3; } - .alert-box .close:hover, .alert-box .close:focus { - opacity: 0.5; } - .alert-box.radius { - border-radius: 3px; } - .alert-box.round { - border-radius: 1000px; } - .alert-box.success { - background-color: #43ac6a; - border-color: #3a945b; - color: white; } - .alert-box.alert { - background-color: #f04124; - border-color: #de2d0f; - color: white; } - .alert-box.secondary { - background-color: #e7e7e7; - border-color: #c7c7c7; - color: #4f4f4f; } - .alert-box.warning { - background-color: #f08a24; - border-color: #de770f; - color: white; } - .alert-box.info { - background-color: #a0d3e8; - border-color: #74bfdd; - color: #4f4f4f; } - .alert-box.alert-close { - opacity: 0; } - -.inline-list { - margin: 0 auto 1.0625rem auto; - margin-left: -1.375rem; - margin-right: 0; - padding: 0; - list-style: none; - overflow: hidden; } - .inline-list > li { - list-style: none; - float: left; - margin-left: 1.375rem; - display: block; } - .inline-list > li > * { - display: block; } - -.button-group { - list-style: none; - margin: 0; - left: 0; - *zoom: 1; } - .button-group:before, .button-group:after { - content: " "; - display: table; } - .button-group:after { - clear: both; } - .button-group li { - margin: 0; - float: left; } - .button-group li > button, .button-group li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group li:first-child button, .button-group li:first-child .button { - border-left: 0; } - .button-group li:first-child { - margin-left: 0; } - .button-group.radius > * > button, .button-group.radius > * .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.radius > *:first-child button, .button-group.radius > *:first-child .button { - border-left: 0; } - .button-group.radius > *:first-child, .button-group.radius > *:first-child > a, .button-group.radius > *:first-child > button, .button-group.radius > *:first-child > .button { - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - .button-group.radius > *:last-child, .button-group.radius > *:last-child > a, .button-group.radius > *:last-child > button, .button-group.radius > *:last-child > .button { - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - .button-group.round > * > button, .button-group.round > * .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.round > *:first-child button, .button-group.round > *:first-child .button { - border-left: 0; } - .button-group.round > *:first-child, .button-group.round > *:first-child > a, .button-group.round > *:first-child > button, .button-group.round > *:first-child > .button { - border-bottom-left-radius: 1000px; - border-top-left-radius: 1000px; } - .button-group.round > *:last-child, .button-group.round > *:last-child > a, .button-group.round > *:last-child > button, .button-group.round > *:last-child > .button { - border-bottom-right-radius: 1000px; - border-top-right-radius: 1000px; } - .button-group.even-2 li { - width: 50%; } - .button-group.even-2 li > button, .button-group.even-2 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-2 li:first-child button, .button-group.even-2 li:first-child .button { - border-left: 0; } - .button-group.even-2 li button, .button-group.even-2 li .button { - width: 100%; } - .button-group.even-3 li { - width: 33.33333%; } - .button-group.even-3 li > button, .button-group.even-3 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-3 li:first-child button, .button-group.even-3 li:first-child .button { - border-left: 0; } - .button-group.even-3 li button, .button-group.even-3 li .button { - width: 100%; } - .button-group.even-4 li { - width: 25%; } - .button-group.even-4 li > button, .button-group.even-4 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-4 li:first-child button, .button-group.even-4 li:first-child .button { - border-left: 0; } - .button-group.even-4 li button, .button-group.even-4 li .button { - width: 100%; } - .button-group.even-5 li { - width: 20%; } - .button-group.even-5 li > button, .button-group.even-5 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-5 li:first-child button, .button-group.even-5 li:first-child .button { - border-left: 0; } - .button-group.even-5 li button, .button-group.even-5 li .button { - width: 100%; } - .button-group.even-6 li { - width: 16.66667%; } - .button-group.even-6 li > button, .button-group.even-6 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-6 li:first-child button, .button-group.even-6 li:first-child .button { - border-left: 0; } - .button-group.even-6 li button, .button-group.even-6 li .button { - width: 100%; } - .button-group.even-7 li { - width: 14.28571%; } - .button-group.even-7 li > button, .button-group.even-7 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-7 li:first-child button, .button-group.even-7 li:first-child .button { - border-left: 0; } - .button-group.even-7 li button, .button-group.even-7 li .button { - width: 100%; } - .button-group.even-8 li { - width: 12.5%; } - .button-group.even-8 li > button, .button-group.even-8 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-8 li:first-child button, .button-group.even-8 li:first-child .button { - border-left: 0; } - .button-group.even-8 li button, .button-group.even-8 li .button { - width: 100%; } - -.button-bar { - *zoom: 1; } - .button-bar:before, .button-bar:after { - content: " "; - display: table; } - .button-bar:after { - clear: both; } - .button-bar .button-group { - float: left; - margin-right: 0.625rem; } - .button-bar .button-group div { - overflow: hidden; } - -/* Panels */ -.panel { - border-style: solid; - border-width: 1px; - border-color: #d8d8d8; - margin-bottom: 1.25rem; - padding: 1.25rem; - background: #f2f2f2; } - .panel > :first-child { - margin-top: 0; } - .panel > :last-child { - margin-bottom: 0; } - .panel h1, .panel h2, .panel h3, .panel h4, .panel h5, .panel h6, .panel p { - color: #333333; } - .panel h1, .panel h2, .panel h3, .panel h4, .panel h5, .panel h6 { - line-height: 1; - margin-bottom: 0.625rem; } - .panel h1.subheader, .panel h2.subheader, .panel h3.subheader, .panel h4.subheader, .panel h5.subheader, .panel h6.subheader { - line-height: 1.4; } - .panel.callout { - border-style: solid; - border-width: 1px; - border-color: #b6edff; - margin-bottom: 1.25rem; - padding: 1.25rem; - background: #ecfaff; } - .panel.callout > :first-child { - margin-top: 0; } - .panel.callout > :last-child { - margin-bottom: 0; } - .panel.callout h1, .panel.callout h2, .panel.callout h3, .panel.callout h4, .panel.callout h5, .panel.callout h6, .panel.callout p { - color: #333333; } - .panel.callout h1, .panel.callout h2, .panel.callout h3, .panel.callout h4, .panel.callout h5, .panel.callout h6 { - line-height: 1; - margin-bottom: 0.625rem; } - .panel.callout h1.subheader, .panel.callout h2.subheader, .panel.callout h3.subheader, .panel.callout h4.subheader, .panel.callout h5.subheader, .panel.callout h6.subheader { - line-height: 1.4; } - .panel.callout a:not(.button) { - color: #008cba; } - .panel.radius { - border-radius: 3px; } - -.dropdown.button, button.dropdown { - position: relative; - padding-right: 3.5625rem; } - .dropdown.button:before, button.dropdown:before { - position: absolute; - content: ""; - width: 0; - height: 0; - display: block; - border-style: solid; - border-color: white transparent transparent transparent; - top: 50%; } - .dropdown.button:before, button.dropdown:before { - border-width: 0.375rem; - right: 1.40625rem; - margin-top: -0.15625rem; } - .dropdown.button:before, button.dropdown:before { - border-color: white transparent transparent transparent; } - .dropdown.button.tiny, button.dropdown.tiny { - padding-right: 2.625rem; } - .dropdown.button.tiny:before, button.dropdown.tiny:before { - border-width: 0.375rem; - right: 1.125rem; - margin-top: -0.125rem; } - .dropdown.button.tiny:before, button.dropdown.tiny:before { - border-color: white transparent transparent transparent; } - .dropdown.button.small, button.dropdown.small { - padding-right: 3.0625rem; } - .dropdown.button.small:before, button.dropdown.small:before { - border-width: 0.4375rem; - right: 1.3125rem; - margin-top: -0.15625rem; } - .dropdown.button.small:before, button.dropdown.small:before { - border-color: white transparent transparent transparent; } - .dropdown.button.large, button.dropdown.large { - padding-right: 3.625rem; } - .dropdown.button.large:before, button.dropdown.large:before { - border-width: 0.3125rem; - right: 1.71875rem; - margin-top: -0.15625rem; } - .dropdown.button.large:before, button.dropdown.large:before { - border-color: white transparent transparent transparent; } - .dropdown.button.secondary:before, button.dropdown.secondary:before { - border-color: #333333 transparent transparent transparent; } - -div.switch { - position: relative; - padding: 0; - display: block; - overflow: hidden; - border-style: solid; - border-width: 1px; - margin-bottom: 1.25rem; - height: 2.25rem; - background: white; - border-color: #cccccc; } - div.switch label { - position: relative; - left: 0; - z-index: 2; - float: left; - width: 50%; - height: 100%; - margin: 0; - font-weight: bold; - text-align: left; - transition: all 0.1s ease-out; } - div.switch input { - position: absolute; - z-index: 3; - opacity: 0; - width: 100%; - height: 100%; - -moz-appearance: none; } - div.switch input:hover, div.switch input:focus { - cursor: pointer; } - div.switch span:last-child { - position: absolute; - top: -1px; - left: -1px; - z-index: 1; - display: block; - padding: 0; - border-width: 1px; - border-style: solid; - transition: all 0.1s ease-out; } - div.switch input:not(:checked) + label { - opacity: 0; } - div.switch input:checked { - display: none !important; } - div.switch input { - left: 0; - display: block !important; } - div.switch input:first-of-type + label, - div.switch input:first-of-type + span + label { - left: -50%; } - div.switch input:first-of-type:checked + label, - div.switch input:first-of-type:checked + span + label { - left: 0%; } - div.switch input:last-of-type + label, - div.switch input:last-of-type + span + label { - right: -50%; - left: auto; - text-align: right; } - div.switch input:last-of-type:checked + label, - div.switch input:last-of-type:checked + span + label { - right: 0%; - left: auto; } - div.switch span.custom { - display: none !important; } - form.custom div.switch .hidden-field { - margin-left: auto; - position: absolute; - visibility: visible; } - div.switch label { - padding: 0; - line-height: 2.3rem; - font-size: 0.875rem; } - div.switch input:first-of-type:checked ~ span:last-child { - left: 100%; - margin-left: -2.1875rem; } - div.switch span:last-child { - width: 2.25rem; - height: 2.25rem; } - div.switch span:last-child { - border-color: #b3b3b3; - background: white; - background: linear-gradient(to bottom, white 0%, #f2f2f2 100%); - box-shadow: 2px 0 10px 0 rgba(0, 0, 0, 0.07), 1000px 0 0 980px #f3faf6, -2px 0 10px 0 rgba(0, 0, 0, 0.07), -1000px 0 0 1000px whitesmoke; } - div.switch:hover span:last-child, div.switch:focus span:last-child { - background: white; - background: linear-gradient(to bottom, white 0%, #e6e6e6 100%); } - div.switch:active { - background: transparent; } - div.switch.large { - height: 2.75rem; } - div.switch.large label { - padding: 0; - line-height: 2.3rem; - font-size: 1.0625rem; } - div.switch.large input:first-of-type:checked ~ span:last-child { - left: 100%; - margin-left: -2.6875rem; } - div.switch.large span:last-child { - width: 2.75rem; - height: 2.75rem; } - div.switch.small { - height: 1.75rem; } - div.switch.small label { - padding: 0; - line-height: 2.1rem; - font-size: 0.75rem; } - div.switch.small input:first-of-type:checked ~ span:last-child { - left: 100%; - margin-left: -1.6875rem; } - div.switch.small span:last-child { - width: 1.75rem; - height: 1.75rem; } - div.switch.tiny { - height: 1.375rem; } - div.switch.tiny label { - padding: 0; - line-height: 1.9rem; - font-size: 0.6875rem; } - div.switch.tiny input:first-of-type:checked ~ span:last-child { - left: 100%; - margin-left: -1.3125rem; } - div.switch.tiny span:last-child { - width: 1.375rem; - height: 1.375rem; } - div.switch.radius { - border-radius: 4px; } - div.switch.radius span:last-child { - border-radius: 3px; } - div.switch.round { - border-radius: 1000px; } - div.switch.round span:last-child { - border-radius: 999px; } - div.switch.round label { - padding: 0 0.5625rem; } - -/* Image Thumbnails */ -.th { - line-height: 0; - display: inline-block; - border: solid 4px white; - max-width: 100%; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); - transition: all 200ms ease-out; } - .th:hover, .th:focus { - box-shadow: 0 0 6px 1px rgba(0, 140, 186, 0.5); } - .th.radius { - border-radius: 3px; } - -/* Pricing Tables */ -.pricing-table { - border: solid 1px #dddddd; - margin-left: 0; - margin-bottom: 1.25rem; } - .pricing-table * { - list-style: none; - line-height: 1; } - .pricing-table .title { - background-color: #333333; - padding: 0.9375rem 1.25rem; - text-align: center; - color: #eeeeee; - font-weight: normal; - font-size: 1rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .pricing-table .price { - background-color: #f6f6f6; - padding: 0.9375rem 1.25rem; - text-align: center; - color: #333333; - font-weight: normal; - font-size: 2rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .pricing-table .description { - background-color: white; - padding: 0.9375rem; - text-align: center; - color: #777777; - font-size: 0.75rem; - font-weight: normal; - line-height: 1.4; - border-bottom: dotted 1px #dddddd; } - .pricing-table .bullet-item { - background-color: white; - padding: 0.9375rem; - text-align: center; - color: #333333; - font-size: 0.875rem; - font-weight: normal; - border-bottom: dotted 1px #dddddd; } - .pricing-table .cta-button { - background-color: white; - text-align: center; - padding: 1.25rem 1.25rem 0; } - -@keyframes rotate { - from { - -webkit-transform: rotate(0deg); - -moz-transform: rotate(0deg); - -ms-transform: rotate(0deg); - -o-transform: rotate(0deg); - transform: rotate(0deg); } - - to { - -webkit-transform: rotate(360deg); - -moz-transform: rotate(360deg); - -ms-transform: rotate(360deg); - -o-transform: rotate(360deg); - transform: rotate(360deg); } } - -/* Orbit Graceful Loading */ -.slideshow-wrapper { - position: relative; } - .slideshow-wrapper ul { - list-style-type: none; - margin: 0; } - .slideshow-wrapper ul li, - .slideshow-wrapper ul li .orbit-caption { - display: none; } - .slideshow-wrapper ul li:first-child { - display: block; } - .slideshow-wrapper .orbit-container { - background-color: transparent; } - .slideshow-wrapper .orbit-container li { - display: block; } - .slideshow-wrapper .orbit-container li .orbit-caption { - display: block; } - .slideshow-wrapper .preloader { - display: block; - width: 40px; - height: 40px; - position: absolute; - top: 50%; - left: 50%; - margin-top: -20px; - margin-left: -20px; - border: solid 3px; - border-color: #555555 white; - border-radius: 1000px; - animation-name: rotate; - animation-duration: 1.5s; - animation-iteration-count: infinite; - animation-timing-function: linear; } - -.orbit-container { - overflow: hidden; - width: 100%; - position: relative; - background: none; } - .orbit-container .orbit-slides-container { - list-style: none; - margin: 0; - padding: 0; - position: relative; - -webkit-transform: translateZ(0); } - .orbit-container .orbit-slides-container img { - display: block; - max-width: 100%; } - .orbit-container .orbit-slides-container.fade li { - opacity: 0; - transition: opacity 500ms ease-in-out; - -ms-transform: translate(0, 0); - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); } - .orbit-container .orbit-slides-container.fade li.animate-in { - opacity: 1; - z-index: 20; - transition: opacity 500ms ease-in-out; } - .orbit-container .orbit-slides-container.fade li.animate-out { - z-index: 10; - transition: opacity 500ms ease-in-out; } - .orbit-container .orbit-slides-container.swipe-next li { - -ms-transform: translate(100%, 0); - -webkit-transform: translate3d(100%, 0, 0); - -moz-transform: translate3d(100%, 0, 0); - -o-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); } - .orbit-container .orbit-slides-container.swipe-next li.animate-in { - -ms-transform: translate(0, 0); - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - transition-duration: 500ms; } - .orbit-container .orbit-slides-container.swipe-next li.animate-out { - -ms-transform: translate(-100%, 0); - -webkit-transform: translate3d(-100%, 0, 0); - -moz-transform: translate3d(-100%, 0, 0); - -o-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - transition-duration: 500ms; } - .orbit-container .orbit-slides-container.swipe-prev li { - -ms-transform: translate(-100%, 0); - -webkit-transform: translate3d(-100%, 0, 0); - -moz-transform: translate3d(-100%, 0, 0); - -o-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); } - .orbit-container .orbit-slides-container.swipe-prev li.animate-in { - -ms-transform: translate(0, 0); - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - transition-duration: 500ms; } - .orbit-container .orbit-slides-container.swipe-prev li.animate-out { - -ms-transform: translate(100%, 0); - -webkit-transform: translate3d(100%, 0, 0); - -moz-transform: translate3d(100%, 0, 0); - -o-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - transition-duration: 500ms; } - .orbit-container .orbit-slides-container li { - position: absolute; - top: 0; - left: 0; - width: 100%; - -ms-transform: translate(100%, 0); - -webkit-transform: translate3d(100%, 0, 0); - -moz-transform: translate3d(100%, 0, 0); - -o-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); } - .orbit-container .orbit-slides-container li.active { - opacity: 1; - top: 0; - left: 0; - -ms-transform: translate(0, 0); - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); } - .orbit-container .orbit-slides-container li .orbit-caption { - position: absolute; - bottom: 0; - background-color: rgba(51, 51, 51, 0.8); - color: white; - width: 100%; - padding: 0.625rem 0.875rem; - font-size: 0.875rem; } - .orbit-container .orbit-slide-number { - position: absolute; - top: 10px; - left: 10px; - font-size: 12px; - color: white; - background: rgba(0, 0, 0, 0); - z-index: 10; } - .orbit-container .orbit-slide-number span { - font-weight: 700; - padding: 0.3125rem; } - .orbit-container .orbit-timer { - position: absolute; - top: 12px; - right: 10px; - height: 6px; - width: 100px; - z-index: 10; } - .orbit-container .orbit-timer .orbit-progress { - height: 3px; - background-color: rgba(255, 255, 255, 0.3); - display: block; - width: 0%; - position: relative; - right: 20px; - top: 5px; } - .orbit-container .orbit-timer > span { - display: none; - position: absolute; - top: 0px; - right: 0; - width: 11px; - height: 14px; - border: solid 4px white; - border-top: none; - border-bottom: none; } - .orbit-container .orbit-timer.paused > span { - right: -4px; - top: 0px; - width: 11px; - height: 14px; - border: inset 8px; - border-left-style: solid; - -webkit-transform: rotate(180deg); - -moz-transform: rotate(180deg); - -ms-transform: rotate(180deg); - -o-transform: rotate(180deg); - transform: rotate(180deg); - border-color: transparent white transparent transparent; } - .orbit-container .orbit-timer.paused > span.dark { - border-color: transparent #333333 transparent transparent; } - .orbit-container:hover .orbit-timer > span { - display: block; } - .orbit-container .orbit-prev, - .orbit-container .orbit-next { - position: absolute; - top: 45%; - margin-top: -25px; - width: 36px; - height: 60px; - line-height: 50px; - color: white; - background-color: transparent; - text-indent: -9999px !important; - z-index: 10; } - .orbit-container .orbit-prev:hover, - .orbit-container .orbit-next:hover { - background-color: rgba(0, 0, 0, 0.3); } - .orbit-container .orbit-prev > span, - .orbit-container .orbit-next > span { - position: absolute; - top: 50%; - margin-top: -10px; - display: block; - width: 0; - height: 0; - border: inset 10px; } - .orbit-container .orbit-prev { - left: 0; } - .orbit-container .orbit-prev > span { - border-right-style: solid; - border-color: transparent; - border-right-color: white; } - .orbit-container .orbit-prev:hover > span { - border-right-color: white; } - .orbit-container .orbit-next { - right: 0; } - .orbit-container .orbit-next > span { - border-color: transparent; - border-left-style: solid; - border-left-color: white; - left: 50%; - margin-left: -4px; } - .orbit-container .orbit-next:hover > span { - border-left-color: white; } - .orbit-container .orbit-bullets-container { - text-align: center; } - .orbit-container .orbit-bullets { - margin: 0 auto 30px auto; - overflow: hidden; - position: relative; - top: 10px; - float: none; - text-align: center; - display: block; } - .orbit-container .orbit-bullets li { - display: inline-block; - width: 0.5625rem; - height: 0.5625rem; - background: #cccccc; - float: none; - margin-right: 6px; - border-radius: 1000px; } - .orbit-container .orbit-bullets li.active { - background: #999999; } - .orbit-container .orbit-bullets li:last-child { - margin-right: 0; } - -.touch .orbit-container .orbit-prev, -.touch .orbit-container .orbit-next { - display: none; } -.touch .orbit-bullets { - display: none; } - -@media only screen and (min-width: 40.063em) { - .touch .orbit-container .orbit-prev, - .touch .orbit-container .orbit-next { - display: inherit; } - .touch .orbit-bullets { - display: block; } } -@media only screen and (max-width: 40em) { - .orbit-stack-on-small .orbit-slides-container { - height: auto !important; } - .orbit-stack-on-small .orbit-slides-container > * { - position: relative; - margin-left: 0% !important; - opacity: 1 !important; - -webkit-transform: none !important; - -moz-transform: none !important; - -ms-transform: none !important; - -o-transform: none !important; - transform: none !important; - transition: none !important; } - .orbit-stack-on-small .orbit-timer { - display: none; } - .orbit-stack-on-small .orbit-next, .orbit-stack-on-small .orbit-prev { - display: none; } - .orbit-stack-on-small .orbit-bullets { - display: none; } } -[data-magellan-expedition], [data-magellan-expedition-clone] { - background: white; - z-index: 50; - min-width: 100%; - padding: 10px; } - [data-magellan-expedition] .sub-nav, [data-magellan-expedition-clone] .sub-nav { - margin-bottom: 0; } - [data-magellan-expedition] .sub-nav dd, [data-magellan-expedition-clone] .sub-nav dd { - margin-bottom: 0; } - [data-magellan-expedition] .sub-nav a, [data-magellan-expedition-clone] .sub-nav a { - line-height: 1.8em; } - -.tabs { - *zoom: 1; - margin-bottom: 0 !important; } - .tabs:before, .tabs:after { - content: " "; - display: table; } - .tabs:after { - clear: both; } - .tabs dd { - position: relative; - margin-bottom: 0 !important; - float: left; } - .tabs dd > a { - display: block; - background: #efefef; - color: #222222; - padding: 1rem 2rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-size: 1rem; } - .tabs dd > a:hover { - background: #e1e1e1; } - .tabs dd.active a { - background: white; } - .tabs.radius dd:first-child a { - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - .tabs.radius dd:last-child a { - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - .tabs.vertical dd { - position: inherit; - float: none; - display: block; - top: auto; } - -.tabs-content { - *zoom: 1; - margin-bottom: 1.5rem; - width: 100%; } - .tabs-content:before, .tabs-content:after { - content: " "; - display: table; } - .tabs-content:after { - clear: both; } - .tabs-content > .content { - display: none; - float: left; - padding: 0.9375rem 0; - width: 100%; } - .tabs-content > .content.active { - display: block; - float: none; } - .tabs-content > .content.contained { - padding: 0.9375rem; } - .tabs-content.vertical { - display: block; } - .tabs-content.vertical > .content { - padding: 0 0.9375rem; } - -@media only screen and (min-width: 40.063em) { - .tabs.vertical { - width: 20%; - float: left; - margin-bottom: 1.25rem; } - - .tabs-content.vertical { - width: 80%; - float: left; - margin-left: -1px; } } -.no-js .tabs-content > .content { - display: block; - float: none; } - -ul.pagination { - display: block; - height: 1.5rem; - margin-left: -0.3125rem; } - ul.pagination li { - height: 1.5rem; - color: #222222; - font-size: 0.875rem; - margin-left: 0.3125rem; } - ul.pagination li a { - display: block; - padding: 0.0625rem 0.625rem 0.0625rem; - color: #999999; - border-radius: 3px; } - ul.pagination li:hover a, - ul.pagination li a:focus { - background: #e6e6e6; } - ul.pagination li.unavailable a { - cursor: default; - color: #999999; } - ul.pagination li.unavailable:hover a, ul.pagination li.unavailable a:focus { - background: transparent; } - ul.pagination li.current a { - background: #008cba; - color: white; - font-weight: bold; - cursor: default; } - ul.pagination li.current a:hover, ul.pagination li.current a:focus { - background: #008cba; } - ul.pagination li { - float: left; - display: block; } - -/* Pagination centred wrapper */ -.pagination-centered { - text-align: center; } - .pagination-centered ul.pagination li { - float: none; - display: inline-block; } - -.side-nav { - display: block; - margin: 0; - padding: 0.875rem 0; - list-style-type: none; - list-style-position: inside; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .side-nav li { - margin: 0 0 0.4375rem 0; - font-size: 0.875rem; } - .side-nav li a:not(.button) { - display: block; - color: #008cba; } - .side-nav li a:not(.button):hover, .side-nav li a:not(.button):focus { - color: #1cc7ff; } - .side-nav li.active > a:first-child:not(.button) { - color: #1cc7ff; - font-weight: normal; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .side-nav li.divider { - border-top: 1px solid; - height: 0; - padding: 0; - list-style: none; - border-top-color: white; } - -.accordion { - *zoom: 1; - margin-bottom: 0; } - .accordion:before, .accordion:after { - content: " "; - display: table; } - .accordion:after { - clear: both; } - .accordion dd { - display: block; - margin-bottom: 0 !important; } - .accordion dd.active > a { - background: #e8e8e8; } - .accordion dd > a { - background: #efefef; - color: #222222; - padding: 1rem; - display: block; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-size: 1rem; } - .accordion dd > a:hover { - background: #e3e3e3; } - .accordion .content { - display: none; - padding: 0.9375rem; } - .accordion .content.active { - display: block; - background: white; } - -.text-left { - text-align: left !important; } - -.text-right { - text-align: right !important; } - -.text-center { - text-align: center !important; } - -.text-justify { - text-align: justify !important; } - -@media only screen and (max-width: 40em) { - .small-only-text-left { - text-align: left !important; } - - .small-only-text-right { - text-align: right !important; } - - .small-only-text-center { - text-align: center !important; } - - .small-only-text-justify { - text-align: justify !important; } } -@media only screen { - .small-text-left { - text-align: left !important; } - - .small-text-right { - text-align: right !important; } - - .small-text-center { - text-align: center !important; } - - .small-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 40.063em) and (max-width: 64em) { - .medium-only-text-left { - text-align: left !important; } - - .medium-only-text-right { - text-align: right !important; } - - .medium-only-text-center { - text-align: center !important; } - - .medium-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 40.063em) { - .medium-text-left { - text-align: left !important; } - - .medium-text-right { - text-align: right !important; } - - .medium-text-center { - text-align: center !important; } - - .medium-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 64.063em) and (max-width: 90em) { - .large-only-text-left { - text-align: left !important; } - - .large-only-text-right { - text-align: right !important; } - - .large-only-text-center { - text-align: center !important; } - - .large-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 64.063em) { - .large-text-left { - text-align: left !important; } - - .large-text-right { - text-align: right !important; } - - .large-text-center { - text-align: center !important; } - - .large-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 90.063em) and (max-width: 120em) { - .xlarge-only-text-left { - text-align: left !important; } - - .xlarge-only-text-right { - text-align: right !important; } - - .xlarge-only-text-center { - text-align: center !important; } - - .xlarge-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 90.063em) { - .xlarge-text-left { - text-align: left !important; } - - .xlarge-text-right { - text-align: right !important; } - - .xlarge-text-center { - text-align: center !important; } - - .xlarge-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 120.063em) and (max-width: 99999999em) { - .xxlarge-only-text-left { - text-align: left !important; } - - .xxlarge-only-text-right { - text-align: right !important; } - - .xxlarge-only-text-center { - text-align: center !important; } - - .xxlarge-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 120.063em) { - .xxlarge-text-left { - text-align: left !important; } - - .xxlarge-text-right { - text-align: right !important; } - - .xxlarge-text-center { - text-align: center !important; } - - .xxlarge-text-justify { - text-align: justify !important; } } -/* Typography resets */ -div, -dl, -dt, -dd, -ul, -ol, -li, -h1, -h2, -h3, -h4, -h5, -h6, -pre, -form, -p, -blockquote, -th, -td { - margin: 0; - padding: 0; } - -/* Default Link Styles */ -a { - color: #008cba; - text-decoration: none; - line-height: inherit; } - a:hover, a:focus { - color: #0078a0; } - a img { - border: none; } - -/* Default paragraph styles */ -p { - font-family: inherit; - font-weight: normal; - font-size: 1rem; - line-height: 1.6; - margin-bottom: 1.25rem; - text-rendering: optimizeLegibility; } - p.lead { - font-size: 1.21875rem; - line-height: 1.6; } - p aside { - font-size: 0.875rem; - line-height: 1.35; - font-style: italic; } - -/* Default header styles */ -h1, h2, h3, h4, h5, h6 { - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - font-style: normal; - color: #222222; - text-rendering: optimizeLegibility; - margin-top: 0.2rem; - margin-bottom: 0.5rem; - line-height: 1.4; } - h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { - font-size: 60%; - color: #6f6f6f; - line-height: 0; } - -h1 { - font-size: 2.125rem; } - -h2 { - font-size: 1.6875rem; } - -h3 { - font-size: 1.375rem; } - -h4 { - font-size: 1.125rem; } - -h5 { - font-size: 1.125rem; } - -h6 { - font-size: 1rem; } - -.subheader { - line-height: 1.4; - color: #6f6f6f; - font-weight: normal; - margin-top: 0.2rem; - margin-bottom: 0.5rem; } - -hr { - border: solid #dddddd; - border-width: 1px 0 0; - clear: both; - margin: 1.25rem 0 1.1875rem; - height: 0; } - -/* Helpful Typography Defaults */ -em, -i { - font-style: italic; - line-height: inherit; } - -strong, -b { - font-weight: bold; - line-height: inherit; } - -small { - font-size: 60%; - line-height: inherit; } - -code { - font-family: Consolas, "Liberation Mono", Courier, monospace; - font-weight: bold; - color: #bd260d; } - -/* Lists */ -ul, -ol, -dl { - font-size: 1rem; - line-height: 1.6; - margin-bottom: 1.25rem; - list-style-position: outside; - font-family: inherit; } - -ul { - margin-left: 1.1rem; } - ul.no-bullet { - margin-left: 0; } - ul.no-bullet li ul, - ul.no-bullet li ol { - margin-left: 1.25rem; - margin-bottom: 0; - list-style: none; } - -/* Unordered Lists */ -ul li ul, -ul li ol { - margin-left: 1.25rem; - margin-bottom: 0; } -ul.square li ul, ul.circle li ul, ul.disc li ul { - list-style: inherit; } -ul.square { - list-style-type: square; - margin-left: 1.1rem; } -ul.circle { - list-style-type: circle; - margin-left: 1.1rem; } -ul.disc { - list-style-type: disc; - margin-left: 1.1rem; } -ul.no-bullet { - list-style: none; } - -/* Ordered Lists */ -ol { - margin-left: 1.4rem; } - ol li ul, - ol li ol { - margin-left: 1.25rem; - margin-bottom: 0; } - -/* Definition Lists */ -dl dt { - margin-bottom: 0.3rem; - font-weight: bold; } -dl dd { - margin-bottom: 0.75rem; } - -/* Abbreviations */ -abbr, -acronym { - text-transform: uppercase; - font-size: 90%; - color: #222222; - border-bottom: 1px dotted #dddddd; - cursor: help; } - -abbr { - text-transform: none; } - -/* Blockquotes */ -blockquote { - margin: 0 0 1.25rem; - padding: 0.5625rem 1.25rem 0 1.1875rem; - border-left: 1px solid #dddddd; } - blockquote cite { - display: block; - font-size: 0.8125rem; - color: #555555; } - blockquote cite:before { - content: "\2014 \0020"; } - blockquote cite a, - blockquote cite a:visited { - color: #555555; } - -blockquote, -blockquote p { - line-height: 1.6; - color: #6f6f6f; } - -/* Microformats */ -.vcard { - display: inline-block; - margin: 0 0 1.25rem 0; - border: 1px solid #dddddd; - padding: 0.625rem 0.75rem; } - .vcard li { - margin: 0; - display: block; } - .vcard .fn { - font-weight: bold; - font-size: 0.9375rem; } - -.vevent .summary { - font-weight: bold; } -.vevent abbr { - cursor: default; - text-decoration: none; - font-weight: bold; - border: none; - padding: 0 0.0625rem; } - -@media only screen and (min-width: 40.063em) { - h1, h2, h3, h4, h5, h6 { - line-height: 1.4; } - - h1 { - font-size: 2.75rem; } - - h2 { - font-size: 2.3125rem; } - - h3 { - font-size: 1.6875rem; } - - h4 { - font-size: 1.4375rem; } } -/* - * Print styles. - * - * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ - * Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) -*/ -.print-only { - display: none !important; } - -@media print { - * { - background: transparent !important; - color: black !important; - /* Black prints faster: h5bp.com/s */ - box-shadow: none !important; - text-shadow: none !important; } - - a, - a:visited { - text-decoration: underline; } - - a[href]:after { - content: " (" attr(href) ")"; } - - abbr[title]:after { - content: " (" attr(title) ")"; } - - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; } - - pre, - blockquote { - border: 1px solid #999999; - page-break-inside: avoid; } - - thead { - display: table-header-group; - /* h5bp.com/t */ } - - tr, - img { - page-break-inside: avoid; } - - img { - max-width: 100% !important; } - - @page { - margin: 0.5cm; } - - p, - h2, - h3 { - orphans: 3; - widows: 3; } - - h2, - h3 { - page-break-after: avoid; } - - .hide-on-print { - display: none !important; } - - .print-only { - display: block !important; } - - .hide-for-print { - display: none !important; } - - .show-for-print { - display: inherit !important; } } -.split.button { - position: relative; - padding-right: 5.0625rem; } - .split.button span { - display: block; - height: 100%; - position: absolute; - right: 0; - top: 0; - border-left: solid 1px; } - .split.button span:before { - position: absolute; - content: ""; - width: 0; - height: 0; - display: block; - border-style: inset; - top: 50%; - left: 50%; } - .split.button span:active { - background-color: rgba(0, 0, 0, 0.1); } - .split.button span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button span { - width: 3.09375rem; } - .split.button span:before { - border-top-style: solid; - border-width: 0.375rem; - top: 48%; - margin-left: -0.375rem; } - .split.button span:before { - border-color: white transparent transparent transparent; } - .split.button.secondary span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button.secondary span:before { - border-color: white transparent transparent transparent; } - .split.button.alert span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button.success span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button.tiny { - padding-right: 3.75rem; } - .split.button.tiny span { - width: 2.25rem; } - .split.button.tiny span:before { - border-top-style: solid; - border-width: 0.375rem; - top: 48%; - margin-left: -0.375rem; } - .split.button.small { - padding-right: 4.375rem; } - .split.button.small span { - width: 2.625rem; } - .split.button.small span:before { - border-top-style: solid; - border-width: 0.4375rem; - top: 48%; - margin-left: -0.375rem; } - .split.button.large { - padding-right: 5.5rem; } - .split.button.large span { - width: 3.4375rem; } - .split.button.large span:before { - border-top-style: solid; - border-width: 0.3125rem; - top: 48%; - margin-left: -0.375rem; } - .split.button.expand { - padding-left: 2rem; } - .split.button.secondary span:before { - border-color: #333333 transparent transparent transparent; } - .split.button.radius span { - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - .split.button.round span { - border-bottom-right-radius: 1000px; - border-top-right-radius: 1000px; } - -.reveal-modal-bg { - position: fixed; - height: 100%; - width: 100%; - background: black; - background: rgba(0, 0, 0, 0.45); - z-index: 99; - display: none; - top: 0; - left: 0; } - -dialog, .reveal-modal { - visibility: hidden; - display: none; - position: absolute; - z-index: 100; - width: 100vw; - top: 0; - left: 0; - background-color: white; - padding: 1.25rem; - border: solid 1px #666666; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); } - @media only screen and (max-width: 40em) { - dialog, .reveal-modal { - min-height: 100vh; } } - @media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - left: 50%; } } - dialog .column, - dialog .columns, .reveal-modal .column, - .reveal-modal .columns { - min-width: 0; } - dialog > :first-child, .reveal-modal > :first-child { - margin-top: 0; } - dialog > :last-child, .reveal-modal > :last-child { - margin-bottom: 0; } - @media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - margin-left: -40%; - width: 80%; } } - @media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - top: 6.25rem; } } - dialog .close-reveal-modal, .reveal-modal .close-reveal-modal { - font-size: 2.5rem; - line-height: 1; - position: absolute; - top: 0.5rem; - right: 0.6875rem; - color: #aaaaaa; - font-weight: bold; - cursor: pointer; } - -dialog[open] { - display: block; - visibility: visible; } - -@media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - padding: 1.875rem; } - dialog.radius, .reveal-modal.radius { - border-radius: 3px; } - dialog.round, .reveal-modal.round { - border-radius: 1000px; } - dialog.collapse, .reveal-modal.collapse { - padding: 0; } - dialog.full, .reveal-modal.full { - top: 0; - left: 0; - height: 100vh; - min-height: 100vh; - margin-left: 0 !important; } } - @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.tiny, .reveal-modal.tiny { - margin-left: -15%; - width: 30%; } } - @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.small, .reveal-modal.small { - margin-left: -20%; - width: 40%; } } - @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.medium, .reveal-modal.medium { - margin-left: -30%; - width: 60%; } } - @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.large, .reveal-modal.large { - margin-left: -35%; - width: 70%; } } - @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.xlarge, .reveal-modal.xlarge { - margin-left: -47.5%; - width: 95%; } } - - @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.full, .reveal-modal.full { - margin-left: -50vw; - width: 100vw; } } - -@media print { - dialog, .reveal-modal { - background: white !important; } } -/* Tooltips */ -.has-tip { - border-bottom: dotted 1px #cccccc; - cursor: help; - font-weight: bold; - color: #333333; } - .has-tip:hover, .has-tip:focus { - border-bottom: dotted 1px #003f54; - color: #008cba; } - .has-tip.tip-left, .has-tip.tip-right { - float: none !important; } - -.tooltip { - display: none; - position: absolute; - z-index: 999; - font-weight: normal; - font-size: 0.875rem; - line-height: 1.3; - padding: 0.75rem; - max-width: 85%; - left: 50%; - width: 100%; - color: white; - background: #333333; } - .tooltip > .nub { - display: block; - left: 5px; - position: absolute; - width: 0; - height: 0; - border: solid 5px; - border-color: transparent transparent #333333 transparent; - top: -10px; } - .tooltip > .nub.rtl { - left: auto; - right: 5px; } - .tooltip.radius { - border-radius: 3px; } - .tooltip.round { - border-radius: 1000px; } - .tooltip.round > .nub { - left: 2rem; } - .tooltip.opened { - color: #008cba !important; - border-bottom: dotted 1px #003f54 !important; } - -.tap-to-close { - display: block; - font-size: 0.625rem; - color: #777777; - font-weight: normal; } - -@media only screen and (min-width: 40.063em) { - .tooltip > .nub { - border-color: transparent transparent #333333 transparent; - top: -10px; } - .tooltip.tip-top > .nub { - border-color: #333333 transparent transparent transparent; - top: auto; - bottom: -10px; } - .tooltip.tip-left, .tooltip.tip-right { - float: none !important; } - .tooltip.tip-left > .nub { - border-color: transparent transparent transparent #333333; - right: -10px; - left: auto; - top: 50%; - margin-top: -5px; } - .tooltip.tip-right > .nub { - border-color: transparent #333333 transparent transparent; - right: auto; - left: -10px; - top: 50%; - margin-top: -5px; } } -/* Clearing Styles */ -.clearing-thumbs, [data-clearing] { - *zoom: 1; - margin-bottom: 0; - margin-left: 0; - list-style: none; } - .clearing-thumbs:before, .clearing-thumbs:after, [data-clearing]:before, [data-clearing]:after { - content: " "; - display: table; } - .clearing-thumbs:after, [data-clearing]:after { - clear: both; } - .clearing-thumbs li, [data-clearing] li { - float: left; - margin-right: 10px; } - .clearing-thumbs[class*="block-grid-"] li, [data-clearing][class*="block-grid-"] li { - margin-right: 0; } - -.clearing-blackout { - background: #333333; - position: fixed; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 998; } - .clearing-blackout .clearing-close { - display: block; } - -.clearing-container { - position: relative; - z-index: 998; - height: 100%; - overflow: hidden; - margin: 0; } - -.clearing-touch-label { - position: absolute; - top: 50%; - left: 50%; - color: #aaa; - font-size: 0.6em; } - -.visible-img { - height: 95%; - position: relative; } - .visible-img img { - position: absolute; - left: 50%; - top: 50%; - margin-left: -50%; - max-height: 100%; - max-width: 100%; } - -.clearing-caption { - color: #cccccc; - font-size: 0.875em; - line-height: 1.3; - margin-bottom: 0; - text-align: center; - bottom: 0; - background: #333333; - width: 100%; - padding: 10px 30px 20px; - position: absolute; - left: 0; } - -.clearing-close { - z-index: 999; - padding-left: 20px; - padding-top: 10px; - font-size: 30px; - line-height: 1; - color: #cccccc; - display: none; } - .clearing-close:hover, .clearing-close:focus { - color: #ccc; } - -.clearing-assembled .clearing-container { - height: 100%; } - .clearing-assembled .clearing-container .carousel > ul { - display: none; } - -.clearing-feature li { - display: none; } - .clearing-feature li.clearing-featured-img { - display: block; } - -@media only screen and (min-width: 40.063em) { - .clearing-main-prev, - .clearing-main-next { - position: absolute; - height: 100%; - width: 40px; - top: 0; } - .clearing-main-prev > span, - .clearing-main-next > span { - position: absolute; - top: 50%; - display: block; - width: 0; - height: 0; - border: solid 12px; } - .clearing-main-prev > span:hover, - .clearing-main-next > span:hover { - opacity: 0.8; } - - .clearing-main-prev { - left: 0; } - .clearing-main-prev > span { - left: 5px; - border-color: transparent; - border-right-color: #cccccc; } - - .clearing-main-next { - right: 0; } - .clearing-main-next > span { - border-color: transparent; - border-left-color: #cccccc; } - - .clearing-main-prev.disabled, - .clearing-main-next.disabled { - opacity: 0.3; } - - .clearing-assembled .clearing-container .carousel { - background: rgba(51, 51, 51, 0.8); - height: 120px; - margin-top: 10px; - text-align: center; } - .clearing-assembled .clearing-container .carousel > ul { - display: inline-block; - z-index: 999; - height: 100%; - position: relative; - float: none; } - .clearing-assembled .clearing-container .carousel > ul li { - display: block; - width: 120px; - min-height: inherit; - float: left; - overflow: hidden; - margin-right: 0; - padding: 0; - position: relative; - cursor: pointer; - opacity: 0.4; - clear: none; } - .clearing-assembled .clearing-container .carousel > ul li.fix-height img { - height: 100%; - max-width: none; } - .clearing-assembled .clearing-container .carousel > ul li a.th { - border: none; - box-shadow: none; - display: block; } - .clearing-assembled .clearing-container .carousel > ul li img { - cursor: pointer !important; - width: 100% !important; } - .clearing-assembled .clearing-container .carousel > ul li.visible { - opacity: 1; } - .clearing-assembled .clearing-container .carousel > ul li:hover { - opacity: 0.8; } - .clearing-assembled .clearing-container .visible-img { - background: #333333; - overflow: hidden; - height: 85%; } - - .clearing-close { - position: absolute; - top: 10px; - right: 20px; - padding-left: 0; - padding-top: 0; } } -/* Progress Bar */ -.progress { - background-color: #f6f6f6; - height: 1.5625rem; - border: 1px solid white; - padding: 0.125rem; - margin-bottom: 0.625rem; } - .progress .meter { - background: #008cba; - height: 100%; - display: block; } - .progress.secondary .meter { - background: #e7e7e7; - height: 100%; - display: block; } - .progress.success .meter { - background: #43ac6a; - height: 100%; - display: block; } - .progress.alert .meter { - background: #f04124; - height: 100%; - display: block; } - .progress.radius { - border-radius: 3px; } - .progress.radius .meter { - border-radius: 2px; } - .progress.round { - border-radius: 1000px; } - .progress.round .meter { - border-radius: 999px; } - -.sub-nav { - display: block; - width: auto; - overflow: hidden; - margin: -0.25rem 0 1.125rem; - padding-top: 0.25rem; - margin-right: 0; - margin-left: -0.75rem; } - .sub-nav dt { - text-transform: uppercase; } - .sub-nav dt, - .sub-nav dd, - .sub-nav li { - float: left; - display: inline; - margin-left: 1rem; - margin-bottom: 0.625rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - font-size: 0.875rem; - color: #999999; } - .sub-nav dt a, - .sub-nav dd a, - .sub-nav li a { - text-decoration: none; - color: #999999; - padding: 0.1875rem 1rem; } - .sub-nav dt a:hover, - .sub-nav dd a:hover, - .sub-nav li a:hover { - color: #737373; } - .sub-nav dt.active a, - .sub-nav dd.active a, - .sub-nav li.active a { - border-radius: 3px; - font-weight: normal; - background: #008cba; - padding: 0.1875rem 1rem; - cursor: default; - color: white; } - .sub-nav dt.active a:hover, - .sub-nav dd.active a:hover, - .sub-nav li.active a:hover { - background: #0078a0; } - -/* Foundation Joyride */ -.joyride-list { - display: none; } - -/* Default styles for the container */ -.joyride-tip-guide { - display: none; - position: absolute; - background: #333333; - color: white; - z-index: 101; - top: 0; - left: 2.5%; - font-family: inherit; - font-weight: normal; - width: 95%; } - -.lt-ie9 .joyride-tip-guide { - max-width: 800px; - left: 50%; - margin-left: -400px; } - -.joyride-content-wrapper { - width: 100%; - padding: 1.125rem 1.25rem 1.5rem; } - .joyride-content-wrapper .button { - margin-bottom: 0 !important; } - -/* Add a little css triangle pip, older browser just miss out on the fanciness of it */ -.joyride-tip-guide .joyride-nub { - display: block; - position: absolute; - left: 22px; - width: 0; - height: 0; - border: 10px solid #333333; } - .joyride-tip-guide .joyride-nub.top { - border-top-style: solid; - border-color: #333333; - border-top-color: transparent !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - top: -20px; } - .joyride-tip-guide .joyride-nub.bottom { - border-bottom-style: solid; - border-color: #333333 !important; - border-bottom-color: transparent !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - bottom: -20px; } - .joyride-tip-guide .joyride-nub.right { - right: -20px; } - .joyride-tip-guide .joyride-nub.left { - left: -20px; } - -/* Typography */ -.joyride-tip-guide h1, -.joyride-tip-guide h2, -.joyride-tip-guide h3, -.joyride-tip-guide h4, -.joyride-tip-guide h5, -.joyride-tip-guide h6 { - line-height: 1.25; - margin: 0; - font-weight: bold; - color: white; } - -.joyride-tip-guide p { - margin: 0 0 1.125rem 0; - font-size: 0.875rem; - line-height: 1.3; } - -.joyride-timer-indicator-wrap { - width: 50px; - height: 3px; - border: solid 1px #555555; - position: absolute; - right: 1.0625rem; - bottom: 1rem; } - -.joyride-timer-indicator { - display: block; - width: 0; - height: inherit; - background: #666666; } - -.joyride-close-tip { - position: absolute; - right: 12px; - top: 10px; - color: #777777 !important; - text-decoration: none; - font-size: 24px; - font-weight: normal; - line-height: 0.5 !important; } - .joyride-close-tip:hover, .joyride-close-tip:focus { - color: #eeeeee !important; } - -.joyride-modal-bg { - position: fixed; - height: 100%; - width: 100%; - background: transparent; - background: rgba(0, 0, 0, 0.5); - z-index: 100; - display: none; - top: 0; - left: 0; - cursor: pointer; } - -.joyride-expose-wrapper { - background-color: #ffffff; - position: absolute; - border-radius: 3px; - z-index: 102; - box-shadow: 0 0 15px white; } - -.joyride-expose-cover { - background: transparent; - border-radius: 3px; - position: absolute; - z-index: 9999; - top: 0; - left: 0; } - -/* Styles for screens that are at least 768px; */ -@media only screen and (min-width: 40.063em) { - .joyride-tip-guide { - width: 300px; - left: inherit; } - .joyride-tip-guide .joyride-nub.bottom { - border-color: #333333 !important; - border-bottom-color: transparent !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - bottom: -20px; } - .joyride-tip-guide .joyride-nub.right { - border-color: #333333 !important; - border-top-color: transparent !important; - border-right-color: transparent !important; - border-bottom-color: transparent !important; - top: 22px; - left: auto; - right: -20px; } - .joyride-tip-guide .joyride-nub.left { - border-color: #333333 !important; - border-top-color: transparent !important; - border-left-color: transparent !important; - border-bottom-color: transparent !important; - top: 22px; - left: -20px; - right: auto; } } -.label { - font-weight: normal; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - text-align: center; - text-decoration: none; - line-height: 1; - white-space: nowrap; - display: inline-block; - position: relative; - margin-bottom: inherit; - padding: 0.25rem 0.5rem 0.375rem; - font-size: 0.6875rem; - background-color: #008cba; - color: white; } - .label.radius { - border-radius: 3px; } - .label.round { - border-radius: 1000px; } - .label.alert { - background-color: #f04124; - color: white; } - .label.success { - background-color: #43ac6a; - color: white; } - .label.secondary { - background-color: #e7e7e7; - color: #333333; } - -.off-canvas-wrap { - -webkit-backface-visibility: hidden; - position: relative; - width: 100%; - overflow: hidden; } - .off-canvas-wrap.move-right, .off-canvas-wrap.move-left { - min-height: 100%; - -webkit-overflow-scrolling: touch; } - -.inner-wrap { - -webkit-backface-visibility: hidden; - position: relative; - width: 100%; - *zoom: 1; - -webkit-transition: -webkit-transform 500ms ease; - -moz-transition: -moz-transform 500ms ease; - -ms-transition: -ms-transform 500ms ease; - -o-transition: -o-transform 500ms ease; - transition: transform 500ms ease; } - .inner-wrap:before, .inner-wrap:after { - content: " "; - display: table; } - .inner-wrap:after { - clear: both; } - -.tab-bar { - -webkit-backface-visibility: hidden; - background: #333333; - color: white; - height: 2.8125rem; - line-height: 2.8125rem; - position: relative; } - .tab-bar h1, .tab-bar h2, .tab-bar h3, .tab-bar h4, .tab-bar h5, .tab-bar h6 { - color: white; - font-weight: bold; - line-height: 2.8125rem; - margin: 0; } - .tab-bar h1, .tab-bar h2, .tab-bar h3, .tab-bar h4 { - font-size: 1.125rem; } - -.left-small { - width: 2.8125rem; - height: 2.8125rem; - position: absolute; - top: 0; - border-right: solid 1px #1a1a1a; - left: 0; } - -.right-small { - width: 2.8125rem; - height: 2.8125rem; - position: absolute; - top: 0; - border-left: solid 1px #1a1a1a; - right: 0; } - -.tab-bar-section { - padding: 0 0.625rem; - position: absolute; - text-align: center; - height: 2.8125rem; - top: 0; } - @media only screen and (min-width: 40.063em) { - .tab-bar-section { - text-align: left; } } - .tab-bar-section.left { - left: 0; - right: 2.8125rem; } - .tab-bar-section.right { - left: 2.8125rem; - right: 0; } - .tab-bar-section.middle { - left: 2.8125rem; - right: 2.8125rem; } - -.tab-bar .menu-icon { - text-indent: 2.1875rem; - width: 2.8125rem; - height: 2.8125rem; - display: block; - line-height: 2.0625rem; - padding: 0; - color: white; - position: relative; - -ms-transform: translate(0, 0); - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -ms-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); } - .tab-bar .menu-icon span { - position: absolute; - display: block; - height: 0; - width: 1rem; - line-height: 1; - top: 0.9375rem; - left: 0.90625rem; - box-shadow: 0 0px 0 1px white, 0 7px 0 1px white, 0 14px 0 1px white; } - .tab-bar .menu-icon:hover span { - box-shadow: 0 0px 0 1px #b3b3b3, 0 7px 0 1px #b3b3b3, 0 14px 0 1px #b3b3b3; } - -.left-off-canvas-menu { - -webkit-backface-visibility: hidden; - width: 15.625rem; - top: 0; - bottom: 0; - position: absolute; - overflow-y: auto; - background: #333333; - z-index: 1001; - box-sizing: content-box; - -webkit-overflow-scrolling: touch; - -ms-transform: translate(-100%, 0); - -webkit-transform: translate3d(-100%, 0, 0); - -moz-transform: translate3d(-100%, 0, 0); - -ms-transform: translate3d(-100%, 0, 0); - -o-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - left: 0; } - .left-off-canvas-menu * { - -webkit-backface-visibility: hidden; } - -.right-off-canvas-menu { - -webkit-backface-visibility: hidden; - width: 15.625rem; - top: 0; - bottom: 0; - position: absolute; - overflow-y: auto; - background: #333333; - z-index: 1001; - box-sizing: content-box; - -webkit-overflow-scrolling: touch; - -ms-transform: translate(100%, 0); - -webkit-transform: translate3d(100%, 0, 0); - -moz-transform: translate3d(100%, 0, 0); - -ms-transform: translate3d(100%, 0, 0); - -o-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - right: 0; } - .right-off-canvas-menu * { - -webkit-backface-visibility: hidden; } - -ul.off-canvas-list { - list-style-type: none; - padding: 0; - margin: 0; } - ul.off-canvas-list li label { - padding: 0.3rem 0.9375rem; - color: #999999; - text-transform: uppercase; - font-weight: bold; - background: #444444; - border-top: 1px solid #5e5e5e; - border-bottom: none; - margin: 0; } - ul.off-canvas-list li a { - display: block; - padding: 0.66667rem; - color: rgba(255, 255, 255, 0.7); - border-bottom: 1px solid #262626; - transition: background 300ms ease; } - ul.off-canvas-list li a:hover { - background: #242424; } - -.move-right > .inner-wrap { - -ms-transform: translate(15.625rem, 0); - -webkit-transform: translate3d(15.625rem, 0, 0); - -moz-transform: translate3d(15.625rem, 0, 0); - -ms-transform: translate3d(15.625rem, 0, 0); - -o-transform: translate3d(15.625rem, 0, 0); - transform: translate3d(15.625rem, 0, 0); } -.move-right .exit-off-canvas { - -webkit-backface-visibility: hidden; - transition: background 300ms ease; - cursor: pointer; - box-shadow: -4px 0 4px rgba(0, 0, 0, 0.5), 4px 0 4px rgba(0, 0, 0, 0.5); - display: block; - position: absolute; - background: rgba(255, 255, 255, 0.2); - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 1002; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - @media only screen and (min-width: 40.063em) { - .move-right .exit-off-canvas:hover { - background: rgba(255, 255, 255, 0.05); } } - -.move-left > .inner-wrap { - -ms-transform: translate(-15.625rem, 0); - -webkit-transform: translate3d(-15.625rem, 0, 0); - -moz-transform: translate3d(-15.625rem, 0, 0); - -ms-transform: translate3d(-15.625rem, 0, 0); - -o-transform: translate3d(-15.625rem, 0, 0); - transform: translate3d(-15.625rem, 0, 0); } -.move-left .exit-off-canvas { - -webkit-backface-visibility: hidden; - transition: background 300ms ease; - cursor: pointer; - box-shadow: -4px 0 4px rgba(0, 0, 0, 0.5), 4px 0 4px rgba(0, 0, 0, 0.5); - display: block; - position: absolute; - background: rgba(255, 255, 255, 0.2); - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 1002; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - @media only screen and (min-width: 40.063em) { - .move-left .exit-off-canvas:hover { - background: rgba(255, 255, 255, 0.05); } } - -.no-csstransforms .left-off-canvas-menu { - left: -15.625rem; } -.no-csstransforms .right-off-canvas-menu { - right: -15.625rem; } -.no-csstransforms .move-left > .inner-wrap { - right: 15.625rem; } -.no-csstransforms .move-right > .inner-wrap { - left: 15.625rem; } - -/* Foundation Dropdowns */ -.f-dropdown { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - width: 100%; - max-height: none; - height: auto; - background: white; - border: solid 1px #cccccc; - font-size: 0.875rem; - z-index: 99; - margin-top: 2px; - max-width: 200px; } - .f-dropdown > *:first-child { - margin-top: 0; } - .f-dropdown > *:last-child { - margin-bottom: 0; } - .f-dropdown:before { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 6px; - border-color: transparent transparent white transparent; - border-bottom-style: solid; - position: absolute; - top: -12px; - left: 10px; - z-index: 99; } - .f-dropdown:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 7px; - border-color: transparent transparent #cccccc transparent; - border-bottom-style: solid; - position: absolute; - top: -14px; - left: 9px; - z-index: 98; } - .f-dropdown.right:before { - left: auto; - right: 10px; } - .f-dropdown.right:after { - left: auto; - right: 9px; } - .f-dropdown.drop-right { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - width: 100%; - max-height: none; - height: auto; - background: white; - border: solid 1px #cccccc; - font-size: 0.875rem; - z-index: 99; - margin-top: 0; - margin-left: 2px; - max-width: 200px; } - .f-dropdown.drop-right > *:first-child { - margin-top: 0; } - .f-dropdown.drop-right > *:last-child { - margin-bottom: 0; } - .f-dropdown.drop-right:before { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 6px; - border-color: transparent white transparent transparent; - border-right-style: solid; - position: absolute; - top: 10px; - left: -12px; - z-index: 99; } - .f-dropdown.drop-right:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 7px; - border-color: transparent #cccccc transparent transparent; - border-right-style: solid; - position: absolute; - top: 9px; - left: -14px; - z-index: 98; } - .f-dropdown.drop-left { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - width: 100%; - max-height: none; - height: auto; - background: white; - border: solid 1px #cccccc; - font-size: 0.875rem; - z-index: 99; - margin-top: 0; - margin-left: -2px; - max-width: 200px; } - .f-dropdown.drop-left > *:first-child { - margin-top: 0; } - .f-dropdown.drop-left > *:last-child { - margin-bottom: 0; } - .f-dropdown.drop-left:before { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 6px; - border-color: transparent transparent transparent white; - border-left-style: solid; - position: absolute; - top: 10px; - right: -12px; - left: auto; - z-index: 99; } - .f-dropdown.drop-left:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 7px; - border-color: transparent transparent transparent #cccccc; - border-left-style: solid; - position: absolute; - top: 9px; - right: -14px; - left: auto; - z-index: 98; } - .f-dropdown.drop-top { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - width: 100%; - max-height: none; - height: auto; - background: white; - border: solid 1px #cccccc; - font-size: 0.875rem; - z-index: 99; - margin-top: -2px; - margin-left: 0; - max-width: 200px; } - .f-dropdown.drop-top > *:first-child { - margin-top: 0; } - .f-dropdown.drop-top > *:last-child { - margin-bottom: 0; } - .f-dropdown.drop-top:before { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 6px; - border-color: white transparent transparent transparent; - border-top-style: solid; - position: absolute; - top: auto; - bottom: -12px; - left: 10px; - right: auto; - z-index: 99; } - .f-dropdown.drop-top:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 7px; - border-color: #cccccc transparent transparent transparent; - border-top-style: solid; - position: absolute; - top: auto; - bottom: -14px; - left: 9px; - right: auto; - z-index: 98; } - .f-dropdown li { - font-size: 0.875rem; - cursor: pointer; - line-height: 1.125rem; - margin: 0; } - .f-dropdown li:hover, .f-dropdown li:focus { - background: #eeeeee; } - .f-dropdown li a { - display: block; - padding: 0.5rem; - color: #555555; } - .f-dropdown.content { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - padding: 1.25rem; - width: 100%; - height: auto; - max-height: none; - background: white; - border: solid 1px #cccccc; - font-size: 0.875rem; - z-index: 99; - max-width: 200px; } - .f-dropdown.content > *:first-child { - margin-top: 0; } - .f-dropdown.content > *:last-child { - margin-bottom: 0; } - .f-dropdown.tiny { - max-width: 200px; } - .f-dropdown.small { - max-width: 300px; } - .f-dropdown.medium { - max-width: 500px; } - .f-dropdown.large { - max-width: 800px; } - -table { - background: white; - margin-bottom: 1.25rem; - border: solid 1px #dddddd; } - table thead, - table tfoot { - background: whitesmoke; } - table thead tr th, - table thead tr td, - table tfoot tr th, - table tfoot tr td { - padding: 0.5rem 0.625rem 0.625rem; - font-size: 0.875rem; - font-weight: bold; - color: #222222; - text-align: left; } - table tr th, - table tr td { - padding: 0.5625rem 0.625rem; - font-size: 0.875rem; - color: #222222; } - table tr.even, table tr.alt, table tr:nth-of-type(even) { - background: #f9f9f9; } - table thead tr th, - table tfoot tr th, - table tbody tr td, - table tr td, - table tfoot tr td { - display: table-cell; - line-height: 1.125rem; } - -/* Standard Forms */ -form { - margin: 0 0 1rem; } - -/* Using forms within rows, we need to set some defaults */ -form .row .row { - margin: 0 -0.5rem; } - form .row .row .column, - form .row .row .columns { - padding: 0 0.5rem; } - form .row .row.collapse { - margin: 0; } - form .row .row.collapse .column, - form .row .row.collapse .columns { - padding: 0; } - form .row .row.collapse input { - border-bottom-right-radius: 0; - border-top-right-radius: 0; } -form .row input.column, -form .row input.columns, -form .row textarea.column, -form .row textarea.columns { - padding-left: 0.5rem; } - -/* Label Styles */ -label { - font-size: 0.875rem; - color: #4d4d4d; - cursor: pointer; - display: block; - font-weight: normal; - line-height: 1.5; - margin-bottom: 0; - /* Styles for required inputs */ } - label.right { - float: none; - text-align: right; } - label.inline { - margin: 0 0 1rem 0; - padding: 0.5625rem 0; } - label small { - text-transform: capitalize; - color: #676767; } - -select::-ms-expand { - display: none; } - -@-moz-document url-prefix() { - select { - background: #fafafa; } - - select:hover { - background: #f3f3f3; } } - -/* Attach elements to the beginning or end of an input */ -.prefix, -.postfix { - display: block; - position: relative; - z-index: 2; - text-align: center; - width: 100%; - padding-top: 0; - padding-bottom: 0; - border-style: solid; - border-width: 1px; - overflow: hidden; - font-size: 0.875rem; - height: 2.3125rem; - line-height: 2.3125rem; } - -/* Adjust padding, alignment and radius if pre/post element is a button */ -.postfix.button { - padding-left: 0; - padding-right: 0; - padding-top: 0; - padding-bottom: 0; - text-align: center; - line-height: 2.125rem; - border: none; } - -.prefix.button { - padding-left: 0; - padding-right: 0; - padding-top: 0; - padding-bottom: 0; - text-align: center; - line-height: 2.125rem; - border: none; } - -.prefix.button.radius { - border-radius: 0; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - -.postfix.button.radius { - border-radius: 0; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - -.prefix.button.round { - border-radius: 0; - border-bottom-left-radius: 1000px; - border-top-left-radius: 1000px; } - -.postfix.button.round { - border-radius: 0; - border-bottom-right-radius: 1000px; - border-top-right-radius: 1000px; } - -/* Separate prefix and postfix styles when on span or label so buttons keep their own */ -span.prefix, label.prefix { - background: #f2f2f2; - border-right: none; - color: #333333; - border-color: #cccccc; } - span.prefix.radius, label.prefix.radius { - border-radius: 0; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - -span.postfix, label.postfix { - background: #f2f2f2; - border-left: none; - color: #333333; - border-color: #cccccc; } - span.postfix.radius, label.postfix.radius { - border-radius: 0; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - -/* We use this to get basic styling on all basic form elements */ -input[type="text"], -input[type="password"], -input[type="date"], -input[type="datetime"], -input[type="datetime-local"], -input[type="month"], -input[type="week"], -input[type="email"], -input[type="number"], -input[type="search"], -input[type="tel"], -input[type="time"], -input[type="url"], -textarea { - -webkit-appearance: none; - background-color: white; - font-family: inherit; - border: 1px solid #cccccc; - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - color: rgba(0, 0, 0, 0.75); - display: block; - font-size: 0.875rem; - margin: 0 0 1rem 0; - padding: 0.5rem; - height: 2.3125rem; - width: 100%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - transition: box-shadow 0.45s, border-color 0.45s ease-in-out; } - input[type="text"]:focus, - input[type="password"]:focus, - input[type="date"]:focus, - input[type="datetime"]:focus, - input[type="datetime-local"]:focus, - input[type="month"]:focus, - input[type="week"]:focus, - input[type="email"]:focus, - input[type="number"]:focus, - input[type="search"]:focus, - input[type="tel"]:focus, - input[type="time"]:focus, - input[type="url"]:focus, - textarea:focus { - box-shadow: 0 0 5px #999999; - border-color: #999999; } - input[type="text"]:focus, - input[type="password"]:focus, - input[type="date"]:focus, - input[type="datetime"]:focus, - input[type="datetime-local"]:focus, - input[type="month"]:focus, - input[type="week"]:focus, - input[type="email"]:focus, - input[type="number"]:focus, - input[type="search"]:focus, - input[type="tel"]:focus, - input[type="time"]:focus, - input[type="url"]:focus, - textarea:focus { - background: #fafafa; - border-color: #999999; - outline: none; } - input[type="text"][disabled], fieldset[disabled] input[type="text"], - input[type="password"][disabled], fieldset[disabled] - input[type="password"], - input[type="date"][disabled], fieldset[disabled] - input[type="date"], - input[type="datetime"][disabled], fieldset[disabled] - input[type="datetime"], - input[type="datetime-local"][disabled], fieldset[disabled] - input[type="datetime-local"], - input[type="month"][disabled], fieldset[disabled] - input[type="month"], - input[type="week"][disabled], fieldset[disabled] - input[type="week"], - input[type="email"][disabled], fieldset[disabled] - input[type="email"], - input[type="number"][disabled], fieldset[disabled] - input[type="number"], - input[type="search"][disabled], fieldset[disabled] - input[type="search"], - input[type="tel"][disabled], fieldset[disabled] - input[type="tel"], - input[type="time"][disabled], fieldset[disabled] - input[type="time"], - input[type="url"][disabled], fieldset[disabled] - input[type="url"], - textarea[disabled], fieldset[disabled] - textarea { - background-color: #dddddd; } - input[type="text"].radius, - input[type="password"].radius, - input[type="date"].radius, - input[type="datetime"].radius, - input[type="datetime-local"].radius, - input[type="month"].radius, - input[type="week"].radius, - input[type="email"].radius, - input[type="number"].radius, - input[type="search"].radius, - input[type="tel"].radius, - input[type="time"].radius, - input[type="url"].radius, - textarea.radius { - border-radius: 3px; } - -input[type="submit"] { - -webkit-appearance: none; } - -/* Respect enforced amount of rows for textarea */ -textarea[rows] { - height: auto; } - -/* Add height value for select elements to match text input height */ -select { - -webkit-appearance: none !important; - background-color: #fafafa; - background-image: url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg=="); - background-repeat: no-repeat; - background-position: 97% center; - border: 1px solid #cccccc; - padding: 0.5rem; - font-size: 0.875rem; - border-radius: 0; - height: 2.3125rem; } - select.radius { - border-radius: 3px; } - select:hover { - background-color: #f3f3f3; - border-color: #999999; } - -/* Adjust margin for form elements below */ -input[type="file"], -input[type="checkbox"], -input[type="radio"], -select { - margin: 0 0 1rem 0; } - -input[type="checkbox"] + label, -input[type="radio"] + label { - display: inline-block; - margin-left: 0.5rem; - margin-right: 1rem; - margin-bottom: 0; - vertical-align: baseline; } - -/* Normalize file input width */ -input[type="file"] { - width: 100%; } - -/* We add basic fieldset styling */ -fieldset { - border: 1px solid #dddddd; - padding: 1.25rem; - margin: 1.125rem 0; } - fieldset legend { - font-weight: bold; - background: white; - padding: 0 0.1875rem; - margin: 0; - margin-left: -0.1875rem; } - -/* Error Handling */ -[data-abide] .error small.error, [data-abide] span.error, [data-abide] small.error { - display: block; - padding: 0.375rem 0.5625rem 0.5625rem; - margin-top: -1px; - margin-bottom: 1rem; - font-size: 0.75rem; - font-weight: normal; - font-style: italic; - background: #f04124; - color: white; } -[data-abide] span.error, [data-abide] small.error { - display: none; } - -span.error, small.error { - display: block; - padding: 0.375rem 0.5625rem 0.5625rem; - margin-top: -1px; - margin-bottom: 1rem; - font-size: 0.75rem; - font-weight: normal; - font-style: italic; - background: #f04124; - color: white; } - -.error input, -.error textarea, -.error select { - margin-bottom: 0; } -.error input[type="checkbox"], -.error input[type="radio"] { - margin-bottom: 1rem; } -.error label, -.error label.error { - color: #f04124; } -.error small.error { - display: block; - padding: 0.375rem 0.5625rem 0.5625rem; - margin-top: -1px; - margin-bottom: 1rem; - font-size: 0.75rem; - font-weight: normal; - font-style: italic; - background: #f04124; - color: white; } -.error > label > small { - color: #676767; - background: transparent; - padding: 0; - text-transform: capitalize; - font-style: normal; - font-size: 60%; - margin: 0; - display: inline; } -.error span.error-message { - display: block; } - -input.error, -textarea.error { - margin-bottom: 0; } - -label.error { - color: #f04124; } - -.range-slider { - display: block; - position: relative; - width: 100%; - height: 1rem; - border: 1px solid #dddddd; - margin: 1.25rem 0; - -ms-touch-action: none; - touch-action: none; - background: #fafafa; } - .range-slider.vertical-range { - display: block; - position: relative; - width: 100%; - height: 1rem; - border: 1px solid #dddddd; - margin: 1.25rem 0; - -ms-touch-action: none; - touch-action: none; - display: inline-block; - width: 1rem; - height: 12.5rem; } - .range-slider.vertical-range .range-slider-handle { - margin-top: 0; - margin-left: -0.5rem; - position: absolute; - bottom: -10.5rem; } - .range-slider.vertical-range .range-slider-active-segment { - width: 0.875rem; - height: auto; - bottom: 0; } - .range-slider.radius { - background: #fafafa; - border-radius: 3px; } - .range-slider.radius .range-slider-handle { - background: #008cba; - border-radius: 3px; } - .range-slider.radius .range-slider-handle:hover { - background: #007ba4; } - .range-slider.round { - background: #fafafa; - border-radius: 1000px; } - .range-slider.round .range-slider-handle { - background: #008cba; - border-radius: 1000px; } - .range-slider.round .range-slider-handle:hover { - background: #007ba4; } - -.range-slider-active-segment { - display: inline-block; - position: absolute; - height: 0.875rem; - background: #e5e5e5; } - -.range-slider-handle { - display: inline-block; - position: absolute; - z-index: 1; - top: -0.3125rem; - width: 2rem; - height: 1.375rem; - border: 1px solid none; - cursor: pointer; - background: #008cba; } - .range-slider-handle:hover { - background: #007ba4; } - -[class*="block-grid-"] { - display: block; - padding: 0; - margin: 0 -0.625rem; - *zoom: 1; } - [class*="block-grid-"]:before, [class*="block-grid-"]:after { - content: " "; - display: table; } - [class*="block-grid-"]:after { - clear: both; } - [class*="block-grid-"] > li { - display: block; - height: auto; - float: left; - padding: 0 0.625rem 1.25rem; } - -@media only screen { - .small-block-grid-1 > li { - width: 100%; - list-style: none; } - .small-block-grid-1 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-1 > li:nth-of-type(1n+1) { - clear: both; } - - .small-block-grid-2 > li { - width: 50%; - list-style: none; } - .small-block-grid-2 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-2 > li:nth-of-type(2n+1) { - clear: both; } - - .small-block-grid-3 > li { - width: 33.33333%; - list-style: none; } - .small-block-grid-3 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-3 > li:nth-of-type(3n+1) { - clear: both; } - - .small-block-grid-4 > li { - width: 25%; - list-style: none; } - .small-block-grid-4 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-4 > li:nth-of-type(4n+1) { - clear: both; } - - .small-block-grid-5 > li { - width: 20%; - list-style: none; } - .small-block-grid-5 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-5 > li:nth-of-type(5n+1) { - clear: both; } - - .small-block-grid-6 > li { - width: 16.66667%; - list-style: none; } - .small-block-grid-6 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-6 > li:nth-of-type(6n+1) { - clear: both; } - - .small-block-grid-7 > li { - width: 14.28571%; - list-style: none; } - .small-block-grid-7 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-7 > li:nth-of-type(7n+1) { - clear: both; } - - .small-block-grid-8 > li { - width: 12.5%; - list-style: none; } - .small-block-grid-8 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-8 > li:nth-of-type(8n+1) { - clear: both; } - - .small-block-grid-9 > li { - width: 11.11111%; - list-style: none; } - .small-block-grid-9 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-9 > li:nth-of-type(9n+1) { - clear: both; } - - .small-block-grid-10 > li { - width: 10%; - list-style: none; } - .small-block-grid-10 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-10 > li:nth-of-type(10n+1) { - clear: both; } - - .small-block-grid-11 > li { - width: 9.09091%; - list-style: none; } - .small-block-grid-11 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-11 > li:nth-of-type(11n+1) { - clear: both; } - - .small-block-grid-12 > li { - width: 8.33333%; - list-style: none; } - .small-block-grid-12 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-12 > li:nth-of-type(12n+1) { - clear: both; } } -@media only screen and (min-width: 40.063em) { - .medium-block-grid-1 > li { - width: 100%; - list-style: none; } - .medium-block-grid-1 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-1 > li:nth-of-type(1n+1) { - clear: both; } - - .medium-block-grid-2 > li { - width: 50%; - list-style: none; } - .medium-block-grid-2 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-2 > li:nth-of-type(2n+1) { - clear: both; } - - .medium-block-grid-3 > li { - width: 33.33333%; - list-style: none; } - .medium-block-grid-3 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-3 > li:nth-of-type(3n+1) { - clear: both; } - - .medium-block-grid-4 > li { - width: 25%; - list-style: none; } - .medium-block-grid-4 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-4 > li:nth-of-type(4n+1) { - clear: both; } - - .medium-block-grid-5 > li { - width: 20%; - list-style: none; } - .medium-block-grid-5 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-5 > li:nth-of-type(5n+1) { - clear: both; } - - .medium-block-grid-6 > li { - width: 16.66667%; - list-style: none; } - .medium-block-grid-6 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-6 > li:nth-of-type(6n+1) { - clear: both; } - - .medium-block-grid-7 > li { - width: 14.28571%; - list-style: none; } - .medium-block-grid-7 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-7 > li:nth-of-type(7n+1) { - clear: both; } - - .medium-block-grid-8 > li { - width: 12.5%; - list-style: none; } - .medium-block-grid-8 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-8 > li:nth-of-type(8n+1) { - clear: both; } - - .medium-block-grid-9 > li { - width: 11.11111%; - list-style: none; } - .medium-block-grid-9 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-9 > li:nth-of-type(9n+1) { - clear: both; } - - .medium-block-grid-10 > li { - width: 10%; - list-style: none; } - .medium-block-grid-10 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-10 > li:nth-of-type(10n+1) { - clear: both; } - - .medium-block-grid-11 > li { - width: 9.09091%; - list-style: none; } - .medium-block-grid-11 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-11 > li:nth-of-type(11n+1) { - clear: both; } - - .medium-block-grid-12 > li { - width: 8.33333%; - list-style: none; } - .medium-block-grid-12 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-12 > li:nth-of-type(12n+1) { - clear: both; } } -@media only screen and (min-width: 64.063em) { - .large-block-grid-1 > li { - width: 100%; - list-style: none; } - .large-block-grid-1 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-1 > li:nth-of-type(1n+1) { - clear: both; } - - .large-block-grid-2 > li { - width: 50%; - list-style: none; } - .large-block-grid-2 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-2 > li:nth-of-type(2n+1) { - clear: both; } - - .large-block-grid-3 > li { - width: 33.33333%; - list-style: none; } - .large-block-grid-3 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-3 > li:nth-of-type(3n+1) { - clear: both; } - - .large-block-grid-4 > li { - width: 25%; - list-style: none; } - .large-block-grid-4 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-4 > li:nth-of-type(4n+1) { - clear: both; } - - .large-block-grid-5 > li { - width: 20%; - list-style: none; } - .large-block-grid-5 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-5 > li:nth-of-type(5n+1) { - clear: both; } - - .large-block-grid-6 > li { - width: 16.66667%; - list-style: none; } - .large-block-grid-6 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-6 > li:nth-of-type(6n+1) { - clear: both; } - - .large-block-grid-7 > li { - width: 14.28571%; - list-style: none; } - .large-block-grid-7 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-7 > li:nth-of-type(7n+1) { - clear: both; } - - .large-block-grid-8 > li { - width: 12.5%; - list-style: none; } - .large-block-grid-8 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-8 > li:nth-of-type(8n+1) { - clear: both; } - - .large-block-grid-9 > li { - width: 11.11111%; - list-style: none; } - .large-block-grid-9 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-9 > li:nth-of-type(9n+1) { - clear: both; } - - .large-block-grid-10 > li { - width: 10%; - list-style: none; } - .large-block-grid-10 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-10 > li:nth-of-type(10n+1) { - clear: both; } - - .large-block-grid-11 > li { - width: 9.09091%; - list-style: none; } - .large-block-grid-11 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-11 > li:nth-of-type(11n+1) { - clear: both; } - - .large-block-grid-12 > li { - width: 8.33333%; - list-style: none; } - .large-block-grid-12 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-12 > li:nth-of-type(12n+1) { - clear: both; } } -.flex-video { - position: relative; - padding-top: 1.5625rem; - padding-bottom: 67.5%; - height: 0; - margin-bottom: 1rem; - overflow: hidden; } - .flex-video.widescreen { - padding-bottom: 56.34%; } - .flex-video.vimeo { - padding-top: 0; } - .flex-video iframe, - .flex-video object, - .flex-video embed, - .flex-video video { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; } - -.keystroke, -kbd { - background-color: #ededed; - border-color: #dddddd; - color: #222222; - border-style: solid; - border-width: 1px; - margin: 0; - font-family: "Consolas", "Menlo", "Courier", monospace; - font-size: inherit; - padding: 0.125rem 0.25rem 0; - border-radius: 3px; } - -/* small displays */ -@media only screen { - .show-for-small-only, .show-for-small-up, .show-for-small, .show-for-small-down, .hide-for-medium-only, .hide-for-medium-up, .hide-for-medium, .show-for-medium-down, .hide-for-large-only, .hide-for-large-up, .hide-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { - display: inherit !important; } - - .hide-for-small-only, .hide-for-small-up, .hide-for-small, .hide-for-small-down, .show-for-medium-only, .show-for-medium-up, .show-for-medium, .hide-for-medium-down, .show-for-large-only, .show-for-large-up, .show-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { - display: none !important; } - - table.show-for-small-only, table.show-for-small-up, table.show-for-small, table.show-for-small-down, table.hide-for-medium-only, table.hide-for-medium-up, table.hide-for-medium, table.show-for-medium-down, table.hide-for-large-only, table.hide-for-large-up, table.hide-for-large, table.show-for-large-down, table.hide-for-xlarge-only, table.hide-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { - display: table; } - - thead.show-for-small-only, thead.show-for-small-up, thead.show-for-small, thead.show-for-small-down, thead.hide-for-medium-only, thead.hide-for-medium-up, thead.hide-for-medium, thead.show-for-medium-down, thead.hide-for-large-only, thead.hide-for-large-up, thead.hide-for-large, thead.show-for-large-down, thead.hide-for-xlarge-only, thead.hide-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { - display: table-header-group !important; } - - tbody.show-for-small-only, tbody.show-for-small-up, tbody.show-for-small, tbody.show-for-small-down, tbody.hide-for-medium-only, tbody.hide-for-medium-up, tbody.hide-for-medium, tbody.show-for-medium-down, tbody.hide-for-large-only, tbody.hide-for-large-up, tbody.hide-for-large, tbody.show-for-large-down, tbody.hide-for-xlarge-only, tbody.hide-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { - display: table-row-group !important; } - - tr.show-for-small-only, tr.show-for-small-up, tr.show-for-small, tr.show-for-small-down, tr.hide-for-medium-only, tr.hide-for-medium-up, tr.hide-for-medium, tr.show-for-medium-down, tr.hide-for-large-only, tr.hide-for-large-up, tr.hide-for-large, tr.show-for-large-down, tr.hide-for-xlarge-only, tr.hide-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { - display: table-row !important; } - - th.show-for-small-only, td.show-for-small-only, th.show-for-small-up, td.show-for-small-up, th.show-for-small, td.show-for-small, th.show-for-small-down, td.show-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.hide-for-medium-up, td.hide-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.show-for-medium-down, td.show-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.hide-for-large-up, td.hide-for-large-up, th.hide-for-large, td.hide-for-large, th.show-for-large-down, td.show-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.hide-for-xlarge-up, td.hide-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { - display: table-cell !important; } } -/* medium displays */ -@media only screen and (min-width: 40.063em) { - .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .show-for-medium-only, .show-for-medium-up, .show-for-medium, .show-for-medium-down, .hide-for-large-only, .hide-for-large-up, .hide-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { - display: inherit !important; } - - .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .hide-for-medium-only, .hide-for-medium-up, .hide-for-medium, .hide-for-medium-down, .show-for-large-only, .show-for-large-up, .show-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { - display: none !important; } - - table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.show-for-medium-only, table.show-for-medium-up, table.show-for-medium, table.show-for-medium-down, table.hide-for-large-only, table.hide-for-large-up, table.hide-for-large, table.show-for-large-down, table.hide-for-xlarge-only, table.hide-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { - display: table; } - - thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.show-for-medium-only, thead.show-for-medium-up, thead.show-for-medium, thead.show-for-medium-down, thead.hide-for-large-only, thead.hide-for-large-up, thead.hide-for-large, thead.show-for-large-down, thead.hide-for-xlarge-only, thead.hide-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { - display: table-header-group !important; } - - tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.show-for-medium-only, tbody.show-for-medium-up, tbody.show-for-medium, tbody.show-for-medium-down, tbody.hide-for-large-only, tbody.hide-for-large-up, tbody.hide-for-large, tbody.show-for-large-down, tbody.hide-for-xlarge-only, tbody.hide-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { - display: table-row-group !important; } - - tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.show-for-medium-only, tr.show-for-medium-up, tr.show-for-medium, tr.show-for-medium-down, tr.hide-for-large-only, tr.hide-for-large-up, tr.hide-for-large, tr.show-for-large-down, tr.hide-for-xlarge-only, tr.hide-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { - display: table-row !important; } - - th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.show-for-medium-only, td.show-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.show-for-medium, td.show-for-medium, th.show-for-medium-down, td.show-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.hide-for-large-up, td.hide-for-large-up, th.hide-for-large, td.hide-for-large, th.show-for-large-down, td.show-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.hide-for-xlarge-up, td.hide-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { - display: table-cell !important; } } -/* large displays */ -@media only screen and (min-width: 64.063em) { - .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .hide-for-medium-only, .show-for-medium-up, .hide-for-medium, .hide-for-medium-down, .show-for-large-only, .show-for-large-up, .show-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { - display: inherit !important; } - - .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .show-for-medium-only, .hide-for-medium-up, .show-for-medium, .show-for-medium-down, .hide-for-large-only, .hide-for-large-up, .hide-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { - display: none !important; } - - table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.hide-for-medium-only, table.show-for-medium-up, table.hide-for-medium, table.hide-for-medium-down, table.show-for-large-only, table.show-for-large-up, table.show-for-large, table.show-for-large-down, table.hide-for-xlarge-only, table.hide-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { - display: table; } - - thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.hide-for-medium, thead.hide-for-medium-down, thead.show-for-large-only, thead.show-for-large-up, thead.show-for-large, thead.show-for-large-down, thead.hide-for-xlarge-only, thead.hide-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { - display: table-header-group !important; } - - tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.show-for-large-only, tbody.show-for-large-up, tbody.show-for-large, tbody.show-for-large-down, tbody.hide-for-xlarge-only, tbody.hide-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { - display: table-row-group !important; } - - tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.hide-for-medium, tr.hide-for-medium-down, tr.show-for-large-only, tr.show-for-large-up, tr.show-for-large, tr.show-for-large-down, tr.hide-for-xlarge-only, tr.hide-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { - display: table-row !important; } - - th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.hide-for-medium-down, td.hide-for-medium-down, th.show-for-large-only, td.show-for-large-only, th.show-for-large-up, td.show-for-large-up, th.show-for-large, td.show-for-large, th.show-for-large-down, td.show-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.hide-for-xlarge-up, td.hide-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { - display: table-cell !important; } } -/* xlarge displays */ -@media only screen and (min-width: 90.063em) { - .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .hide-for-medium-only, .show-for-medium-up, .hide-for-medium, .hide-for-medium-down, .hide-for-large-only, .show-for-large-up, .hide-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { - display: inherit !important; } - - .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .show-for-medium-only, .hide-for-medium-up, .show-for-medium, .show-for-medium-down, .show-for-large-only, .hide-for-large-up, .show-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { - display: none !important; } - - table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.hide-for-medium-only, table.show-for-medium-up, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-large-only, table.show-for-large-up, table.hide-for-large, table.hide-for-large-down, table.show-for-xlarge-only, table.show-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { - display: table; } - - thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-large-only, thead.show-for-large-up, thead.hide-for-large, thead.hide-for-large-down, thead.show-for-xlarge-only, thead.show-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { - display: table-header-group !important; } - - tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-large-only, tbody.show-for-large-up, tbody.hide-for-large, tbody.hide-for-large-down, tbody.show-for-xlarge-only, tbody.show-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { - display: table-row-group !important; } - - tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-large-only, tr.show-for-large-up, tr.hide-for-large, tr.hide-for-large-down, tr.show-for-xlarge-only, tr.show-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { - display: table-row !important; } - - th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.hide-for-medium-down, td.hide-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.show-for-large-up, td.show-for-large-up, th.hide-for-large, td.hide-for-large, th.hide-for-large-down, td.hide-for-large-down, th.show-for-xlarge-only, td.show-for-xlarge-only, th.show-for-xlarge-up, td.show-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { - display: table-cell !important; } } -/* xxlarge displays */ -@media only screen and (min-width: 120.063em) { - .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .hide-for-medium-only, .show-for-medium-up, .hide-for-medium, .hide-for-medium-down, .hide-for-large-only, .show-for-large-up, .hide-for-large, .hide-for-large-down, .hide-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { - display: inherit !important; } - - .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .show-for-medium-only, .hide-for-medium-up, .show-for-medium, .show-for-medium-down, .show-for-large-only, .hide-for-large-up, .show-for-large, .show-for-large-down, .show-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { - display: none !important; } - - table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.hide-for-medium-only, table.show-for-medium-up, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-large-only, table.show-for-large-up, table.hide-for-large, table.hide-for-large-down, table.hide-for-xlarge-only, table.show-for-xlarge-up, table.show-for-xxlarge-only, table.show-for-xxlarge-up { - display: table; } - - thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-large-only, thead.show-for-large-up, thead.hide-for-large, thead.hide-for-large-down, thead.hide-for-xlarge-only, thead.show-for-xlarge-up, thead.show-for-xxlarge-only, thead.show-for-xxlarge-up { - display: table-header-group !important; } - - tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-large-only, tbody.show-for-large-up, tbody.hide-for-large, tbody.hide-for-large-down, tbody.hide-for-xlarge-only, tbody.show-for-xlarge-up, tbody.show-for-xxlarge-only, tbody.show-for-xxlarge-up { - display: table-row-group !important; } - - tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-large-only, tr.show-for-large-up, tr.hide-for-large, tr.hide-for-large-down, tr.hide-for-xlarge-only, tr.show-for-xlarge-up, tr.show-for-xxlarge-only, tr.show-for-xxlarge-up { - display: table-row !important; } - - th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.hide-for-medium-down, td.hide-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.show-for-large-up, td.show-for-large-up, th.hide-for-large, td.hide-for-large, th.hide-for-large-down, td.hide-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.show-for-xlarge-up, td.show-for-xlarge-up, th.show-for-xxlarge-only, td.show-for-xxlarge-only, th.show-for-xxlarge-up, td.show-for-xxlarge-up { - display: table-cell !important; } } -/* Orientation targeting */ -.show-for-landscape, -.hide-for-portrait { - display: inherit !important; } - -.hide-for-landscape, -.show-for-portrait { - display: none !important; } - -/* Specific visibility for tables */ -table.hide-for-landscape, table.show-for-portrait { - display: table; } - -thead.hide-for-landscape, thead.show-for-portrait { - display: table-header-group !important; } - -tbody.hide-for-landscape, tbody.show-for-portrait { - display: table-row-group !important; } - -tr.hide-for-landscape, tr.show-for-portrait { - display: table-row !important; } - -td.hide-for-landscape, td.show-for-portrait, -th.hide-for-landscape, -th.show-for-portrait { - display: table-cell !important; } - -@media only screen and (orientation: landscape) { - .show-for-landscape, - .hide-for-portrait { - display: inherit !important; } - - .hide-for-landscape, - .show-for-portrait { - display: none !important; } - - /* Specific visibility for tables */ - table.show-for-landscape, table.hide-for-portrait { - display: table; } - - thead.show-for-landscape, thead.hide-for-portrait { - display: table-header-group !important; } - - tbody.show-for-landscape, tbody.hide-for-portrait { - display: table-row-group !important; } - - tr.show-for-landscape, tr.hide-for-portrait { - display: table-row !important; } - - td.show-for-landscape, td.hide-for-portrait, - th.show-for-landscape, - th.hide-for-portrait { - display: table-cell !important; } } -@media only screen and (orientation: portrait) { - .show-for-portrait, - .hide-for-landscape { - display: inherit !important; } - - .hide-for-portrait, - .show-for-landscape { - display: none !important; } - - /* Specific visibility for tables */ - table.show-for-portrait, table.hide-for-landscape { - display: table; } - - thead.show-for-portrait, thead.hide-for-landscape { - display: table-header-group !important; } - - tbody.show-for-portrait, tbody.hide-for-landscape { - display: table-row-group !important; } - - tr.show-for-portrait, tr.hide-for-landscape { - display: table-row !important; } - - td.show-for-portrait, td.hide-for-landscape, - th.show-for-portrait, - th.hide-for-landscape { - display: table-cell !important; } } -/* Touch-enabled device targeting */ -.show-for-touch { - display: none !important; } - -.hide-for-touch { - display: inherit !important; } - -.touch .show-for-touch { - display: inherit !important; } - -.touch .hide-for-touch { - display: none !important; } - -/* Specific visibility for tables */ -table.hide-for-touch { - display: table; } - -.touch table.show-for-touch { - display: table; } - -thead.hide-for-touch { - display: table-header-group !important; } - -.touch thead.show-for-touch { - display: table-header-group !important; } - -tbody.hide-for-touch { - display: table-row-group !important; } - -.touch tbody.show-for-touch { - display: table-row-group !important; } - -tr.hide-for-touch { - display: table-row !important; } - -.touch tr.show-for-touch { - display: table-row !important; } - -td.hide-for-touch { - display: table-cell !important; } - -.touch td.show-for-touch { - display: table-cell !important; } - -th.hide-for-touch { - display: table-cell !important; } - -.touch th.show-for-touch { - display: table-cell !important; } diff --git a/css/foundation.min.css b/css/foundation.min.css deleted file mode 100644 index a9e0be2..0000000 --- a/css/foundation.min.css +++ /dev/null @@ -1 +0,0 @@ -meta.foundation-version{font-family:"/5.4.7/"}meta.foundation-mq-small{font-family:"/only screen/";width:0em}meta.foundation-mq-medium{font-family:"/only screen and (min-width:40.063em)/";width:40.063em}meta.foundation-mq-large{font-family:"/only screen and (min-width:64.063em)/";width:64.063em}meta.foundation-mq-xlarge{font-family:"/only screen and (min-width:90.063em)/";width:90.063em}meta.foundation-mq-xxlarge{font-family:"/only screen and (min-width:120.063em)/";width:120.063em}meta.foundation-data-attribute-namespace{font-family:false}html,body{height:100%}*,*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:#222;padding:0;margin:0;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;font-style:normal;line-height:1.5;position:relative;cursor:auto}a:hover{cursor:pointer}img{max-width:100%;height:auto}img{-ms-interpolation-mode:bicubic}#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none !important}.left{float:left !important}.right{float:right !important}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.hide{display:none !important;visibility:hidden}.invisible{visibility:hidden}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}img{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.row{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5rem}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.row.collapse>.column,.row.collapse>.columns{padding-left:0;padding-right:0}.row.collapse .row{margin-left:0;margin-right:0}.row .row{width:auto;margin-left:-0.9375rem;margin-right:-0.9375rem;margin-top:0;margin-bottom:0;max-width:none}.row .row:before,.row .row:after{content:" ";display:table}.row .row:after{clear:both}.row .row.collapse{width:auto;margin:0;max-width:none}.row .row.collapse:before,.row .row.collapse:after{content:" ";display:table}.row .row.collapse:after{clear:both}.column,.columns{padding-left:0.9375rem;padding-right:0.9375rem;width:100%;float:left}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}@media only screen{.small-push-0{position:relative;left:0%;right:auto}.small-pull-0{position:relative;right:0%;left:auto}.small-push-1{position:relative;left:8.33333%;right:auto}.small-pull-1{position:relative;right:8.33333%;left:auto}.small-push-2{position:relative;left:16.66667%;right:auto}.small-pull-2{position:relative;right:16.66667%;left:auto}.small-push-3{position:relative;left:25%;right:auto}.small-pull-3{position:relative;right:25%;left:auto}.small-push-4{position:relative;left:33.33333%;right:auto}.small-pull-4{position:relative;right:33.33333%;left:auto}.small-push-5{position:relative;left:41.66667%;right:auto}.small-pull-5{position:relative;right:41.66667%;left:auto}.small-push-6{position:relative;left:50%;right:auto}.small-pull-6{position:relative;right:50%;left:auto}.small-push-7{position:relative;left:58.33333%;right:auto}.small-pull-7{position:relative;right:58.33333%;left:auto}.small-push-8{position:relative;left:66.66667%;right:auto}.small-pull-8{position:relative;right:66.66667%;left:auto}.small-push-9{position:relative;left:75%;right:auto}.small-pull-9{position:relative;right:75%;left:auto}.small-push-10{position:relative;left:83.33333%;right:auto}.small-pull-10{position:relative;right:83.33333%;left:auto}.small-push-11{position:relative;left:91.66667%;right:auto}.small-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.small-1{width:8.33333%}.small-2{width:16.66667%}.small-3{width:25%}.small-4{width:33.33333%}.small-5{width:41.66667%}.small-6{width:50%}.small-7{width:58.33333%}.small-8{width:66.66667%}.small-9{width:75%}.small-10{width:83.33333%}.small-11{width:91.66667%}.small-12{width:100%}.small-offset-0{margin-left:0% !important}.small-offset-1{margin-left:8.33333% !important}.small-offset-2{margin-left:16.66667% !important}.small-offset-3{margin-left:25% !important}.small-offset-4{margin-left:33.33333% !important}.small-offset-5{margin-left:41.66667% !important}.small-offset-6{margin-left:50% !important}.small-offset-7{margin-left:58.33333% !important}.small-offset-8{margin-left:66.66667% !important}.small-offset-9{margin-left:75% !important}.small-offset-10{margin-left:83.33333% !important}.small-offset-11{margin-left:91.66667% !important}.small-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.column.small-centered,.columns.small-centered{margin-left:auto;margin-right:auto;float:none}.column.small-uncentered,.columns.small-uncentered{margin-left:0;margin-right:0;float:left}.column.small-centered:last-child,.columns.small-centered:last-child{float:none}.column.small-uncentered:last-child,.columns.small-uncentered:last-child{float:left}.column.small-uncentered.opposite,.columns.small-uncentered.opposite{float:right}}@media only screen and (min-width: 40.063em){.medium-push-0{position:relative;left:0%;right:auto}.medium-pull-0{position:relative;right:0%;left:auto}.medium-push-1{position:relative;left:8.33333%;right:auto}.medium-pull-1{position:relative;right:8.33333%;left:auto}.medium-push-2{position:relative;left:16.66667%;right:auto}.medium-pull-2{position:relative;right:16.66667%;left:auto}.medium-push-3{position:relative;left:25%;right:auto}.medium-pull-3{position:relative;right:25%;left:auto}.medium-push-4{position:relative;left:33.33333%;right:auto}.medium-pull-4{position:relative;right:33.33333%;left:auto}.medium-push-5{position:relative;left:41.66667%;right:auto}.medium-pull-5{position:relative;right:41.66667%;left:auto}.medium-push-6{position:relative;left:50%;right:auto}.medium-pull-6{position:relative;right:50%;left:auto}.medium-push-7{position:relative;left:58.33333%;right:auto}.medium-pull-7{position:relative;right:58.33333%;left:auto}.medium-push-8{position:relative;left:66.66667%;right:auto}.medium-pull-8{position:relative;right:66.66667%;left:auto}.medium-push-9{position:relative;left:75%;right:auto}.medium-pull-9{position:relative;right:75%;left:auto}.medium-push-10{position:relative;left:83.33333%;right:auto}.medium-pull-10{position:relative;right:83.33333%;left:auto}.medium-push-11{position:relative;left:91.66667%;right:auto}.medium-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.medium-1{width:8.33333%}.medium-2{width:16.66667%}.medium-3{width:25%}.medium-4{width:33.33333%}.medium-5{width:41.66667%}.medium-6{width:50%}.medium-7{width:58.33333%}.medium-8{width:66.66667%}.medium-9{width:75%}.medium-10{width:83.33333%}.medium-11{width:91.66667%}.medium-12{width:100%}.medium-offset-0{margin-left:0% !important}.medium-offset-1{margin-left:8.33333% !important}.medium-offset-2{margin-left:16.66667% !important}.medium-offset-3{margin-left:25% !important}.medium-offset-4{margin-left:33.33333% !important}.medium-offset-5{margin-left:41.66667% !important}.medium-offset-6{margin-left:50% !important}.medium-offset-7{margin-left:58.33333% !important}.medium-offset-8{margin-left:66.66667% !important}.medium-offset-9{margin-left:75% !important}.medium-offset-10{margin-left:83.33333% !important}.medium-offset-11{margin-left:91.66667% !important}.medium-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.column.medium-centered,.columns.medium-centered{margin-left:auto;margin-right:auto;float:none}.column.medium-uncentered,.columns.medium-uncentered{margin-left:0;margin-right:0;float:left}.column.medium-centered:last-child,.columns.medium-centered:last-child{float:none}.column.medium-uncentered:last-child,.columns.medium-uncentered:last-child{float:left}.column.medium-uncentered.opposite,.columns.medium-uncentered.opposite{float:right}.push-0{position:relative;left:0%;right:auto}.pull-0{position:relative;right:0%;left:auto}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}@media only screen and (min-width: 64.063em){.large-push-0{position:relative;left:0%;right:auto}.large-pull-0{position:relative;right:0%;left:auto}.large-push-1{position:relative;left:8.33333%;right:auto}.large-pull-1{position:relative;right:8.33333%;left:auto}.large-push-2{position:relative;left:16.66667%;right:auto}.large-pull-2{position:relative;right:16.66667%;left:auto}.large-push-3{position:relative;left:25%;right:auto}.large-pull-3{position:relative;right:25%;left:auto}.large-push-4{position:relative;left:33.33333%;right:auto}.large-pull-4{position:relative;right:33.33333%;left:auto}.large-push-5{position:relative;left:41.66667%;right:auto}.large-pull-5{position:relative;right:41.66667%;left:auto}.large-push-6{position:relative;left:50%;right:auto}.large-pull-6{position:relative;right:50%;left:auto}.large-push-7{position:relative;left:58.33333%;right:auto}.large-pull-7{position:relative;right:58.33333%;left:auto}.large-push-8{position:relative;left:66.66667%;right:auto}.large-pull-8{position:relative;right:66.66667%;left:auto}.large-push-9{position:relative;left:75%;right:auto}.large-pull-9{position:relative;right:75%;left:auto}.large-push-10{position:relative;left:83.33333%;right:auto}.large-pull-10{position:relative;right:83.33333%;left:auto}.large-push-11{position:relative;left:91.66667%;right:auto}.large-pull-11{position:relative;right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375rem;padding-right:0.9375rem;float:left}.large-1{width:8.33333%}.large-2{width:16.66667%}.large-3{width:25%}.large-4{width:33.33333%}.large-5{width:41.66667%}.large-6{width:50%}.large-7{width:58.33333%}.large-8{width:66.66667%}.large-9{width:75%}.large-10{width:83.33333%}.large-11{width:91.66667%}.large-12{width:100%}.large-offset-0{margin-left:0% !important}.large-offset-1{margin-left:8.33333% !important}.large-offset-2{margin-left:16.66667% !important}.large-offset-3{margin-left:25% !important}.large-offset-4{margin-left:33.33333% !important}.large-offset-5{margin-left:41.66667% !important}.large-offset-6{margin-left:50% !important}.large-offset-7{margin-left:58.33333% !important}.large-offset-8{margin-left:66.66667% !important}.large-offset-9{margin-left:75% !important}.large-offset-10{margin-left:83.33333% !important}.large-offset-11{margin-left:91.66667% !important}.large-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.column.large-centered,.columns.large-centered{margin-left:auto;margin-right:auto;float:none}.column.large-uncentered,.columns.large-uncentered{margin-left:0;margin-right:0;float:left}.column.large-centered:last-child,.columns.large-centered:last-child{float:none}.column.large-uncentered:last-child,.columns.large-uncentered:last-child{float:left}.column.large-uncentered.opposite,.columns.large-uncentered.opposite{float:right}.push-0{position:relative;left:0%;right:auto}.pull-0{position:relative;right:0%;left:auto}.push-1{position:relative;left:8.33333%;right:auto}.pull-1{position:relative;right:8.33333%;left:auto}.push-2{position:relative;left:16.66667%;right:auto}.pull-2{position:relative;right:16.66667%;left:auto}.push-3{position:relative;left:25%;right:auto}.pull-3{position:relative;right:25%;left:auto}.push-4{position:relative;left:33.33333%;right:auto}.pull-4{position:relative;right:33.33333%;left:auto}.push-5{position:relative;left:41.66667%;right:auto}.pull-5{position:relative;right:41.66667%;left:auto}.push-6{position:relative;left:50%;right:auto}.pull-6{position:relative;right:50%;left:auto}.push-7{position:relative;left:58.33333%;right:auto}.pull-7{position:relative;right:58.33333%;left:auto}.push-8{position:relative;left:66.66667%;right:auto}.pull-8{position:relative;right:66.66667%;left:auto}.push-9{position:relative;left:75%;right:auto}.pull-9{position:relative;right:75%;left:auto}.push-10{position:relative;left:83.33333%;right:auto}.pull-10{position:relative;right:83.33333%;left:auto}.push-11{position:relative;left:91.66667%;right:auto}.pull-11{position:relative;right:91.66667%;left:auto}}button,.button{border-style:solid;border-width:0px;cursor:pointer;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;line-height:normal;margin:0 0 1.25rem;position:relative;text-decoration:none;text-align:center;-webkit-appearance:none;-webkit-border-radius:0;display:inline-block;padding-top:1rem;padding-right:2rem;padding-bottom:1.0625rem;padding-left:2rem;font-size:1rem;background-color:#008cba;border-color:#007095;color:#fff;transition:background-color 300ms ease-out}button:hover,button:focus,.button:hover,.button:focus{background-color:#007095}button:hover,button:focus,.button:hover,.button:focus{color:#fff}button.secondary,.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{background-color:#b9b9b9}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{color:#333}button.success,.button.success{background-color:#43ac6a;border-color:#368a55;color:#fff}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{background-color:#368a55}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{color:#fff}button.alert,.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{background-color:#cf2a0e}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{color:#fff}button.warning,.button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}button.warning:hover,button.warning:focus,.button.warning:hover,.button.warning:focus{background-color:#cf6e0e}button.warning:hover,button.warning:focus,.button.warning:hover,.button.warning:focus{color:#fff}button.info,.button.info{background-color:#a0d3e8;border-color:#61b6d9;color:#333}button.info:hover,button.info:focus,.button.info:hover,.button.info:focus{background-color:#61b6d9}button.info:hover,button.info:focus,.button.info:hover,.button.info:focus{color:#fff}button.large,.button.large{padding-top:1.125rem;padding-right:2.25rem;padding-bottom:1.1875rem;padding-left:2.25rem;font-size:1.25rem}button.small,.button.small{padding-top:0.875rem;padding-right:1.75rem;padding-bottom:0.9375rem;padding-left:1.75rem;font-size:0.8125rem}button.tiny,.button.tiny{padding-top:0.625rem;padding-right:1.25rem;padding-bottom:0.6875rem;padding-left:1.25rem;font-size:0.6875rem}button.expand,.button.expand{padding-right:0;padding-left:0;width:100%}button.left-align,.button.left-align{text-align:left;text-indent:0.75rem}button.right-align,.button.right-align{text-align:right;padding-right:0.75rem}button.radius,.button.radius{border-radius:3px}button.round,.button.round{border-radius:1000px}button.disabled,button[disabled],.button.disabled,.button[disabled]{background-color:#008cba;border-color:#007095;color:#fff;cursor:default;opacity:0.7;box-shadow:none}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#007095}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{color:#fff}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#008cba}button.disabled.secondary,button[disabled].secondary,.button.disabled.secondary,.button[disabled].secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333;cursor:default;opacity:0.7;box-shadow:none}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#b9b9b9}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{color:#333}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#e7e7e7}button.disabled.success,button[disabled].success,.button.disabled.success,.button[disabled].success{background-color:#43ac6a;border-color:#368a55;color:#fff;cursor:default;opacity:0.7;box-shadow:none}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#368a55}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{color:#fff}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#43ac6a}button.disabled.alert,button[disabled].alert,.button.disabled.alert,.button[disabled].alert{background-color:#f04124;border-color:#cf2a0e;color:#fff;cursor:default;opacity:0.7;box-shadow:none}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#cf2a0e}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{color:#fff}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#f04124}button.disabled.warning,button[disabled].warning,.button.disabled.warning,.button[disabled].warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff;cursor:default;opacity:0.7;box-shadow:none}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#cf6e0e}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{color:#fff}button.disabled.warning:hover,button.disabled.warning:focus,button[disabled].warning:hover,button[disabled].warning:focus,.button.disabled.warning:hover,.button.disabled.warning:focus,.button[disabled].warning:hover,.button[disabled].warning:focus{background-color:#f08a24}button.disabled.info,button[disabled].info,.button.disabled.info,.button[disabled].info{background-color:#a0d3e8;border-color:#61b6d9;color:#333;cursor:default;opacity:0.7;box-shadow:none}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{background-color:#61b6d9}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{color:#fff}button.disabled.info:hover,button.disabled.info:focus,button[disabled].info:hover,button[disabled].info:focus,.button.disabled.info:hover,.button.disabled.info:focus,.button[disabled].info:hover,.button[disabled].info:focus{background-color:#a0d3e8}button::-moz-focus-inner{border:0;padding:0}@media only screen and (min-width: 40.063em){button,.button{display:inline-block}}form{margin:0 0 1rem}form .row .row{margin:0 -0.5rem}form .row .row .column,form .row .row .columns{padding:0 0.5rem}form .row .row.collapse{margin:0}form .row .row.collapse .column,form .row .row.collapse .columns{padding:0}form .row .row.collapse input{-webkit-border-bottom-right-radius:0;-webkit-border-top-right-radius:0;border-bottom-right-radius:0;border-top-right-radius:0}form .row input.column,form .row input.columns,form .row textarea.column,form .row textarea.columns{padding-left:0.5rem}label{font-size:0.875rem;color:#4d4d4d;cursor:pointer;display:block;font-weight:normal;line-height:1.5;margin-bottom:0}label.right{float:none !important;text-align:right}label.inline{margin:0 0 1rem 0;padding:0.5625rem 0}label small{text-transform:capitalize;color:#676767}.prefix,.postfix{display:block;position:relative;z-index:2;text-align:center;width:100%;padding-top:0;padding-bottom:0;border-style:solid;border-width:1px;overflow:hidden;font-size:0.875rem;height:2.3125rem;line-height:2.3125rem}.postfix.button{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;text-align:center;line-height:2.125rem;border:none}.prefix.button{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;text-align:center;line-height:2.125rem;border:none}.prefix.button.radius{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.postfix.button.radius{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.prefix.button.round{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.postfix.button.round{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}span.prefix,label.prefix{background:#f2f2f2;border-right:none;color:#333;border-color:#ccc}span.postfix,label.postfix{background:#f2f2f2;border-left:none;color:#333;border-color:#ccc}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="datetime-local"],input[type="month"],input[type="week"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],input[type="color"],textarea{-webkit-appearance:none;-webkit-border-radius:0px;background-color:#fff;font-family:inherit;border-style:solid;border-width:1px;border-color:#ccc;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);color:rgba(0,0,0,0.75);display:block;font-size:0.875rem;margin:0 0 1rem 0;padding:0.5rem;height:2.3125rem;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;transition:box-shadow 0.45s,border-color 0.45s ease-in-out}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,input[type="color"]:focus,textarea:focus{box-shadow:0 0 5px #999;border-color:#999}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,input[type="color"]:focus,textarea:focus{background:#fafafa;border-color:#999;outline:none}input[type="text"]:disabled,input[type="password"]:disabled,input[type="date"]:disabled,input[type="datetime"]:disabled,input[type="datetime-local"]:disabled,input[type="month"]:disabled,input[type="week"]:disabled,input[type="email"]:disabled,input[type="number"]:disabled,input[type="search"]:disabled,input[type="tel"]:disabled,input[type="time"]:disabled,input[type="url"]:disabled,input[type="color"]:disabled,textarea:disabled{background-color:#ddd;cursor:default}input[type="text"][disabled],input[type="text"][readonly],fieldset[disabled] input[type="text"],input[type="password"][disabled],input[type="password"][readonly],fieldset[disabled] input[type="password"],input[type="date"][disabled],input[type="date"][readonly],fieldset[disabled] input[type="date"],input[type="datetime"][disabled],input[type="datetime"][readonly],fieldset[disabled] input[type="datetime"],input[type="datetime-local"][disabled],input[type="datetime-local"][readonly],fieldset[disabled] input[type="datetime-local"],input[type="month"][disabled],input[type="month"][readonly],fieldset[disabled] input[type="month"],input[type="week"][disabled],input[type="week"][readonly],fieldset[disabled] input[type="week"],input[type="email"][disabled],input[type="email"][readonly],fieldset[disabled] input[type="email"],input[type="number"][disabled],input[type="number"][readonly],fieldset[disabled] input[type="number"],input[type="search"][disabled],input[type="search"][readonly],fieldset[disabled] input[type="search"],input[type="tel"][disabled],input[type="tel"][readonly],fieldset[disabled] input[type="tel"],input[type="time"][disabled],input[type="time"][readonly],fieldset[disabled] input[type="time"],input[type="url"][disabled],input[type="url"][readonly],fieldset[disabled] input[type="url"],input[type="color"][disabled],input[type="color"][readonly],fieldset[disabled] input[type="color"],textarea[disabled],textarea[readonly],fieldset[disabled] textarea{background-color:#ddd;cursor:default}input[type="text"].radius,input[type="password"].radius,input[type="date"].radius,input[type="datetime"].radius,input[type="datetime-local"].radius,input[type="month"].radius,input[type="week"].radius,input[type="email"].radius,input[type="number"].radius,input[type="search"].radius,input[type="tel"].radius,input[type="time"].radius,input[type="url"].radius,input[type="color"].radius,textarea.radius{border-radius:3px}form .row .prefix-radius.row.collapse input,form .row .prefix-radius.row.collapse textarea,form .row .prefix-radius.row.collapse select{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}form .row .prefix-radius.row.collapse .prefix{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}form .row .postfix-radius.row.collapse input,form .row .postfix-radius.row.collapse textarea,form .row .postfix-radius.row.collapse select{border-radius:0;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}form .row .postfix-radius.row.collapse .postfix{border-radius:0;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}form .row .prefix-round.row.collapse input,form .row .prefix-round.row.collapse textarea,form .row .prefix-round.row.collapse select{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}form .row .prefix-round.row.collapse .prefix{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}form .row .postfix-round.row.collapse input,form .row .postfix-round.row.collapse textarea,form .row .postfix-round.row.collapse select{border-radius:0;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}form .row .postfix-round.row.collapse .postfix{border-radius:0;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}input[type="submit"]{-webkit-appearance:none;-webkit-border-radius:0px}textarea[rows]{height:auto}textarea{max-width:100%}select{-webkit-appearance:none !important;-webkit-border-radius:0px;background-color:#fafafa;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+);background-position:100% center;background-repeat:no-repeat;border-style:solid;border-width:1px;border-color:#ccc;padding:0.5rem;font-size:0.875rem;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;color:rgba(0,0,0,0.75);line-height:normal;border-radius:0;height:2.3125rem}select::-ms-expand{display:none}select.radius{border-radius:3px}select:hover{background-color:#f3f3f3;border-color:#999}select:disabled{background-color:#ddd;cursor:default}input[type="file"],input[type="checkbox"],input[type="radio"],select{margin:0 0 1rem 0}input[type="checkbox"]+label,input[type="radio"]+label{display:inline-block;margin-left:0.5rem;margin-right:1rem;margin-bottom:0;vertical-align:baseline}input[type="file"]{width:100%}fieldset{border:1px solid #ddd;padding:1.25rem;margin:1.125rem 0}fieldset legend{font-weight:bold;background:#fff;padding:0 0.1875rem;margin:0;margin-left:-0.1875rem}[data-abide] .error small.error,[data-abide] .error span.error,[data-abide] span.error,[data-abide] small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#f04124;color:#fff}[data-abide] span.error,[data-abide] small.error{display:none}span.error,small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#f04124;color:#fff}.error input,.error textarea,.error select{margin-bottom:0}.error input[type="checkbox"],.error input[type="radio"]{margin-bottom:1rem}.error label,.error label.error{color:#f04124}.error small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#f04124;color:#fff}.error>label>small{color:#676767;background:transparent;padding:0;text-transform:capitalize;font-style:normal;font-size:60%;margin:0;display:inline}.error span.error-message{display:block}input.error,textarea.error,select.error{margin-bottom:0}label.error{color:#f04124}meta.foundation-mq-topbar{font-family:"/only screen and (min-width:40.063em)/";width:40.063em}.contain-to-grid{width:100%;background:#333}.contain-to-grid .top-bar{margin-bottom:0}.fixed{width:100%;left:0;position:fixed;top:0;z-index:99}.fixed.expanded:not(.top-bar){overflow-y:auto;height:auto;width:100%;max-height:100%}.fixed.expanded:not(.top-bar) .title-area{position:fixed;width:100%;z-index:99}.fixed.expanded:not(.top-bar) .top-bar-section{z-index:98;margin-top:45px}.top-bar{overflow:hidden;height:45px;line-height:45px;position:relative;background:#333;margin-bottom:0}.top-bar ul{margin-bottom:0;list-style:none}.top-bar .row{max-width:none}.top-bar form,.top-bar input{margin-bottom:0}.top-bar input{height:1.8rem;padding-top:.35rem;padding-bottom:.35rem;font-size:0.75rem}.top-bar .button,.top-bar button{padding-top:0.4125rem;padding-bottom:0.4125rem;margin-bottom:0;font-size:0.75rem}@media only screen and (max-width: 40em){.top-bar .button,.top-bar button{position:relative;top:-1px}}.top-bar .title-area{position:relative;margin:0}.top-bar .name{height:45px;margin:0;font-size:16px}.top-bar .name h1{line-height:45px;font-size:1.0625rem;margin:0}.top-bar .name h1 a{font-weight:normal;color:#fff;width:75%;display:block;padding:0 15px}.top-bar .toggle-topbar{position:absolute;right:0;top:0}.top-bar .toggle-topbar a{color:#fff;text-transform:uppercase;font-size:0.8125rem;font-weight:bold;position:relative;display:block;padding:0 15px;height:45px;line-height:45px}.top-bar .toggle-topbar.menu-icon{top:50%;margin-top:-16px}.top-bar .toggle-topbar.menu-icon a{height:34px;line-height:33px;padding:0 40px 0 15px;color:#fff;position:relative}.top-bar .toggle-topbar.menu-icon a span::after{content:"";position:absolute;display:block;height:0;top:50%;margin-top:-8px;right:15px;box-shadow:0 0px 0 1px #fff,0 7px 0 1px #fff,0 14px 0 1px #fff;width:16px}.top-bar .toggle-topbar.menu-icon a span:hover:after{box-shadow:0 0px 0 1px "",0 7px 0 1px "",0 14px 0 1px ""}.top-bar.expanded{height:auto;background:transparent}.top-bar.expanded .title-area{background:#333}.top-bar.expanded .toggle-topbar a{color:#888}.top-bar.expanded .toggle-topbar a span::after{box-shadow:0 0px 0 1px #888,0 7px 0 1px #888,0 14px 0 1px #888}.top-bar-section{left:0;position:relative;width:auto;transition:left 300ms ease-out}.top-bar-section ul{padding:0;width:100%;height:auto;display:block;font-size:16px;margin:0}.top-bar-section .divider,.top-bar-section [role="separator"]{border-top:solid 1px #1a1a1a;clear:both;height:1px;width:100%}.top-bar-section ul li{background:#333}.top-bar-section ul li>a{display:block;width:100%;color:#fff;padding:12px 0 12px 0;padding-left:15px;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:0.8125rem;font-weight:normal;text-transform:none}.top-bar-section ul li>a.button{font-size:0.8125rem;padding-right:15px;padding-left:15px;background-color:#008cba;border-color:#007095;color:#fff}.top-bar-section ul li>a.button:hover,.top-bar-section ul li>a.button:focus{background-color:#007095}.top-bar-section ul li>a.button:hover,.top-bar-section ul li>a.button:focus{color:#fff}.top-bar-section ul li>a.button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}.top-bar-section ul li>a.button.secondary:hover,.top-bar-section ul li>a.button.secondary:focus{background-color:#b9b9b9}.top-bar-section ul li>a.button.secondary:hover,.top-bar-section ul li>a.button.secondary:focus{color:#333}.top-bar-section ul li>a.button.success{background-color:#43ac6a;border-color:#368a55;color:#fff}.top-bar-section ul li>a.button.success:hover,.top-bar-section ul li>a.button.success:focus{background-color:#368a55}.top-bar-section ul li>a.button.success:hover,.top-bar-section ul li>a.button.success:focus{color:#fff}.top-bar-section ul li>a.button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}.top-bar-section ul li>a.button.alert:hover,.top-bar-section ul li>a.button.alert:focus{background-color:#cf2a0e}.top-bar-section ul li>a.button.alert:hover,.top-bar-section ul li>a.button.alert:focus{color:#fff}.top-bar-section ul li>a.button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}.top-bar-section ul li>a.button.warning:hover,.top-bar-section ul li>a.button.warning:focus{background-color:#cf6e0e}.top-bar-section ul li>a.button.warning:hover,.top-bar-section ul li>a.button.warning:focus{color:#fff}.top-bar-section ul li>button{font-size:0.8125rem;padding-right:15px;padding-left:15px;background-color:#008cba;border-color:#007095;color:#fff}.top-bar-section ul li>button:hover,.top-bar-section ul li>button:focus{background-color:#007095}.top-bar-section ul li>button:hover,.top-bar-section ul li>button:focus{color:#fff}.top-bar-section ul li>button.secondary{background-color:#e7e7e7;border-color:#b9b9b9;color:#333}.top-bar-section ul li>button.secondary:hover,.top-bar-section ul li>button.secondary:focus{background-color:#b9b9b9}.top-bar-section ul li>button.secondary:hover,.top-bar-section ul li>button.secondary:focus{color:#333}.top-bar-section ul li>button.success{background-color:#43ac6a;border-color:#368a55;color:#fff}.top-bar-section ul li>button.success:hover,.top-bar-section ul li>button.success:focus{background-color:#368a55}.top-bar-section ul li>button.success:hover,.top-bar-section ul li>button.success:focus{color:#fff}.top-bar-section ul li>button.alert{background-color:#f04124;border-color:#cf2a0e;color:#fff}.top-bar-section ul li>button.alert:hover,.top-bar-section ul li>button.alert:focus{background-color:#cf2a0e}.top-bar-section ul li>button.alert:hover,.top-bar-section ul li>button.alert:focus{color:#fff}.top-bar-section ul li>button.warning{background-color:#f08a24;border-color:#cf6e0e;color:#fff}.top-bar-section ul li>button.warning:hover,.top-bar-section ul li>button.warning:focus{background-color:#cf6e0e}.top-bar-section ul li>button.warning:hover,.top-bar-section ul li>button.warning:focus{color:#fff}.top-bar-section ul li:hover:not(.has-form)>a{background-color:#555;background:#272727;color:#fff}.top-bar-section ul li.active>a{background:#008cba;color:#fff}.top-bar-section ul li.active>a:hover{background:#0078a0;color:#fff}.top-bar-section .has-form{padding:15px}.top-bar-section .has-dropdown{position:relative}.top-bar-section .has-dropdown>a:after{content:"";display:block;width:0;height:0;border:inset 5px;border-color:transparent transparent transparent rgba(255,255,255,0.4);border-left-style:solid;margin-right:15px;margin-top:-4.5px;position:absolute;top:50%;right:0}.top-bar-section .has-dropdown.moved{position:static}.top-bar-section .has-dropdown.moved>.dropdown{display:block;position:static !important;height:auto;width:auto;overflow:visible;clip:auto;position:absolute !important;width:100%}.top-bar-section .has-dropdown.moved>a:after{display:none}.top-bar-section .dropdown{padding:0;position:absolute;left:100%;top:0;z-index:99;display:block;position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}.top-bar-section .dropdown li{width:100%;height:auto}.top-bar-section .dropdown li a{font-weight:normal;padding:8px 15px}.top-bar-section .dropdown li a.parent-link{font-weight:normal}.top-bar-section .dropdown li.title h5,.top-bar-section .dropdown li.parent-link{margin-bottom:0;margin-top:0;font-size:1.125rem}.top-bar-section .dropdown li.title h5 a,.top-bar-section .dropdown li.parent-link a{color:#fff;display:block}.top-bar-section .dropdown li.title h5 a:hover,.top-bar-section .dropdown li.parent-link a:hover{background:none}.top-bar-section .dropdown li.has-form{padding:8px 15px}.top-bar-section .dropdown li .button,.top-bar-section .dropdown li button{top:auto}.top-bar-section .dropdown label{padding:8px 15px 2px;margin-bottom:0;text-transform:uppercase;color:#777;font-weight:bold;font-size:0.625rem}.js-generated{display:block}@media only screen and (min-width: 40.063em){.top-bar{background:#333;overflow:visible}.top-bar:before,.top-bar:after{content:" ";display:table}.top-bar:after{clear:both}.top-bar .toggle-topbar{display:none}.top-bar .title-area{float:left}.top-bar .name h1 a{width:auto}.top-bar input,.top-bar .button,.top-bar button{font-size:0.875rem;position:relative;top:7px}.top-bar.expanded{background:#333}.contain-to-grid .top-bar{max-width:62.5rem;margin:0 auto;margin-bottom:0}.top-bar-section{transition:none 0 0;left:0 !important}.top-bar-section ul{width:auto;height:auto !important;display:inline}.top-bar-section ul li{float:left}.top-bar-section ul li .js-generated{display:none}.top-bar-section li.hover>a:not(.button){background-color:#555;background:#272727;color:#fff}.top-bar-section li:not(.has-form) a:not(.button){padding:0 15px;line-height:45px;background:#333}.top-bar-section li:not(.has-form) a:not(.button):hover{background-color:#555;background:#272727}.top-bar-section li.active:not(.has-form) a:not(.button){padding:0 15px;line-height:45px;color:#fff;background:#008cba}.top-bar-section li.active:not(.has-form) a:not(.button):hover{background:#0078a0;color:#fff}.top-bar-section .has-dropdown>a{padding-right:35px !important}.top-bar-section .has-dropdown>a:after{content:"";display:block;width:0;height:0;border:inset 5px;border-color:rgba(255,255,255,0.4) transparent transparent transparent;border-top-style:solid;margin-top:-2.5px;top:22.5px}.top-bar-section .has-dropdown.moved{position:relative}.top-bar-section .has-dropdown.moved>.dropdown{display:block;position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}.top-bar-section .has-dropdown.hover>.dropdown,.top-bar-section .has-dropdown.not-click:hover>.dropdown{display:block;position:static !important;height:auto;width:auto;overflow:visible;clip:auto;position:absolute !important}.top-bar-section .has-dropdown>a:focus+.dropdown{display:block;position:static !important;height:auto;width:auto;overflow:visible;clip:auto;position:absolute !important}.top-bar-section .has-dropdown .dropdown li.has-dropdown>a:after{border:none;content:"\00bb";top:1rem;margin-top:-1px;right:5px;line-height:1.2}.top-bar-section .dropdown{left:0;top:auto;background:transparent;min-width:100%}.top-bar-section .dropdown li a{color:#fff;line-height:45px;white-space:nowrap;padding:12px 15px;background:#333}.top-bar-section .dropdown li:not(.has-form):not(.active)>a:not(.button){color:#fff;background:#333}.top-bar-section .dropdown li:not(.has-form):not(.active):hover>a:not(.button){color:#fff;background-color:#555;background:#272727}.top-bar-section .dropdown li label{white-space:nowrap;background:#333}.top-bar-section .dropdown li .dropdown{left:100%;top:0}.top-bar-section>ul>.divider,.top-bar-section>ul>[role="separator"]{border-bottom:none;border-top:none;border-right:solid 1px #4e4e4e;clear:none;height:45px;width:0}.top-bar-section .has-form{background:#333;padding:0 15px;height:45px}.top-bar-section .right li .dropdown{left:auto;right:0}.top-bar-section .right li .dropdown li .dropdown{right:100%}.top-bar-section .left li .dropdown{right:auto;left:0}.top-bar-section .left li .dropdown li .dropdown{left:100%}.no-js .top-bar-section ul li:hover>a{background-color:#555;background:#272727;color:#fff}.no-js .top-bar-section ul li:active>a{background:#008cba;color:#fff}.no-js .top-bar-section .has-dropdown:hover>.dropdown{display:block;position:static !important;height:auto;width:auto;overflow:visible;clip:auto;position:absolute !important}.no-js .top-bar-section .has-dropdown>a:focus+.dropdown{display:block;position:static !important;height:auto;width:auto;overflow:visible;clip:auto;position:absolute !important}}.breadcrumbs{display:block;padding:0.5625rem 0.875rem 0.5625rem;overflow:hidden;margin-left:0;list-style:none;border-style:solid;border-width:1px;background-color:#f4f4f4;border-color:#dcdcdc;border-radius:3px}.breadcrumbs>*{margin:0;float:left;font-size:0.6875rem;line-height:0.6875rem;text-transform:uppercase;color:#008cba}.breadcrumbs>*:hover a,.breadcrumbs>*:focus a{text-decoration:underline}.breadcrumbs>* a{color:#008cba}.breadcrumbs>*.current{cursor:default;color:#333}.breadcrumbs>*.current a{cursor:default;color:#333}.breadcrumbs>*.current:hover,.breadcrumbs>*.current:hover a,.breadcrumbs>*.current:focus,.breadcrumbs>*.current:focus a{text-decoration:none}.breadcrumbs>*.unavailable{color:#999}.breadcrumbs>*.unavailable a{color:#999}.breadcrumbs>*.unavailable:hover,.breadcrumbs>*.unavailable:hover a,.breadcrumbs>*.unavailable:focus,.breadcrumbs>*.unavailable a:focus{text-decoration:none;color:#999;cursor:default}.breadcrumbs>*:before{content:"/";color:#aaa;margin:0 0.75rem;position:relative;top:1px}.breadcrumbs>*:first-child:before{content:" ";margin:0}[aria-label="breadcrumbs"] [aria-hidden="true"]:after{content:"/"}.alert-box{border-style:solid;border-width:1px;display:block;font-weight:normal;margin-bottom:1.25rem;position:relative;padding:0.875rem 1.5rem 0.875rem 0.875rem;font-size:0.8125rem;transition:opacity 300ms ease-out;background-color:#008cba;border-color:#0078a0;color:#fff}.alert-box .close{font-size:1.375rem;padding:9px 6px 4px;line-height:0;position:absolute;top:50%;margin-top:-0.6875rem;right:0.25rem;color:#333;opacity:0.3;background:inherit}.alert-box .close:hover,.alert-box .close:focus{opacity:0.5}.alert-box.radius{border-radius:3px}.alert-box.round{border-radius:1000px}.alert-box.success{background-color:#43ac6a;border-color:#3a945b;color:#fff}.alert-box.alert{background-color:#f04124;border-color:#de2d0f;color:#fff}.alert-box.secondary{background-color:#e7e7e7;border-color:#c7c7c7;color:#4f4f4f}.alert-box.warning{background-color:#f08a24;border-color:#de770f;color:#fff}.alert-box.info{background-color:#a0d3e8;border-color:#74bfdd;color:#4f4f4f}.alert-box.alert-close{opacity:0}.inline-list{margin:0 auto 1.0625rem auto;margin-left:-1.375rem;margin-right:0;padding:0;list-style:none;overflow:hidden}.inline-list>li{list-style:none;float:left;margin-left:1.375rem;display:block}.inline-list>li>*{display:block}.button-group{list-style:none;margin:0;left:0}.button-group:before,.button-group:after{content:" ";display:table}.button-group:after{clear:both}.button-group>li{margin:0 -2px;display:inline-block}.button-group>li>button,.button-group>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group>li:first-child button,.button-group>li:first-child .button{border-left:0}.button-group.stack>li{margin:0 -2px;display:inline-block;display:block;margin:0;float:none}.button-group.stack>li>button,.button-group.stack>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack>li:first-child button,.button-group.stack>li:first-child .button{border-left:0}.button-group.stack>li>button,.button-group.stack>li .button{border-top:1px solid;border-color:rgba(255,255,255,0.5);border-left-width:0px;margin:0;display:block}.button-group.stack>li:first-child button,.button-group.stack>li:first-child .button{border-top:0}.button-group.stack-for-small>li{margin:0 -2px;display:inline-block}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-left:0}@media only screen and (max-width: 40em){.button-group.stack-for-small>li{margin:0 -2px;display:inline-block;display:block;margin:0}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-left:0}.button-group.stack-for-small>li>button,.button-group.stack-for-small>li .button{border-top:1px solid;border-color:rgba(255,255,255,0.5);border-left-width:0px;margin:0;display:block}.button-group.stack-for-small>li:first-child button,.button-group.stack-for-small>li:first-child .button{border-top:0}}.button-group.radius>*{margin:0 -2px;display:inline-block}.button-group.radius>*>button,.button-group.radius>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius>*:first-child button,.button-group.radius>*:first-child .button{border-left:0}.button-group.radius>*,.button-group.radius>*>a,.button-group.radius>*>button,.button-group.radius>*>.button{border-radius:0}.button-group.radius>*:first-child,.button-group.radius>*:first-child>a,.button-group.radius>*:first-child>button,.button-group.radius>*:first-child>.button{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius>*:last-child,.button-group.radius>*:last-child>a,.button-group.radius>*:last-child>button,.button-group.radius>*:last-child>.button{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.button-group.radius.stack>*{margin:0 -2px;display:inline-block;display:block;margin:0}.button-group.radius.stack>*>button,.button-group.radius.stack>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack>*:first-child button,.button-group.radius.stack>*:first-child .button{border-left:0}.button-group.radius.stack>*>button,.button-group.radius.stack>* .button{border-top:1px solid;border-color:rgba(255,255,255,0.5);border-left-width:0px;margin:0;display:block}.button-group.radius.stack>*:first-child button,.button-group.radius.stack>*:first-child .button{border-top:0}.button-group.radius.stack>*,.button-group.radius.stack>*>a,.button-group.radius.stack>*>button,.button-group.radius.stack>*>.button{border-radius:0}.button-group.radius.stack>*:first-child,.button-group.radius.stack>*:first-child>a,.button-group.radius.stack>*:first-child>button,.button-group.radius.stack>*:first-child>.button{-webkit-top-left-radius:3px;-webkit-top-right-radius:3px;border-top-left-radius:3px;border-top-right-radius:3px}.button-group.radius.stack>*:last-child,.button-group.radius.stack>*:last-child>a,.button-group.radius.stack>*:last-child>button,.button-group.radius.stack>*:last-child>.button{-webkit-bottom-left-radius:3px;-webkit-bottom-right-radius:3px;border-bottom-left-radius:3px;border-bottom-right-radius:3px}@media only screen and (min-width: 40.063em){.button-group.radius.stack-for-small>*{margin:0 -2px;display:inline-block}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-left:0}.button-group.radius.stack-for-small>*,.button-group.radius.stack-for-small>*>a,.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>*>.button{border-radius:0}.button-group.radius.stack-for-small>*:first-child,.button-group.radius.stack-for-small>*:first-child>a,.button-group.radius.stack-for-small>*:first-child>button,.button-group.radius.stack-for-small>*:first-child>.button{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius.stack-for-small>*:last-child,.button-group.radius.stack-for-small>*:last-child>a,.button-group.radius.stack-for-small>*:last-child>button,.button-group.radius.stack-for-small>*:last-child>.button{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}}@media only screen and (max-width: 40em){.button-group.radius.stack-for-small>*{margin:0 -2px;display:inline-block;display:block;margin:0}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-left:0}.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>* .button{border-top:1px solid;border-color:rgba(255,255,255,0.5);border-left-width:0px;margin:0;display:block}.button-group.radius.stack-for-small>*:first-child button,.button-group.radius.stack-for-small>*:first-child .button{border-top:0}.button-group.radius.stack-for-small>*,.button-group.radius.stack-for-small>*>a,.button-group.radius.stack-for-small>*>button,.button-group.radius.stack-for-small>*>.button{border-radius:0}.button-group.radius.stack-for-small>*:first-child,.button-group.radius.stack-for-small>*:first-child>a,.button-group.radius.stack-for-small>*:first-child>button,.button-group.radius.stack-for-small>*:first-child>.button{-webkit-top-left-radius:3px;-webkit-top-right-radius:3px;border-top-left-radius:3px;border-top-right-radius:3px}.button-group.radius.stack-for-small>*:last-child,.button-group.radius.stack-for-small>*:last-child>a,.button-group.radius.stack-for-small>*:last-child>button,.button-group.radius.stack-for-small>*:last-child>.button{-webkit-bottom-left-radius:3px;-webkit-bottom-right-radius:3px;border-bottom-left-radius:3px;border-bottom-right-radius:3px}}.button-group.round>*{margin:0 -2px;display:inline-block}.button-group.round>*>button,.button-group.round>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round>*:first-child button,.button-group.round>*:first-child .button{border-left:0}.button-group.round>*,.button-group.round>*>a,.button-group.round>*>button,.button-group.round>*>.button{border-radius:0}.button-group.round>*:first-child,.button-group.round>*:first-child>a,.button-group.round>*:first-child>button,.button-group.round>*:first-child>.button{-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round>*:last-child,.button-group.round>*:last-child>a,.button-group.round>*:last-child>button,.button-group.round>*:last-child>.button{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.button-group.round.stack>*{margin:0 -2px;display:inline-block;display:block;margin:0}.button-group.round.stack>*>button,.button-group.round.stack>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack>*:first-child button,.button-group.round.stack>*:first-child .button{border-left:0}.button-group.round.stack>*>button,.button-group.round.stack>* .button{border-top:1px solid;border-color:rgba(255,255,255,0.5);border-left-width:0px;margin:0;display:block}.button-group.round.stack>*:first-child button,.button-group.round.stack>*:first-child .button{border-top:0}.button-group.round.stack>*,.button-group.round.stack>*>a,.button-group.round.stack>*>button,.button-group.round.stack>*>.button{border-radius:0}.button-group.round.stack>*:first-child,.button-group.round.stack>*:first-child>a,.button-group.round.stack>*:first-child>button,.button-group.round.stack>*:first-child>.button{-webkit-top-left-radius:1rem;-webkit-top-right-radius:1rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.button-group.round.stack>*:last-child,.button-group.round.stack>*:last-child>a,.button-group.round.stack>*:last-child>button,.button-group.round.stack>*:last-child>.button{-webkit-bottom-left-radius:1rem;-webkit-bottom-right-radius:1rem;border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}@media only screen and (min-width: 40.063em){.button-group.round.stack-for-small>*{margin:0 -2px;display:inline-block}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-left:0}.button-group.round.stack-for-small>*,.button-group.round.stack-for-small>*>a,.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>*>.button{border-radius:0}.button-group.round.stack-for-small>*:first-child,.button-group.round.stack-for-small>*:first-child>a,.button-group.round.stack-for-small>*:first-child>button,.button-group.round.stack-for-small>*:first-child>.button{-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round.stack-for-small>*:last-child,.button-group.round.stack-for-small>*:last-child>a,.button-group.round.stack-for-small>*:last-child>button,.button-group.round.stack-for-small>*:last-child>.button{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}}@media only screen and (max-width: 40em){.button-group.round.stack-for-small>*{margin:0 -2px;display:inline-block;display:block;margin:0}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-left:0}.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>* .button{border-top:1px solid;border-color:rgba(255,255,255,0.5);border-left-width:0px;margin:0;display:block}.button-group.round.stack-for-small>*:first-child button,.button-group.round.stack-for-small>*:first-child .button{border-top:0}.button-group.round.stack-for-small>*,.button-group.round.stack-for-small>*>a,.button-group.round.stack-for-small>*>button,.button-group.round.stack-for-small>*>.button{border-radius:0}.button-group.round.stack-for-small>*:first-child,.button-group.round.stack-for-small>*:first-child>a,.button-group.round.stack-for-small>*:first-child>button,.button-group.round.stack-for-small>*:first-child>.button{-webkit-top-left-radius:1rem;-webkit-top-right-radius:1rem;border-top-left-radius:1rem;border-top-right-radius:1rem}.button-group.round.stack-for-small>*:last-child,.button-group.round.stack-for-small>*:last-child>a,.button-group.round.stack-for-small>*:last-child>button,.button-group.round.stack-for-small>*:last-child>.button{-webkit-bottom-left-radius:1rem;-webkit-bottom-right-radius:1rem;border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}}.button-group.even-2 li{margin:0 -2px;display:inline-block;width:50%}.button-group.even-2 li>button,.button-group.even-2 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-2 li:first-child button,.button-group.even-2 li:first-child .button{border-left:0}.button-group.even-2 li button,.button-group.even-2 li .button{width:100%}.button-group.even-3 li{margin:0 -2px;display:inline-block;width:33.33333%}.button-group.even-3 li>button,.button-group.even-3 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-3 li:first-child button,.button-group.even-3 li:first-child .button{border-left:0}.button-group.even-3 li button,.button-group.even-3 li .button{width:100%}.button-group.even-4 li{margin:0 -2px;display:inline-block;width:25%}.button-group.even-4 li>button,.button-group.even-4 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-4 li:first-child button,.button-group.even-4 li:first-child .button{border-left:0}.button-group.even-4 li button,.button-group.even-4 li .button{width:100%}.button-group.even-5 li{margin:0 -2px;display:inline-block;width:20%}.button-group.even-5 li>button,.button-group.even-5 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-5 li:first-child button,.button-group.even-5 li:first-child .button{border-left:0}.button-group.even-5 li button,.button-group.even-5 li .button{width:100%}.button-group.even-6 li{margin:0 -2px;display:inline-block;width:16.66667%}.button-group.even-6 li>button,.button-group.even-6 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-6 li:first-child button,.button-group.even-6 li:first-child .button{border-left:0}.button-group.even-6 li button,.button-group.even-6 li .button{width:100%}.button-group.even-7 li{margin:0 -2px;display:inline-block;width:14.28571%}.button-group.even-7 li>button,.button-group.even-7 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-7 li:first-child button,.button-group.even-7 li:first-child .button{border-left:0}.button-group.even-7 li button,.button-group.even-7 li .button{width:100%}.button-group.even-8 li{margin:0 -2px;display:inline-block;width:12.5%}.button-group.even-8 li>button,.button-group.even-8 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-8 li:first-child button,.button-group.even-8 li:first-child .button{border-left:0}.button-group.even-8 li button,.button-group.even-8 li .button{width:100%}.button-bar:before,.button-bar:after{content:" ";display:table}.button-bar:after{clear:both}.button-bar .button-group{float:left;margin-right:0.625rem}.button-bar .button-group div{overflow:hidden}.panel{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#f2f2f2;color:#333}.panel>:first-child{margin-top:0}.panel>:last-child{margin-bottom:0}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6,.panel p,.panel li,.panel dl{color:#333}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6{line-height:1;margin-bottom:0.625rem}.panel h1.subheader,.panel h2.subheader,.panel h3.subheader,.panel h4.subheader,.panel h5.subheader,.panel h6.subheader{line-height:1.4}.panel.callout{border-style:solid;border-width:1px;border-color:#b6edff;margin-bottom:1.25rem;padding:1.25rem;background:#ecfaff;color:#333}.panel.callout>:first-child{margin-top:0}.panel.callout>:last-child{margin-bottom:0}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6,.panel.callout p,.panel.callout li,.panel.callout dl{color:#333}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6{line-height:1;margin-bottom:0.625rem}.panel.callout h1.subheader,.panel.callout h2.subheader,.panel.callout h3.subheader,.panel.callout h4.subheader,.panel.callout h5.subheader,.panel.callout h6.subheader{line-height:1.4}.panel.callout a:not(.button){color:#008cba}.panel.radius{border-radius:3px}.dropdown.button,button.dropdown{position:relative;outline:none;padding-right:3.5625rem}.dropdown.button::after,button.dropdown::after{position:absolute;content:"";width:0;height:0;display:block;border-style:solid;border-color:#fff transparent transparent transparent;top:50%}.dropdown.button::after,button.dropdown::after{border-width:0.375rem;right:1.40625rem;margin-top:-0.15625rem}.dropdown.button::after,button.dropdown::after{border-color:#fff transparent transparent transparent}.dropdown.button.tiny,button.dropdown.tiny{padding-right:2.625rem}.dropdown.button.tiny:after,button.dropdown.tiny:after{border-width:0.375rem;right:1.125rem;margin-top:-0.125rem}.dropdown.button.tiny::after,button.dropdown.tiny::after{border-color:#fff transparent transparent transparent}.dropdown.button.small,button.dropdown.small{padding-right:3.0625rem}.dropdown.button.small::after,button.dropdown.small::after{border-width:0.4375rem;right:1.3125rem;margin-top:-0.15625rem}.dropdown.button.small::after,button.dropdown.small::after{border-color:#fff transparent transparent transparent}.dropdown.button.large,button.dropdown.large{padding-right:3.625rem}.dropdown.button.large::after,button.dropdown.large::after{border-width:0.3125rem;right:1.71875rem;margin-top:-0.15625rem}.dropdown.button.large::after,button.dropdown.large::after{border-color:#fff transparent transparent transparent}.dropdown.button.secondary:after,button.dropdown.secondary:after{border-color:#333 transparent transparent transparent}.th{line-height:0;display:inline-block;border:solid 4px #fff;max-width:100%;box-shadow:0 0 0 1px rgba(0,0,0,0.2);transition:all 200ms ease-out}.th:hover,.th:focus{box-shadow:0 0 6px 1px rgba(0,140,186,0.5)}.th.radius{border-radius:3px}.toolbar{background:#333;width:100%;font-size:0;display:inline-block}.toolbar.label-bottom .tab .tab-content i,.toolbar.label-bottom .tab .tab-content img{margin-bottom:10px}.toolbar.label-right .tab .tab-content i,.toolbar.label-right .tab .tab-content img{margin-right:10px;display:inline-block}.toolbar.label-right .tab .tab-content label{display:inline-block}.toolbar.vertical.label-right .tab .tab-content{text-align:left}.toolbar.vertical{height:100%;width:auto}.toolbar.vertical .tab{width:auto;margin:auto;float:none}.toolbar .tab{text-align:center;width:25%;margin:0 auto;display:block;padding:20px;float:left}.toolbar .tab:hover{background:rgba(255,255,255,0.1)}.toolbar .tab-content{font-size:16px;text-align:center}.toolbar .tab-content label{color:#ccc}.toolbar .tab-content i{font-size:30px;display:block;margin:0 auto;color:#ccc;vertical-align:middle}.toolbar .tab-content img{width:30px;height:30px;display:block;margin:0 auto}.pricing-table{border:solid 1px #ddd;margin-left:0;margin-bottom:1.25rem}.pricing-table *{list-style:none;line-height:1}.pricing-table .title{background-color:#333;padding:0.9375rem 1.25rem;text-align:center;color:#eee;font-weight:normal;font-size:1rem;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif}.pricing-table .price{background-color:#f6f6f6;padding:0.9375rem 1.25rem;text-align:center;color:#333;font-weight:normal;font-size:2rem;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif}.pricing-table .description{background-color:#fff;padding:0.9375rem;text-align:center;color:#777;font-size:0.75rem;font-weight:normal;line-height:1.4;border-bottom:dotted 1px #ddd}.pricing-table .bullet-item{background-color:#fff;padding:0.9375rem;text-align:center;color:#333;font-size:0.875rem;font-weight:normal;border-bottom:dotted 1px #ddd}.pricing-table .cta-button{background-color:#fff;text-align:center;padding:1.25rem 1.25rem 0}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotate{from{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotate{from{-o-transform:rotate(0deg)}to{-o-transform:rotate(360deg)}}@keyframes rotate{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.slideshow-wrapper{position:relative}.slideshow-wrapper ul{list-style-type:none;margin:0}.slideshow-wrapper ul li,.slideshow-wrapper ul li .orbit-caption{display:none}.slideshow-wrapper ul li:first-child{display:block}.slideshow-wrapper .orbit-container{background-color:transparent}.slideshow-wrapper .orbit-container li{display:block}.slideshow-wrapper .orbit-container li .orbit-caption{display:block}.slideshow-wrapper .orbit-container .orbit-bullets li{display:inline-block}.slideshow-wrapper .preloader{display:block;width:40px;height:40px;position:absolute;top:50%;left:50%;margin-top:-20px;margin-left:-20px;border:solid 3px;border-color:#555 #fff;border-radius:1000px;animation-name:rotate;animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:linear}.orbit-container{overflow:hidden;width:100%;position:relative;background:none}.orbit-container .orbit-slides-container{list-style:none;margin:0;padding:0;position:relative;-webkit-transform:translateZ(0)}.orbit-container .orbit-slides-container img{display:block;max-width:100%}.orbit-container .orbit-slides-container>*{position:absolute;top:0;width:100%;margin-left:100%}.orbit-container .orbit-slides-container>*:first-child{margin-left:0%}.orbit-container .orbit-slides-container>* .orbit-caption{position:absolute;bottom:0;background-color:rgba(51,51,51,0.8);color:#fff;width:100%;padding:0.625rem 0.875rem;font-size:0.875rem}.orbit-container .orbit-slide-number{position:absolute;top:10px;left:10px;font-size:12px;color:#fff;background:rgba(0,0,0,0);z-index:10}.orbit-container .orbit-slide-number span{font-weight:700;padding:0.3125rem}.orbit-container .orbit-timer{position:absolute;top:12px;right:10px;height:6px;width:100px;z-index:10}.orbit-container .orbit-timer .orbit-progress{height:3px;background-color:rgba(255,255,255,0.3);display:block;width:0%;position:relative;right:20px;top:5px}.orbit-container .orbit-timer>span{display:none;position:absolute;top:0px;right:0;width:11px;height:14px;border:solid 4px #fff;border-top:none;border-bottom:none}.orbit-container .orbit-timer.paused>span{right:-4px;top:0px;width:11px;height:14px;border:inset 8px;border-left-style:solid;border-color:transparent;border-left-color:#fff}.orbit-container .orbit-timer.paused>span.dark{border-left-color:#333}.orbit-container:hover .orbit-timer>span{display:block}.orbit-container .orbit-prev,.orbit-container .orbit-next{position:absolute;top:45%;margin-top:-25px;width:36px;height:60px;line-height:50px;color:white;background-color:transparent;text-indent:-9999px !important;z-index:10}.orbit-container .orbit-prev:hover,.orbit-container .orbit-next:hover{background-color:rgba(0,0,0,0.3)}.orbit-container .orbit-prev>span,.orbit-container .orbit-next>span{position:absolute;top:50%;margin-top:-10px;display:block;width:0;height:0;border:inset 10px}.orbit-container .orbit-prev{left:0}.orbit-container .orbit-prev>span{border-right-style:solid;border-color:transparent;border-right-color:#fff}.orbit-container .orbit-prev:hover>span{border-right-color:#fff}.orbit-container .orbit-next{right:0}.orbit-container .orbit-next>span{border-color:transparent;border-left-style:solid;border-left-color:#fff;left:50%;margin-left:-4px}.orbit-container .orbit-next:hover>span{border-left-color:#fff}.orbit-bullets-container{text-align:center}.orbit-bullets{margin:0 auto 30px auto;overflow:hidden;position:relative;top:10px;float:none;text-align:center;display:block}.orbit-bullets li{cursor:pointer;display:inline-block;width:0.5625rem;height:0.5625rem;background:#ccc;float:none;margin-right:6px;border-radius:1000px}.orbit-bullets li.active{background:#999}.orbit-bullets li:last-child{margin-right:0}.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:none}.touch .orbit-bullets{display:none}@media only screen and (min-width: 40.063em){.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:inherit}.touch .orbit-bullets{display:block}}@media only screen and (max-width: 40em){.orbit-stack-on-small .orbit-slides-container{height:auto !important}.orbit-stack-on-small .orbit-slides-container>*{position:relative;margin:0% !important;opacity:1 !important}.orbit-stack-on-small .orbit-slide-number{display:none}.orbit-timer{display:none}.orbit-next,.orbit-prev{display:none}.orbit-bullets{display:none}}[data-magellan-expedition],[data-magellan-expedition-clone]{background:#fff;z-index:50;min-width:100%;padding:10px}[data-magellan-expedition] .sub-nav,[data-magellan-expedition-clone] .sub-nav{margin-bottom:0}[data-magellan-expedition] .sub-nav dd,[data-magellan-expedition-clone] .sub-nav dd{margin-bottom:0}[data-magellan-expedition] .sub-nav a,[data-magellan-expedition-clone] .sub-nav a{line-height:1.8em}.icon-bar{width:100%;font-size:0;display:inline-block;background:#333}.icon-bar>*{text-align:center;font-size:1rem;width:25%;margin:0 auto;display:block;padding:1.25rem;float:left}.icon-bar>* i,.icon-bar>* img{display:block;margin:0 auto}.icon-bar>* i+label,.icon-bar>* img+label{margin-top:.0625rem}.icon-bar>* i{font-size:1.875rem;vertical-align:middle}.icon-bar>* img{width:1.875rem;height:1.875rem}.icon-bar.label-right>* i,.icon-bar.label-right>* img{margin:0 .0625rem 0 0;display:inline-block}.icon-bar.label-right>* i+label,.icon-bar.label-right>* img+label{margin-top:0}.icon-bar.label-right>* label{display:inline-block}.icon-bar.vertical.label-right>*{text-align:left}.icon-bar.vertical,.icon-bar.small-vertical{height:100%;width:auto}.icon-bar.vertical .item,.icon-bar.small-vertical .item{width:auto;margin:auto;float:none}@media only screen and (min-width: 40.063em){.icon-bar.medium-vertical{height:100%;width:auto}.icon-bar.medium-vertical .item{width:auto;margin:auto;float:none}}@media only screen and (min-width: 64.063em){.icon-bar.large-vertical{height:100%;width:auto}.icon-bar.large-vertical .item{width:auto;margin:auto;float:none}}.icon-bar>*{font-size:1rem;padding:1.25rem}.icon-bar>* i+label,.icon-bar>* img+label{margin-top:.0625rem}.icon-bar>* i{font-size:1.875rem}.icon-bar>* img{width:1.875rem;height:1.875rem}.icon-bar>*:hover{background:#008cba}.icon-bar>* label{color:#fff}.icon-bar>* i{color:#fff}.icon-bar.two-up .item{width:50%}.icon-bar.two-up.vertical .item,.icon-bar.two-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.063em){.icon-bar.two-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.063em){.icon-bar.two-up.large-vertical .item{width:auto}}.icon-bar.three-up .item{width:33.3333%}.icon-bar.three-up.vertical .item,.icon-bar.three-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.063em){.icon-bar.three-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.063em){.icon-bar.three-up.large-vertical .item{width:auto}}.icon-bar.four-up .item{width:25%}.icon-bar.four-up.vertical .item,.icon-bar.four-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.063em){.icon-bar.four-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.063em){.icon-bar.four-up.large-vertical .item{width:auto}}.icon-bar.five-up .item{width:20%}.icon-bar.five-up.vertical .item,.icon-bar.five-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.063em){.icon-bar.five-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.063em){.icon-bar.five-up.large-vertical .item{width:auto}}.icon-bar.six-up .item{width:16.66667%}.icon-bar.six-up.vertical .item,.icon-bar.six-up.small-vertical .item{width:auto}@media only screen and (min-width: 40.063em){.icon-bar.six-up.medium-vertical .item{width:auto}}@media only screen and (min-width: 64.063em){.icon-bar.six-up.large-vertical .item{width:auto}}.tabs{margin-bottom:0 !important;margin-left:0}.tabs:before,.tabs:after{content:" ";display:table}.tabs:after{clear:both}.tabs dd,.tabs .tab-title{position:relative;margin-bottom:0 !important;list-style:none;float:left}.tabs dd>a,.tabs .tab-title>a{outline:none;display:block;background-color:#efefef;color:#222;padding:1rem 2rem;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem}.tabs dd>a:hover,.tabs .tab-title>a:hover{background-color:#e1e1e1}.tabs dd.active a,.tabs .tab-title.active a{background-color:#fff;color:#222}.tabs.radius dd:first-child a,.tabs.radius .tab:first-child a{-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.tabs.radius dd:last-child a,.tabs.radius .tab:last-child a{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.tabs.vertical dd,.tabs.vertical .tab-title{position:inherit;float:none;display:block;top:auto}.tabs-content{margin-bottom:1.5rem;width:100%}.tabs-content:before,.tabs-content:after{content:" ";display:table}.tabs-content:after{clear:both}.tabs-content>.content{display:none;float:left;padding:0.9375rem 0;width:100%}.tabs-content>.content.active{display:block;float:none}.tabs-content>.content.contained{padding:0.9375rem}.tabs-content.vertical{display:block}.tabs-content.vertical>.content{padding:0 0.9375rem}@media only screen and (min-width: 40.063em){.tabs.vertical{width:20%;max-width:20%;float:left;margin:0 0 1.25rem}.tabs-content.vertical{width:80%;max-width:80%;float:left;margin-left:-1px;padding-left:1rem}}.no-js .tabs-content>.content{display:block;float:none}ul.pagination{display:block;min-height:1.5rem;margin-left:-0.3125rem}ul.pagination li{height:1.5rem;color:#222;font-size:0.875rem;margin-left:0.3125rem}ul.pagination li a,ul.pagination li button{display:block;padding:0.0625rem 0.625rem 0.0625rem;color:#999;background:none;border-radius:3px;font-weight:normal;font-size:1em;line-height:inherit;transition:background-color 300ms ease-out}ul.pagination li:hover a,ul.pagination li a:focus,ul.pagination li:hover button,ul.pagination li button:focus{background:#e6e6e6}ul.pagination li.unavailable a,ul.pagination li.unavailable button{cursor:default;color:#999}ul.pagination li.unavailable:hover a,ul.pagination li.unavailable a:focus,ul.pagination li.unavailable:hover button,ul.pagination li.unavailable button:focus{background:transparent}ul.pagination li.current a,ul.pagination li.current button{background:#008cba;color:#fff;font-weight:bold;cursor:default}ul.pagination li.current a:hover,ul.pagination li.current a:focus,ul.pagination li.current button:hover,ul.pagination li.current button:focus{background:#008cba}ul.pagination li{float:left;display:block}.pagination-centered{text-align:center}.pagination-centered ul.pagination li{float:none;display:inline-block}.side-nav{display:block;margin:0;padding:0.875rem 0;list-style-type:none;list-style-position:outside;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif}.side-nav li{margin:0 0 0.4375rem 0;font-size:0.875rem;font-weight:normal}.side-nav li a:not(.button){display:block;color:#008cba;margin:0;padding:0.4375rem 0.875rem}.side-nav li a:not(.button):hover,.side-nav li a:not(.button):focus{background:rgba(0,0,0,0.025);color:#1cc7ff}.side-nav li.active>a:first-child:not(.button){color:#1cc7ff;font-weight:normal;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif}.side-nav li.divider{border-top:1px solid;height:0;padding:0;list-style:none;border-top-color:#fff}.side-nav li.heading{color:#008cba;font-size:0.875rem;font-weight:bold;text-transform:uppercase}.accordion{margin-bottom:0}.accordion:before,.accordion:after{content:" ";display:table}.accordion:after{clear:both}.accordion .accordion-navigation,.accordion dd{display:block;margin-bottom:0 !important}.accordion .accordion-navigation.active>a,.accordion dd.active>a{background:#e8e8e8}.accordion .accordion-navigation>a,.accordion dd>a{background:#efefef;color:#222;padding:1rem;display:block;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-size:1rem}.accordion .accordion-navigation>a:hover,.accordion dd>a:hover{background:#e3e3e3}.accordion .accordion-navigation>.content,.accordion dd>.content{display:none;padding:0.9375rem}.accordion .accordion-navigation>.content.active,.accordion dd>.content.active{display:block;background:#fff}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}.text-justify{text-align:justify !important}@media only screen and (max-width: 40em){.small-only-text-left{text-align:left !important}.small-only-text-right{text-align:right !important}.small-only-text-center{text-align:center !important}.small-only-text-justify{text-align:justify !important}}@media only screen{.small-text-left{text-align:left !important}.small-text-right{text-align:right !important}.small-text-center{text-align:center !important}.small-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.063em) and (max-width: 64em){.medium-only-text-left{text-align:left !important}.medium-only-text-right{text-align:right !important}.medium-only-text-center{text-align:center !important}.medium-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.063em){.medium-text-left{text-align:left !important}.medium-text-right{text-align:right !important}.medium-text-center{text-align:center !important}.medium-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.063em) and (max-width: 90em){.large-only-text-left{text-align:left !important}.large-only-text-right{text-align:right !important}.large-only-text-center{text-align:center !important}.large-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.063em){.large-text-left{text-align:left !important}.large-text-right{text-align:right !important}.large-text-center{text-align:center !important}.large-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.063em) and (max-width: 120em){.xlarge-only-text-left{text-align:left !important}.xlarge-only-text-right{text-align:right !important}.xlarge-only-text-center{text-align:center !important}.xlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.063em){.xlarge-text-left{text-align:left !important}.xlarge-text-right{text-align:right !important}.xlarge-text-center{text-align:center !important}.xlarge-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.063em) and (max-width: 99999999em){.xxlarge-only-text-left{text-align:left !important}.xxlarge-only-text-right{text-align:right !important}.xxlarge-only-text-center{text-align:center !important}.xxlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.063em){.xxlarge-text-left{text-align:left !important}.xxlarge-text-right{text-align:right !important}.xxlarge-text-center{text-align:center !important}.xxlarge-text-justify{text-align:justify !important}}div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}a{color:#008cba;text-decoration:none;line-height:inherit}a:hover,a:focus{color:#0078a0}a img{border:none}p{font-family:inherit;font-weight:normal;font-size:1rem;line-height:1.6;margin-bottom:1.25rem;text-rendering:optimizeLegibility}p.lead{font-size:1.21875rem;line-height:1.6}p aside{font-size:0.875rem;line-height:1.35;font-style:italic}h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;font-style:normal;color:#222;text-rendering:optimizeLegibility;margin-top:0.2rem;margin-bottom:0.5rem;line-height:1.4}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:60%;color:#6f6f6f;line-height:0}h1{font-size:2.125rem}h2{font-size:1.6875rem}h3{font-size:1.375rem}h4{font-size:1.125rem}h5{font-size:1.125rem}h6{font-size:1rem}.subheader{line-height:1.4;color:#6f6f6f;font-weight:normal;margin-top:0.2rem;margin-bottom:0.5rem}hr{border:solid #ddd;border-width:1px 0 0;clear:both;margin:1.25rem 0 1.1875rem;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:Consolas,"Liberation Mono",Courier,monospace;font-weight:normal;color:#333;background-color:#f8f8f8;border-width:1px;border-style:solid;border-color:#dfdfdf;padding:0.125rem 0.3125rem 0.0625rem}ul,ol,dl{font-size:1rem;line-height:1.6;margin-bottom:1.25rem;list-style-position:outside;font-family:inherit}ul{margin-left:1.1rem}ul.no-bullet{margin-left:0}ul.no-bullet li ul,ul.no-bullet li ol{margin-left:1.25rem;margin-bottom:0;list-style:none}ul li ul,ul li ol{margin-left:1.25rem;margin-bottom:0}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square;margin-left:1.1rem}ul.circle{list-style-type:circle;margin-left:1.1rem}ul.disc{list-style-type:disc;margin-left:1.1rem}ul.no-bullet{list-style:none}ol{margin-left:1.4rem}ol li ul,ol li ol{margin-left:1.25rem;margin-bottom:0}dl dt{margin-bottom:0.3rem;font-weight:bold}dl dd{margin-bottom:0.75rem}abbr,acronym{text-transform:uppercase;font-size:90%;color:#222;cursor:help}abbr{text-transform:none}abbr[title]{border-bottom:1px dotted #ddd}blockquote{margin:0 0 1.25rem;padding:0.5625rem 1.25rem 0 1.1875rem;border-left:1px solid #ddd}blockquote cite{display:block;font-size:0.8125rem;color:#555}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:#555}blockquote,blockquote p{line-height:1.6;color:#6f6f6f}.vcard{display:inline-block;margin:0 0 1.25rem 0;border:1px solid #ddd;padding:0.625rem 0.75rem}.vcard li{margin:0;display:block}.vcard .fn{font-weight:bold;font-size:0.9375rem}.vevent .summary{font-weight:bold}.vevent abbr{cursor:default;text-decoration:none;font-weight:bold;border:none;padding:0 0.0625rem}@media only screen and (min-width: 40.063em){h1,h2,h3,h4,h5,h6{line-height:1.4}h1{font-size:2.75rem}h2{font-size:2.3125rem}h3{font-size:1.6875rem}h4{font-size:1.4375rem}h5{font-size:1.125rem}h6{font-size:1rem}}.print-only{display:none !important}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.hide-on-print{display:none !important}.print-only{display:block !important}.hide-for-print{display:none !important}.show-for-print{display:inherit !important}}.split.button{position:relative;padding-right:5.0625rem}.split.button span{display:block;height:100%;position:absolute;right:0;top:0;border-left:solid 1px}.split.button span:after{position:absolute;content:"";width:0;height:0;display:block;border-style:inset;top:50%;left:50%}.split.button span:active{background-color:rgba(0,0,0,0.1)}.split.button span{border-left-color:rgba(255,255,255,0.5)}.split.button span{width:3.09375rem}.split.button span:after{border-top-style:solid;border-width:0.375rem;top:48%;margin-left:-0.375rem}.split.button span:after{border-color:#fff transparent transparent transparent}.split.button.secondary span{border-left-color:rgba(255,255,255,0.5)}.split.button.secondary span:after{border-color:#fff transparent transparent transparent}.split.button.alert span{border-left-color:rgba(255,255,255,0.5)}.split.button.success span{border-left-color:rgba(255,255,255,0.5)}.split.button.tiny{padding-right:3.75rem}.split.button.tiny span{width:2.25rem}.split.button.tiny span:after{border-top-style:solid;border-width:0.375rem;top:48%;margin-left:-0.375rem}.split.button.small{padding-right:4.375rem}.split.button.small span{width:2.625rem}.split.button.small span:after{border-top-style:solid;border-width:0.4375rem;top:48%;margin-left:-0.375rem}.split.button.large{padding-right:5.5rem}.split.button.large span{width:3.4375rem}.split.button.large span:after{border-top-style:solid;border-width:0.3125rem;top:48%;margin-left:-0.375rem}.split.button.expand{padding-left:2rem}.split.button.secondary span:after{border-color:#333 transparent transparent transparent}.split.button.radius span{-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.split.button.round span{-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.reveal-modal-bg{position:fixed;top:0;bottom:0;left:0;right:0;background:#000;background:rgba(0,0,0,0.45);z-index:1004;display:none;left:0}.reveal-modal,dialog{visibility:hidden;display:none;position:absolute;z-index:1005;width:100vw;top:0;border-radius:3px;left:0;background-color:#fff;padding:1.25rem;border:solid 1px #666;box-shadow:0 0 10px rgba(0,0,0,0.4);padding:1.875rem}@media only screen and (max-width: 40em){.reveal-modal,dialog{min-height:100vh}}.reveal-modal .column,dialog .column,.reveal-modal .columns,dialog .columns{min-width:0}.reveal-modal>:first-child,dialog>:first-child{margin-top:0}.reveal-modal>:last-child,dialog>:last-child{margin-bottom:0}@media only screen and (min-width: 40.063em){.reveal-modal,dialog{width:80%;max-width:62.5rem;left:0;right:0;margin:0 auto}}@media only screen and (min-width: 40.063em){.reveal-modal,dialog{top:6.25rem}}.reveal-modal.radius,dialog.radius{border-radius:3px}.reveal-modal.round,dialog.round{border-radius:1000px}.reveal-modal.collapse,dialog.collapse{padding:0}@media only screen and (min-width: 40.063em){.reveal-modal.tiny,dialog.tiny{width:30%;max-width:62.5rem;left:0;right:0;margin:0 auto}}@media only screen and (min-width: 40.063em){.reveal-modal.small,dialog.small{width:40%;max-width:62.5rem;left:0;right:0;margin:0 auto}}@media only screen and (min-width: 40.063em){.reveal-modal.medium,dialog.medium{width:60%;max-width:62.5rem;left:0;right:0;margin:0 auto}}@media only screen and (min-width: 40.063em){.reveal-modal.large,dialog.large{width:70%;max-width:62.5rem;left:0;right:0;margin:0 auto}}@media only screen and (min-width: 40.063em){.reveal-modal.xlarge,dialog.xlarge{width:95%;max-width:62.5rem;left:0;right:0;margin:0 auto}}.reveal-modal.full,dialog.full{top:0;left:0;height:100%;height:100vh;min-height:100vh;max-width:none !important;margin-left:0 !important}@media only screen and (min-width: 40.063em){.reveal-modal.full,dialog.full{width:100vw;max-width:62.5rem;left:0;right:0;margin:0 auto}}.reveal-modal .close-reveal-modal,dialog .close-reveal-modal{font-size:2.5rem;line-height:1;position:absolute;top:0.5rem;right:0.6875rem;color:#aaa;font-weight:bold;cursor:pointer}dialog{display:none}dialog::backdrop,dialog+.backdrop{position:fixed;top:0;bottom:0;left:0;right:0;background:#000;background:rgba(0,0,0,0.45);z-index:auto;display:none;left:0}dialog[open]{display:block}@media print{dialog,.reveal-modal,dialog{display:none;background:#fff !important}}.has-tip{border-bottom:dotted 1px #ccc;cursor:help;font-weight:bold;color:#333}.has-tip:hover,.has-tip:focus{border-bottom:dotted 1px #003f54;color:#008cba}.has-tip.tip-left,.has-tip.tip-right{float:none !important}.tooltip{display:none;position:absolute;z-index:1006;font-weight:normal;font-size:0.875rem;line-height:1.3;padding:0.75rem;max-width:300px;left:50%;width:100%;color:#fff;background:#333}.tooltip>.nub{display:block;left:5px;position:absolute;width:0;height:0;border:solid 5px;border-color:transparent transparent #333 transparent;top:-10px;pointer-events:none}.tooltip>.nub.rtl{left:auto;right:5px}.tooltip.radius{border-radius:3px}.tooltip.round{border-radius:1000px}.tooltip.round>.nub{left:2rem}.tooltip.opened{color:#008cba !important;border-bottom:dotted 1px #003f54 !important}.tap-to-close{display:block;font-size:0.625rem;color:#777;font-weight:normal}@media only screen and (min-width: 40.063em){.tooltip>.nub{border-color:transparent transparent #333 transparent;top:-10px}.tooltip.tip-top>.nub{border-color:#333 transparent transparent transparent;top:auto;bottom:-10px}.tooltip.tip-left,.tooltip.tip-right{float:none !important}.tooltip.tip-left>.nub{border-color:transparent transparent transparent #333;right:-10px;left:auto;top:50%;margin-top:-5px}.tooltip.tip-right>.nub{border-color:transparent #333 transparent transparent;right:auto;left:-10px;top:50%;margin-top:-5px}}.clearing-thumbs,[data-clearing]{margin-bottom:0;margin-left:0;list-style:none}.clearing-thumbs:before,.clearing-thumbs:after,[data-clearing]:before,[data-clearing]:after{content:" ";display:table}.clearing-thumbs:after,[data-clearing]:after{clear:both}.clearing-thumbs li,[data-clearing] li{float:left;margin-right:10px}.clearing-thumbs[class*="block-grid-"] li,[data-clearing][class*="block-grid-"] li{margin-right:0}.clearing-blackout{background:#333;position:fixed;width:100%;height:100%;top:0;left:0;z-index:998}.clearing-blackout .clearing-close{display:block}.clearing-container{position:relative;z-index:998;height:100%;overflow:hidden;margin:0}.clearing-touch-label{position:absolute;top:50%;left:50%;color:#aaa;font-size:0.6em}.visible-img{height:95%;position:relative}.visible-img img{position:absolute;left:50%;top:50%;margin-left:-50%;max-height:100%;max-width:100%}.clearing-caption{color:#ccc;font-size:0.875em;line-height:1.3;margin-bottom:0;text-align:center;bottom:0;background:#333;width:100%;padding:10px 30px 20px;position:absolute;left:0}.clearing-close{z-index:999;padding-left:20px;padding-top:10px;font-size:30px;line-height:1;color:#ccc;display:none}.clearing-close:hover,.clearing-close:focus{color:#ccc}.clearing-assembled .clearing-container{height:100%}.clearing-assembled .clearing-container .carousel>ul{display:none}.clearing-feature li{display:none}.clearing-feature li.clearing-featured-img{display:block}@media only screen and (min-width: 40.063em){.clearing-main-prev,.clearing-main-next{position:absolute;height:100%;width:40px;top:0}.clearing-main-prev>span,.clearing-main-next>span{position:absolute;top:50%;display:block;width:0;height:0;border:solid 12px}.clearing-main-prev>span:hover,.clearing-main-next>span:hover{opacity:0.8}.clearing-main-prev{left:0}.clearing-main-prev>span{left:5px;border-color:transparent;border-right-color:#ccc}.clearing-main-next{right:0}.clearing-main-next>span{border-color:transparent;border-left-color:#ccc}.clearing-main-prev.disabled,.clearing-main-next.disabled{opacity:0.3}.clearing-assembled .clearing-container .carousel{background:rgba(51,51,51,0.8);height:120px;margin-top:10px;text-align:center}.clearing-assembled .clearing-container .carousel>ul{display:inline-block;z-index:999;height:100%;position:relative;float:none}.clearing-assembled .clearing-container .carousel>ul li{display:block;width:120px;min-height:inherit;float:left;overflow:hidden;margin-right:0;padding:0;position:relative;cursor:pointer;opacity:0.4;clear:none}.clearing-assembled .clearing-container .carousel>ul li.fix-height img{height:100%;max-width:none}.clearing-assembled .clearing-container .carousel>ul li a.th{border:none;box-shadow:none;display:block}.clearing-assembled .clearing-container .carousel>ul li img{cursor:pointer !important;width:100% !important}.clearing-assembled .clearing-container .carousel>ul li.visible{opacity:1}.clearing-assembled .clearing-container .carousel>ul li:hover{opacity:0.8}.clearing-assembled .clearing-container .visible-img{background:#333;overflow:hidden;height:85%}.clearing-close{position:absolute;top:10px;right:20px;padding-left:0;padding-top:0}}.progress{background-color:#f6f6f6;height:1.5625rem;border:1px solid #fff;padding:0.125rem;margin-bottom:0.625rem}.progress .meter{background:#008cba;height:100%;display:block}.progress.secondary .meter{background:#e7e7e7;height:100%;display:block}.progress.success .meter{background:#43ac6a;height:100%;display:block}.progress.alert .meter{background:#f04124;height:100%;display:block}.progress.radius{border-radius:3px}.progress.radius .meter{border-radius:2px}.progress.round{border-radius:1000px}.progress.round .meter{border-radius:999px}.sub-nav{display:block;width:auto;overflow:hidden;margin-bottom:-0.25rem 0 1.125rem;padding-top:0.25rem}.sub-nav dt{text-transform:uppercase}.sub-nav dt,.sub-nav dd,.sub-nav li{float:left;display:inline;margin-left:1rem;margin-bottom:0;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;font-weight:normal;font-size:0.875rem;color:#999}.sub-nav dt a,.sub-nav dd a,.sub-nav li a{text-decoration:none;color:#999;padding:0.1875rem 1rem}.sub-nav dt a:hover,.sub-nav dd a:hover,.sub-nav li a:hover{color:#737373}.sub-nav dt.active a,.sub-nav dd.active a,.sub-nav li.active a{border-radius:3px;font-weight:normal;background:#008cba;padding:0.1875rem 1rem;cursor:default;color:#fff}.sub-nav dt.active a:hover,.sub-nav dd.active a:hover,.sub-nav li.active a:hover{background:#0078a0}.joyride-list{display:none}.joyride-tip-guide{display:none;position:absolute;background:#333;color:#fff;z-index:101;top:0;left:2.5%;font-family:inherit;font-weight:normal;width:95%}.lt-ie9 .joyride-tip-guide{max-width:800px;left:50%;margin-left:-400px}.joyride-content-wrapper{width:100%;padding:1.125rem 1.25rem 1.5rem}.joyride-content-wrapper .button{margin-bottom:0 !important}.joyride-content-wrapper .joyride-prev-tip{margin-right:10px}.joyride-tip-guide .joyride-nub{display:block;position:absolute;left:22px;width:0;height:0;border:10px solid #333}.joyride-tip-guide .joyride-nub.top{border-top-style:solid;border-color:#333;border-top-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;top:-20px}.joyride-tip-guide .joyride-nub.bottom{border-bottom-style:solid;border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{right:-20px}.joyride-tip-guide .joyride-nub.left{left:-20px}.joyride-tip-guide h1,.joyride-tip-guide h2,.joyride-tip-guide h3,.joyride-tip-guide h4,.joyride-tip-guide h5,.joyride-tip-guide h6{line-height:1.25;margin:0;font-weight:bold;color:#fff}.joyride-tip-guide p{margin:0 0 1.125rem 0;font-size:0.875rem;line-height:1.3}.joyride-timer-indicator-wrap{width:50px;height:3px;border:solid 1px #555;position:absolute;right:1.0625rem;bottom:1rem}.joyride-timer-indicator{display:block;width:0;height:inherit;background:#666}.joyride-close-tip{position:absolute;right:12px;top:10px;color:#777 !important;text-decoration:none;font-size:24px;font-weight:normal;line-height:.5 !important}.joyride-close-tip:hover,.joyride-close-tip:focus{color:#eee !important}.joyride-modal-bg{position:fixed;height:100%;width:100%;background:transparent;background:rgba(0,0,0,0.5);z-index:100;display:none;top:0;left:0;cursor:pointer}.joyride-expose-wrapper{background-color:#fff;position:absolute;border-radius:3px;z-index:102;box-shadow:0 0 15px #fff}.joyride-expose-cover{background:transparent;border-radius:3px;position:absolute;z-index:9999;top:0;left:0}@media only screen and (min-width: 40.063em){.joyride-tip-guide{width:300px;left:inherit}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{border-color:#333 !important;border-top-color:transparent !important;border-right-color:transparent !important;border-bottom-color:transparent !important;top:22px;left:auto;right:-20px}.joyride-tip-guide .joyride-nub.left{border-color:#333 !important;border-top-color:transparent !important;border-left-color:transparent !important;border-bottom-color:transparent !important;top:22px;left:-20px;right:auto}}.label{font-weight:normal;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif;text-align:center;text-decoration:none;line-height:1;white-space:nowrap;display:inline-block;position:relative;margin-bottom:inherit;padding:0.25rem 0.5rem 0.25rem;font-size:0.6875rem;background-color:#008cba;color:#fff}.label.radius{border-radius:3px}.label.round{border-radius:1000px}.label.alert{background-color:#f04124;color:#fff}.label.warning{background-color:#f08a24;color:#fff}.label.success{background-color:#43ac6a;color:#fff}.label.secondary{background-color:#e7e7e7;color:#333}.label.info{background-color:#a0d3e8;color:#333}.off-canvas-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;overflow:hidden}.off-canvas-wrap.move-right,.off-canvas-wrap.move-left{min-height:100%;-webkit-overflow-scrolling:touch}.inner-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.inner-wrap:before,.inner-wrap:after{content:" ";display:table}.inner-wrap:after{clear:both}.tab-bar{-webkit-backface-visibility:hidden;background:#333;color:#fff;height:2.8125rem;line-height:2.8125rem;position:relative}.tab-bar h1,.tab-bar h2,.tab-bar h3,.tab-bar h4,.tab-bar h5,.tab-bar h6{color:#fff;font-weight:bold;line-height:2.8125rem;margin:0}.tab-bar h1,.tab-bar h2,.tab-bar h3,.tab-bar h4{font-size:1.125rem}.left-small{width:2.8125rem;height:2.8125rem;position:absolute;top:0;border-right:solid 1px #1a1a1a;left:0}.right-small{width:2.8125rem;height:2.8125rem;position:absolute;top:0;border-left:solid 1px #1a1a1a;right:0}.tab-bar-section{padding:0 0.625rem;position:absolute;text-align:center;height:2.8125rem;top:0}@media only screen and (min-width: 40.063em){.tab-bar-section.left,.tab-bar-section.right{text-align:left}}.tab-bar-section.left{left:0;right:2.8125rem}.tab-bar-section.right{left:2.8125rem;right:0}.tab-bar-section.middle{left:2.8125rem;right:2.8125rem}.tab-bar .menu-icon{text-indent:2.1875rem;width:2.8125rem;height:2.8125rem;display:block;padding:0;color:#fff;position:relative;transform:translate3d(0, 0, 0)}.tab-bar .menu-icon span::after{content:"";position:absolute;display:block;height:0;top:50%;margin-top:-0.5rem;left:0.90625rem;box-shadow:0 0px 0 1px #fff,0 7px 0 1px #fff,0 14px 0 1px #fff;width:1rem}.tab-bar .menu-icon span:hover:after{box-shadow:0 0px 0 1px #b3b3b3,0 7px 0 1px #b3b3b3,0 14px 0 1px #b3b3b3}.left-off-canvas-menu{-webkit-backface-visibility:hidden;width:15.625rem;top:0;bottom:0;position:absolute;overflow-x:hidden;overflow-y:auto;background:#333;z-index:1001;box-sizing:content-box;transition:transform 500ms ease 0s;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;-ms-transform:translate(-100.5%, 0);-webkit-transform:translate3d(-100.5%, 0, 0);-moz-transform:translate3d(-100.5%, 0, 0);-ms-transform:translate3d(-100.5%, 0, 0);-o-transform:translate3d(-100.5%, 0, 0);transform:translate3d(-100.5%, 0, 0);left:0}.left-off-canvas-menu *{-webkit-backface-visibility:hidden}.right-off-canvas-menu{-webkit-backface-visibility:hidden;width:15.625rem;top:0;bottom:0;position:absolute;overflow-x:hidden;overflow-y:auto;background:#333;z-index:1001;box-sizing:content-box;transition:transform 500ms ease 0s;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;-ms-transform:translate(100.5%, 0);-webkit-transform:translate3d(100.5%, 0, 0);-moz-transform:translate3d(100.5%, 0, 0);-ms-transform:translate3d(100.5%, 0, 0);-o-transform:translate3d(100.5%, 0, 0);transform:translate3d(100.5%, 0, 0);right:0}.right-off-canvas-menu *{-webkit-backface-visibility:hidden}ul.off-canvas-list{list-style-type:none;padding:0;margin:0}ul.off-canvas-list li label{display:block;padding:0.3rem 0.9375rem;color:#999;text-transform:uppercase;font-size:0.75rem;font-weight:bold;background:#444;border-top:1px solid #5e5e5e;border-bottom:none;margin:0}ul.off-canvas-list li a{display:block;padding:0.66667rem;color:rgba(255,255,255,0.7);border-bottom:1px solid #262626;transition:background 300ms ease}ul.off-canvas-list li a:hover{background:#242424}.move-right>.inner-wrap{-ms-transform:translate(15.625rem, 0);-webkit-transform:translate3d(15.625rem, 0, 0);-moz-transform:translate3d(15.625rem, 0, 0);-ms-transform:translate3d(15.625rem, 0, 0);-o-transform:translate3d(15.625rem, 0, 0);transform:translate3d(15.625rem, 0, 0)}.move-right .exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.move-right .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.move-left>.inner-wrap{-ms-transform:translate(-15.625rem, 0);-webkit-transform:translate3d(-15.625rem, 0, 0);-moz-transform:translate3d(-15.625rem, 0, 0);-ms-transform:translate3d(-15.625rem, 0, 0);-o-transform:translate3d(-15.625rem, 0, 0);transform:translate3d(-15.625rem, 0, 0)}.move-left .exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.move-left .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap .left-off-canvas-menu,.offcanvas-overlap .right-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap .exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.offcanvas-overlap .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap-left .right-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap-left .exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.offcanvas-overlap-left .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.offcanvas-overlap-right .left-off-canvas-menu{-ms-transform:none;-webkit-transform:none;-moz-transform:none;-o-transform:none;transform:none;z-index:1003}.offcanvas-overlap-right .exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.offcanvas-overlap-right .exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.no-csstransforms .left-off-canvas-menu{left:-15.625rem}.no-csstransforms .right-off-canvas-menu{right:-15.625rem}.no-csstransforms .move-left>.inner-wrap{right:15.625rem}.no-csstransforms .move-right>.inner-wrap{left:15.625rem}.left-submenu{-webkit-backface-visibility:hidden;width:15.625rem;top:0;bottom:0;position:absolute;margin:0;overflow-x:hidden;overflow-y:auto;background:#333;z-index:1002;box-sizing:content-box;-webkit-overflow-scrolling:touch;-ms-transform:translate(-100%, 0);-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.left-submenu *{-webkit-backface-visibility:hidden}.left-submenu .back>a{padding:0.3rem 0.9375rem;color:#999;text-transform:uppercase;font-weight:bold;background:#444;border-top:1px solid #5e5e5e;border-bottom:none;margin:0}.left-submenu .back>a:hover{background:#303030;border-top:1px solid #5e5e5e;border-bottom:none}.left-submenu .back>a:before{content:"\AB";margin-right:0.5rem;display:inline}.left-submenu.move-right{-ms-transform:translate(0%, 0);-webkit-transform:translate3d(0%, 0, 0);-moz-transform:translate3d(0%, 0, 0);-ms-transform:translate3d(0%, 0, 0);-o-transform:translate3d(0%, 0, 0);transform:translate3d(0%, 0, 0)}.right-submenu{-webkit-backface-visibility:hidden;width:15.625rem;top:0;bottom:0;position:absolute;margin:0;overflow-x:hidden;overflow-y:auto;background:#333;z-index:1002;box-sizing:content-box;-webkit-overflow-scrolling:touch;-ms-transform:translate(100%, 0);-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.right-submenu *{-webkit-backface-visibility:hidden}.right-submenu .back>a{padding:0.3rem 0.9375rem;color:#999;text-transform:uppercase;font-weight:bold;background:#444;border-top:1px solid #5e5e5e;border-bottom:none;margin:0}.right-submenu .back>a:hover{background:#303030;border-top:1px solid #5e5e5e;border-bottom:none}.right-submenu .back>a:after{content:"\BB";margin-left:0.5rem;display:inline}.right-submenu.move-left{-ms-transform:translate(0%, 0);-webkit-transform:translate3d(0%, 0, 0);-moz-transform:translate3d(0%, 0, 0);-ms-transform:translate3d(0%, 0, 0);-o-transform:translate3d(0%, 0, 0);transform:translate3d(0%, 0, 0)}.left-off-canvas-menu ul.off-canvas-list li.has-submenu>a:after{content:"\BB";margin-left:0.5rem;display:inline}.right-off-canvas-menu ul.off-canvas-list li.has-submenu>a:before{content:"\AB";margin-right:0.5rem;display:inline}.f-dropdown{position:absolute;left:-9999px;list-style:none;margin-left:0;outline:none;width:100%;max-height:none;height:auto;background:#fff;border:solid 1px #ccc;font-size:0.875rem;z-index:89;margin-top:2px;max-width:200px}.f-dropdown>*:first-child{margin-top:0}.f-dropdown>*:last-child{margin-bottom:0}.f-dropdown:before{content:"";display:block;width:0;height:0;border:inset 6px;border-color:transparent transparent #fff transparent;border-bottom-style:solid;position:absolute;top:-12px;left:10px;z-index:89}.f-dropdown:after{content:"";display:block;width:0;height:0;border:inset 7px;border-color:transparent transparent #ccc transparent;border-bottom-style:solid;position:absolute;top:-14px;left:9px;z-index:88}.f-dropdown.right:before{left:auto;right:10px}.f-dropdown.right:after{left:auto;right:9px}.f-dropdown.drop-right{position:absolute;left:-9999px;list-style:none;margin-left:0;outline:none;width:100%;max-height:none;height:auto;background:#fff;border:solid 1px #ccc;font-size:0.875rem;z-index:89;margin-top:0;margin-left:2px;max-width:200px}.f-dropdown.drop-right>*:first-child{margin-top:0}.f-dropdown.drop-right>*:last-child{margin-bottom:0}.f-dropdown.drop-right:before{content:"";display:block;width:0;height:0;border:inset 6px;border-color:transparent #fff transparent transparent;border-right-style:solid;position:absolute;top:10px;left:-12px;z-index:89}.f-dropdown.drop-right:after{content:"";display:block;width:0;height:0;border:inset 7px;border-color:transparent #ccc transparent transparent;border-right-style:solid;position:absolute;top:9px;left:-14px;z-index:88}.f-dropdown.drop-left{position:absolute;left:-9999px;list-style:none;margin-left:0;outline:none;width:100%;max-height:none;height:auto;background:#fff;border:solid 1px #ccc;font-size:0.875rem;z-index:89;margin-top:0;margin-left:-2px;max-width:200px}.f-dropdown.drop-left>*:first-child{margin-top:0}.f-dropdown.drop-left>*:last-child{margin-bottom:0}.f-dropdown.drop-left:before{content:"";display:block;width:0;height:0;border:inset 6px;border-color:transparent transparent transparent #fff;border-left-style:solid;position:absolute;top:10px;right:-12px;left:auto;z-index:89}.f-dropdown.drop-left:after{content:"";display:block;width:0;height:0;border:inset 7px;border-color:transparent transparent transparent #ccc;border-left-style:solid;position:absolute;top:9px;right:-14px;left:auto;z-index:88}.f-dropdown.drop-top{position:absolute;left:-9999px;list-style:none;margin-left:0;outline:none;width:100%;max-height:none;height:auto;background:#fff;border:solid 1px #ccc;font-size:0.875rem;z-index:89;margin-top:-2px;margin-left:0;max-width:200px}.f-dropdown.drop-top>*:first-child{margin-top:0}.f-dropdown.drop-top>*:last-child{margin-bottom:0}.f-dropdown.drop-top:before{content:"";display:block;width:0;height:0;border:inset 6px;border-color:#fff transparent transparent transparent;border-top-style:solid;position:absolute;top:auto;bottom:-12px;left:10px;right:auto;z-index:89}.f-dropdown.drop-top:after{content:"";display:block;width:0;height:0;border:inset 7px;border-color:#ccc transparent transparent transparent;border-top-style:solid;position:absolute;top:auto;bottom:-14px;left:9px;right:auto;z-index:88}.f-dropdown li{font-size:0.875rem;cursor:pointer;line-height:1.125rem;margin:0}.f-dropdown li:hover,.f-dropdown li:focus{background:#eee}.f-dropdown li.radius{border-radius:3px}.f-dropdown li a{display:block;padding:0.5rem;color:#555}.f-dropdown.content{position:absolute;left:-9999px;list-style:none;margin-left:0;outline:none;padding:1.25rem;width:100%;height:auto;max-height:none;background:#fff;border:solid 1px #ccc;font-size:0.875rem;z-index:89;max-width:200px}.f-dropdown.content>*:first-child{margin-top:0}.f-dropdown.content>*:last-child{margin-bottom:0}.f-dropdown.tiny{max-width:200px}.f-dropdown.small{max-width:300px}.f-dropdown.medium{max-width:500px}.f-dropdown.large{max-width:800px}.f-dropdown.mega{width:100% !important;max-width:100% !important}.f-dropdown.mega.open{left:0 !important}table{background:#fff;margin-bottom:1.25rem;border:solid 1px #ddd;table-layout:auto}table caption{background:transparent;color:#222;font-size:1rem;font-weight:bold}table thead{background:#f5f5f5}table thead tr th,table thead tr td{padding:0.5rem 0.625rem 0.625rem;font-size:0.875rem;font-weight:bold;color:#222}table tfoot{background:#f5f5f5}table tfoot tr th,table tfoot tr td{padding:0.5rem 0.625rem 0.625rem;font-size:0.875rem;font-weight:bold;color:#222}table tr th,table tr td{padding:0.5625rem 0.625rem;font-size:0.875rem;color:#222;text-align:left}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f9f9f9}table thead tr th,table tfoot tr th,table tfoot tr td,table tbody tr th,table tbody tr td,table tr td{display:table-cell;line-height:1.125rem}.range-slider{display:block;position:relative;width:100%;height:1rem;border:1px solid #ddd;margin:1.25rem 0;-ms-touch-action:none;touch-action:none;background:#fafafa}.range-slider.vertical-range{display:block;position:relative;width:100%;height:1rem;border:1px solid #ddd;margin:1.25rem 0;-ms-touch-action:none;touch-action:none;display:inline-block;width:1rem;height:12.5rem}.range-slider.vertical-range .range-slider-handle{margin-top:0;margin-left:-0.5rem;position:absolute;bottom:-10.5rem}.range-slider.vertical-range .range-slider-active-segment{width:0.875rem;height:auto;bottom:0}.range-slider.radius{background:#fafafa;border-radius:3px}.range-slider.radius .range-slider-handle{background:#008cba;border-radius:3px}.range-slider.radius .range-slider-handle:hover{background:#007ba4}.range-slider.round{background:#fafafa;border-radius:1000px}.range-slider.round .range-slider-handle{background:#008cba;border-radius:1000px}.range-slider.round .range-slider-handle:hover{background:#007ba4}.range-slider.disabled,.range-slider[disabled]{background:#fafafa;cursor:default;opacity:0.7}.range-slider.disabled .range-slider-handle,.range-slider[disabled] .range-slider-handle{background:#008cba;cursor:default;opacity:0.7}.range-slider.disabled .range-slider-handle:hover,.range-slider[disabled] .range-slider-handle:hover{background:#007ba4}.range-slider-active-segment{display:inline-block;position:absolute;height:0.875rem;background:#e5e5e5}.range-slider-handle{display:inline-block;position:absolute;z-index:1;top:-0.3125rem;width:2rem;height:1.375rem;border:1px solid none;cursor:pointer;-ms-touch-action:manipulation;touch-action:manipulation;background:#008cba}.range-slider-handle:hover{background:#007ba4}[class*="block-grid-"]{display:block;padding:0;margin:0 -0.625rem}[class*="block-grid-"]:before,[class*="block-grid-"]:after{content:" ";display:table}[class*="block-grid-"]:after{clear:both}[class*="block-grid-"]>li{display:block;height:auto;float:left;padding:0 0.625rem 1.25rem}@media only screen{.small-block-grid-1>li{width:100%;list-style:none}.small-block-grid-1>li:nth-of-type(1n){clear:none}.small-block-grid-1>li:nth-of-type(1n+1){clear:both}.small-block-grid-2>li{width:50%;list-style:none}.small-block-grid-2>li:nth-of-type(1n){clear:none}.small-block-grid-2>li:nth-of-type(2n+1){clear:both}.small-block-grid-3>li{width:33.33333%;list-style:none}.small-block-grid-3>li:nth-of-type(1n){clear:none}.small-block-grid-3>li:nth-of-type(3n+1){clear:both}.small-block-grid-4>li{width:25%;list-style:none}.small-block-grid-4>li:nth-of-type(1n){clear:none}.small-block-grid-4>li:nth-of-type(4n+1){clear:both}.small-block-grid-5>li{width:20%;list-style:none}.small-block-grid-5>li:nth-of-type(1n){clear:none}.small-block-grid-5>li:nth-of-type(5n+1){clear:both}.small-block-grid-6>li{width:16.66667%;list-style:none}.small-block-grid-6>li:nth-of-type(1n){clear:none}.small-block-grid-6>li:nth-of-type(6n+1){clear:both}.small-block-grid-7>li{width:14.28571%;list-style:none}.small-block-grid-7>li:nth-of-type(1n){clear:none}.small-block-grid-7>li:nth-of-type(7n+1){clear:both}.small-block-grid-8>li{width:12.5%;list-style:none}.small-block-grid-8>li:nth-of-type(1n){clear:none}.small-block-grid-8>li:nth-of-type(8n+1){clear:both}.small-block-grid-9>li{width:11.11111%;list-style:none}.small-block-grid-9>li:nth-of-type(1n){clear:none}.small-block-grid-9>li:nth-of-type(9n+1){clear:both}.small-block-grid-10>li{width:10%;list-style:none}.small-block-grid-10>li:nth-of-type(1n){clear:none}.small-block-grid-10>li:nth-of-type(10n+1){clear:both}.small-block-grid-11>li{width:9.09091%;list-style:none}.small-block-grid-11>li:nth-of-type(1n){clear:none}.small-block-grid-11>li:nth-of-type(11n+1){clear:both}.small-block-grid-12>li{width:8.33333%;list-style:none}.small-block-grid-12>li:nth-of-type(1n){clear:none}.small-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 40.063em){.medium-block-grid-1>li{width:100%;list-style:none}.medium-block-grid-1>li:nth-of-type(1n){clear:none}.medium-block-grid-1>li:nth-of-type(1n+1){clear:both}.medium-block-grid-2>li{width:50%;list-style:none}.medium-block-grid-2>li:nth-of-type(1n){clear:none}.medium-block-grid-2>li:nth-of-type(2n+1){clear:both}.medium-block-grid-3>li{width:33.33333%;list-style:none}.medium-block-grid-3>li:nth-of-type(1n){clear:none}.medium-block-grid-3>li:nth-of-type(3n+1){clear:both}.medium-block-grid-4>li{width:25%;list-style:none}.medium-block-grid-4>li:nth-of-type(1n){clear:none}.medium-block-grid-4>li:nth-of-type(4n+1){clear:both}.medium-block-grid-5>li{width:20%;list-style:none}.medium-block-grid-5>li:nth-of-type(1n){clear:none}.medium-block-grid-5>li:nth-of-type(5n+1){clear:both}.medium-block-grid-6>li{width:16.66667%;list-style:none}.medium-block-grid-6>li:nth-of-type(1n){clear:none}.medium-block-grid-6>li:nth-of-type(6n+1){clear:both}.medium-block-grid-7>li{width:14.28571%;list-style:none}.medium-block-grid-7>li:nth-of-type(1n){clear:none}.medium-block-grid-7>li:nth-of-type(7n+1){clear:both}.medium-block-grid-8>li{width:12.5%;list-style:none}.medium-block-grid-8>li:nth-of-type(1n){clear:none}.medium-block-grid-8>li:nth-of-type(8n+1){clear:both}.medium-block-grid-9>li{width:11.11111%;list-style:none}.medium-block-grid-9>li:nth-of-type(1n){clear:none}.medium-block-grid-9>li:nth-of-type(9n+1){clear:both}.medium-block-grid-10>li{width:10%;list-style:none}.medium-block-grid-10>li:nth-of-type(1n){clear:none}.medium-block-grid-10>li:nth-of-type(10n+1){clear:both}.medium-block-grid-11>li{width:9.09091%;list-style:none}.medium-block-grid-11>li:nth-of-type(1n){clear:none}.medium-block-grid-11>li:nth-of-type(11n+1){clear:both}.medium-block-grid-12>li{width:8.33333%;list-style:none}.medium-block-grid-12>li:nth-of-type(1n){clear:none}.medium-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 64.063em){.large-block-grid-1>li{width:100%;list-style:none}.large-block-grid-1>li:nth-of-type(1n){clear:none}.large-block-grid-1>li:nth-of-type(1n+1){clear:both}.large-block-grid-2>li{width:50%;list-style:none}.large-block-grid-2>li:nth-of-type(1n){clear:none}.large-block-grid-2>li:nth-of-type(2n+1){clear:both}.large-block-grid-3>li{width:33.33333%;list-style:none}.large-block-grid-3>li:nth-of-type(1n){clear:none}.large-block-grid-3>li:nth-of-type(3n+1){clear:both}.large-block-grid-4>li{width:25%;list-style:none}.large-block-grid-4>li:nth-of-type(1n){clear:none}.large-block-grid-4>li:nth-of-type(4n+1){clear:both}.large-block-grid-5>li{width:20%;list-style:none}.large-block-grid-5>li:nth-of-type(1n){clear:none}.large-block-grid-5>li:nth-of-type(5n+1){clear:both}.large-block-grid-6>li{width:16.66667%;list-style:none}.large-block-grid-6>li:nth-of-type(1n){clear:none}.large-block-grid-6>li:nth-of-type(6n+1){clear:both}.large-block-grid-7>li{width:14.28571%;list-style:none}.large-block-grid-7>li:nth-of-type(1n){clear:none}.large-block-grid-7>li:nth-of-type(7n+1){clear:both}.large-block-grid-8>li{width:12.5%;list-style:none}.large-block-grid-8>li:nth-of-type(1n){clear:none}.large-block-grid-8>li:nth-of-type(8n+1){clear:both}.large-block-grid-9>li{width:11.11111%;list-style:none}.large-block-grid-9>li:nth-of-type(1n){clear:none}.large-block-grid-9>li:nth-of-type(9n+1){clear:both}.large-block-grid-10>li{width:10%;list-style:none}.large-block-grid-10>li:nth-of-type(1n){clear:none}.large-block-grid-10>li:nth-of-type(10n+1){clear:both}.large-block-grid-11>li{width:9.09091%;list-style:none}.large-block-grid-11>li:nth-of-type(1n){clear:none}.large-block-grid-11>li:nth-of-type(11n+1){clear:both}.large-block-grid-12>li{width:8.33333%;list-style:none}.large-block-grid-12>li:nth-of-type(1n){clear:none}.large-block-grid-12>li:nth-of-type(12n+1){clear:both}}.flex-video{position:relative;padding-top:1.5625rem;padding-bottom:67.5%;height:0;margin-bottom:1rem;overflow:hidden}.flex-video.widescreen{padding-bottom:56.34%}.flex-video.vimeo{padding-top:0}.flex-video iframe,.flex-video object,.flex-video embed,.flex-video video{position:absolute;top:0;left:0;width:100%;height:100%}.keystroke,kbd{background-color:#ededed;border-color:#ddd;color:#222;border-style:solid;border-width:1px;margin:0;font-family:"Consolas","Menlo","Courier",monospace;font-size:inherit;padding:0.125rem 0.25rem 0;border-radius:3px}.switch{padding:0;border:none;position:relative;outline:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.switch label{display:block;margin-bottom:1rem;position:relative;color:transparent;background:#ddd;text-indent:100%;width:4rem;height:2rem;cursor:pointer;transition:left 0.15s ease-out}.switch input{opacity:0;position:absolute;top:9px;left:10px;padding:0}.switch input+label{margin-left:0;margin-right:0}.switch label:after{content:"";display:block;background:#fff;position:absolute;top:.25rem;left:.25rem;width:1.5rem;height:1.5rem;-webkit-transition:left 0.15s ease-out;-moz-transition:left 0.15s ease-out;transition:left 0.15s ease-out;-webkit-transform:translate3d(0, 0, 0);-moz-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.switch input:checked+label{background:#008cba}.switch input:checked+label:after{left:2.25rem}.switch label{width:4rem;height:2rem}.switch label:after{width:1.5rem;height:1.5rem}.switch input:checked+label:after{left:2.25rem}.switch label{color:transparent;background:#ddd}.switch label:after{background:#fff}.switch input:checked+label{background:#008cba}.switch.large label{width:5rem;height:2.5rem}.switch.large label:after{width:2rem;height:2rem}.switch.large input:checked+label:after{left:2.75rem}.switch.small label{width:3.5rem;height:1.75rem}.switch.small label:after{width:1.25rem;height:1.25rem}.switch.small input:checked+label:after{left:2rem}.switch.tiny label{width:3rem;height:1.5rem}.switch.tiny label:after{width:1rem;height:1rem}.switch.tiny input:checked+label:after{left:1.75rem}.switch.radius label{border-radius:4px}.switch.radius label:after{border-radius:3px}.switch.round{border-radius:1000px}.switch.round label{border-radius:2rem}.switch.round label:after{border-radius:2rem}@media only screen{.show-for-small-only,.show-for-small-up,.show-for-small,.show-for-small-down,.hide-for-medium-only,.hide-for-medium-up,.hide-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xxlarge-only,.hide-for-xxlarge-up{display:inherit !important}.hide-for-small-only,.hide-for-small-up,.hide-for-small,.hide-for-small-down,.show-for-medium-only,.show-for-medium-up,.show-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xxlarge-only,.show-for-xxlarge-up{display:none !important}.visible-for-small-only,.visible-for-small-up,.visible-for-small,.visible-for-small-down,.hidden-for-medium-only,.hidden-for-medium-up,.hidden-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.hidden-for-small-only,.hidden-for-small-up,.hidden-for-small,.hidden-for-small-down,.visible-for-medium-only,.visible-for-medium-up,.visible-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xxlarge-only,.visible-for-xxlarge-up{position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}table.show-for-small-only,table.show-for-small-up,table.show-for-small,table.show-for-small-down,table.hide-for-medium-only,table.hide-for-medium-up,table.hide-for-medium,table.show-for-medium-down,table.hide-for-large-only,table.hide-for-large-up,table.hide-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up{display:table !important}thead.show-for-small-only,thead.show-for-small-up,thead.show-for-small,thead.show-for-small-down,thead.hide-for-medium-only,thead.hide-for-medium-up,thead.hide-for-medium,thead.show-for-medium-down,thead.hide-for-large-only,thead.hide-for-large-up,thead.hide-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up{display:table-header-group !important}tbody.show-for-small-only,tbody.show-for-small-up,tbody.show-for-small,tbody.show-for-small-down,tbody.hide-for-medium-only,tbody.hide-for-medium-up,tbody.hide-for-medium,tbody.show-for-medium-down,tbody.hide-for-large-only,tbody.hide-for-large-up,tbody.hide-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up{display:table-row-group !important}tr.show-for-small-only,tr.show-for-small-up,tr.show-for-small,tr.show-for-small-down,tr.hide-for-medium-only,tr.hide-for-medium-up,tr.hide-for-medium,tr.show-for-medium-down,tr.hide-for-large-only,tr.hide-for-large-up,tr.hide-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up{display:table-row !important}th.show-for-small-only,td.show-for-small-only,th.show-for-small-up,td.show-for-small-up,th.show-for-small,td.show-for-small,th.show-for-small-down,td.show-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.hide-for-medium-up,td.hide-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.show-for-medium-down,td.show-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.hide-for-large-up,td.hide-for-large-up,th.hide-for-large,td.hide-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up{display:table-cell !important}}@media only screen and (min-width: 40.063em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.show-for-medium-only,.show-for-medium-up,.show-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xxlarge-only,.hide-for-xxlarge-up{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.hide-for-medium-only,.hide-for-medium-up,.hide-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xxlarge-only,.show-for-xxlarge-up{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.visible-for-medium-only,.visible-for-medium-up,.visible-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.hidden-for-medium-only,.hidden-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xxlarge-only,.visible-for-xxlarge-up{position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.show-for-medium-only,table.show-for-medium-up,table.show-for-medium,table.show-for-medium-down,table.hide-for-large-only,table.hide-for-large-up,table.hide-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.show-for-medium-only,thead.show-for-medium-up,thead.show-for-medium,thead.show-for-medium-down,thead.hide-for-large-only,thead.hide-for-large-up,thead.hide-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.show-for-medium-only,tbody.show-for-medium-up,tbody.show-for-medium,tbody.show-for-medium-down,tbody.hide-for-large-only,tbody.hide-for-large-up,tbody.hide-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.show-for-medium-only,tr.show-for-medium-up,tr.show-for-medium,tr.show-for-medium-down,tr.hide-for-large-only,tr.hide-for-large-up,tr.hide-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up{display:table-row !important}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.show-for-medium-only,td.show-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.show-for-medium,td.show-for-medium,th.show-for-medium-down,td.show-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.hide-for-large-up,td.hide-for-large-up,th.hide-for-large,td.hide-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up{display:table-cell !important}}@media only screen and (min-width: 64.063em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.show-for-large-only,.show-for-large-up,.show-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xxlarge-only,.hide-for-xxlarge-up{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.hide-for-large-only,.hide-for-large-up,.hide-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.show-for-xxlarge-only,.show-for-xxlarge-up{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.visible-for-large-only,.visible-for-large-up,.visible-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.hidden-for-large-only,.hidden-for-large-up,.hidden-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xxlarge-only,.visible-for-xxlarge-up{position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.show-for-large-only,table.show-for-large-up,table.show-for-large,table.show-for-large-down,table.hide-for-xlarge-only,table.hide-for-xlarge-up,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.show-for-large-only,thead.show-for-large-up,thead.show-for-large,thead.show-for-large-down,thead.hide-for-xlarge-only,thead.hide-for-xlarge-up,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.show-for-large-only,tbody.show-for-large-up,tbody.show-for-large,tbody.show-for-large-down,tbody.hide-for-xlarge-only,tbody.hide-for-xlarge-up,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.show-for-large-only,tr.show-for-large-up,tr.show-for-large,tr.show-for-large-down,tr.hide-for-xlarge-only,tr.hide-for-xlarge-up,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up{display:table-row !important}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.show-for-large-only,td.show-for-large-only,th.show-for-large-up,td.show-for-large-up,th.show-for-large,td.show-for-large,th.show-for-large-down,td.show-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.hide-for-xlarge-up,td.hide-for-xlarge-up,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up{display:table-cell !important}}@media only screen and (min-width: 90.063em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.hide-for-large-only,.show-for-large-up,.hide-for-large,.hide-for-large-down,.show-for-xlarge-only,.show-for-xlarge-up,.hide-for-xxlarge-only,.hide-for-xxlarge-up{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.show-for-large-only,.hide-for-large-up,.show-for-large,.show-for-large-down,.hide-for-xlarge-only,.hide-for-xlarge-up,.show-for-xxlarge-only,.show-for-xxlarge-up{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.hidden-for-large-only,.visible-for-large-up,.hidden-for-large,.hidden-for-large-down,.visible-for-xlarge-only,.visible-for-xlarge-up,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.visible-for-large-only,.hidden-for-large-up,.visible-for-large,.visible-for-large-down,.hidden-for-xlarge-only,.hidden-for-xlarge-up,.visible-for-xxlarge-only,.visible-for-xxlarge-up{position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-large-only,table.show-for-large-up,table.hide-for-large,table.hide-for-large-down,table.show-for-xlarge-only,table.show-for-xlarge-up,table.hide-for-xxlarge-only,table.hide-for-xxlarge-up{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-large-only,thead.show-for-large-up,thead.hide-for-large,thead.hide-for-large-down,thead.show-for-xlarge-only,thead.show-for-xlarge-up,thead.hide-for-xxlarge-only,thead.hide-for-xxlarge-up{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-large-only,tbody.show-for-large-up,tbody.hide-for-large,tbody.hide-for-large-down,tbody.show-for-xlarge-only,tbody.show-for-xlarge-up,tbody.hide-for-xxlarge-only,tbody.hide-for-xxlarge-up{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-large-only,tr.show-for-large-up,tr.hide-for-large,tr.hide-for-large-down,tr.show-for-xlarge-only,tr.show-for-xlarge-up,tr.hide-for-xxlarge-only,tr.hide-for-xxlarge-up{display:table-row !important}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.show-for-large-up,td.show-for-large-up,th.hide-for-large,td.hide-for-large,th.hide-for-large-down,td.hide-for-large-down,th.show-for-xlarge-only,td.show-for-xlarge-only,th.show-for-xlarge-up,td.show-for-xlarge-up,th.hide-for-xxlarge-only,td.hide-for-xxlarge-only,th.hide-for-xxlarge-up,td.hide-for-xxlarge-up{display:table-cell !important}}@media only screen and (min-width: 120.063em){.hide-for-small-only,.show-for-small-up,.hide-for-small,.hide-for-small-down,.hide-for-medium-only,.show-for-medium-up,.hide-for-medium,.hide-for-medium-down,.hide-for-large-only,.show-for-large-up,.hide-for-large,.hide-for-large-down,.hide-for-xlarge-only,.show-for-xlarge-up,.show-for-xxlarge-only,.show-for-xxlarge-up{display:inherit !important}.show-for-small-only,.hide-for-small-up,.show-for-small,.show-for-small-down,.show-for-medium-only,.hide-for-medium-up,.show-for-medium,.show-for-medium-down,.show-for-large-only,.hide-for-large-up,.show-for-large,.show-for-large-down,.show-for-xlarge-only,.hide-for-xlarge-up,.hide-for-xxlarge-only,.hide-for-xxlarge-up{display:none !important}.hidden-for-small-only,.visible-for-small-up,.hidden-for-small,.hidden-for-small-down,.hidden-for-medium-only,.visible-for-medium-up,.hidden-for-medium,.hidden-for-medium-down,.hidden-for-large-only,.visible-for-large-up,.hidden-for-large,.hidden-for-large-down,.hidden-for-xlarge-only,.visible-for-xlarge-up,.visible-for-xxlarge-only,.visible-for-xxlarge-up{position:static !important;height:auto;width:auto;overflow:visible;clip:auto}.visible-for-small-only,.hidden-for-small-up,.visible-for-small,.visible-for-small-down,.visible-for-medium-only,.hidden-for-medium-up,.visible-for-medium,.visible-for-medium-down,.visible-for-large-only,.hidden-for-large-up,.visible-for-large,.visible-for-large-down,.visible-for-xlarge-only,.hidden-for-xlarge-up,.hidden-for-xxlarge-only,.hidden-for-xxlarge-up{position:absolute !important;height:1px;width:1px;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}table.hide-for-small-only,table.show-for-small-up,table.hide-for-small,table.hide-for-small-down,table.hide-for-medium-only,table.show-for-medium-up,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-large-only,table.show-for-large-up,table.hide-for-large,table.hide-for-large-down,table.hide-for-xlarge-only,table.show-for-xlarge-up,table.show-for-xxlarge-only,table.show-for-xxlarge-up{display:table !important}thead.hide-for-small-only,thead.show-for-small-up,thead.hide-for-small,thead.hide-for-small-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-large-only,thead.show-for-large-up,thead.hide-for-large,thead.hide-for-large-down,thead.hide-for-xlarge-only,thead.show-for-xlarge-up,thead.show-for-xxlarge-only,thead.show-for-xxlarge-up{display:table-header-group !important}tbody.hide-for-small-only,tbody.show-for-small-up,tbody.hide-for-small,tbody.hide-for-small-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-large-only,tbody.show-for-large-up,tbody.hide-for-large,tbody.hide-for-large-down,tbody.hide-for-xlarge-only,tbody.show-for-xlarge-up,tbody.show-for-xxlarge-only,tbody.show-for-xxlarge-up{display:table-row-group !important}tr.hide-for-small-only,tr.show-for-small-up,tr.hide-for-small,tr.hide-for-small-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-large-only,tr.show-for-large-up,tr.hide-for-large,tr.hide-for-large-down,tr.hide-for-xlarge-only,tr.show-for-xlarge-up,tr.show-for-xxlarge-only,tr.show-for-xxlarge-up{display:table-row !important}th.hide-for-small-only,td.hide-for-small-only,th.show-for-small-up,td.show-for-small-up,th.hide-for-small,td.hide-for-small,th.hide-for-small-down,td.hide-for-small-down,th.hide-for-medium-only,td.hide-for-medium-only,th.show-for-medium-up,td.show-for-medium-up,th.hide-for-medium,td.hide-for-medium,th.hide-for-medium-down,td.hide-for-medium-down,th.hide-for-large-only,td.hide-for-large-only,th.show-for-large-up,td.show-for-large-up,th.hide-for-large,td.hide-for-large,th.hide-for-large-down,td.hide-for-large-down,th.hide-for-xlarge-only,td.hide-for-xlarge-only,th.show-for-xlarge-up,td.show-for-xlarge-up,th.show-for-xxlarge-only,td.show-for-xxlarge-only,th.show-for-xxlarge-up,td.show-for-xxlarge-up{display:table-cell !important}}.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.hide-for-landscape,table.show-for-portrait{display:table !important}thead.hide-for-landscape,thead.show-for-portrait{display:table-header-group !important}tbody.hide-for-landscape,tbody.show-for-portrait{display:table-row-group !important}tr.hide-for-landscape,tr.show-for-portrait{display:table-row !important}td.hide-for-landscape,td.show-for-portrait,th.hide-for-landscape,th.show-for-portrait{display:table-cell !important}@media only screen and (orientation: landscape){.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.show-for-landscape,table.hide-for-portrait{display:table !important}thead.show-for-landscape,thead.hide-for-portrait{display:table-header-group !important}tbody.show-for-landscape,tbody.hide-for-portrait{display:table-row-group !important}tr.show-for-landscape,tr.hide-for-portrait{display:table-row !important}td.show-for-landscape,td.hide-for-portrait,th.show-for-landscape,th.hide-for-portrait{display:table-cell !important}}@media only screen and (orientation: portrait){.show-for-portrait,.hide-for-landscape{display:inherit !important}.hide-for-portrait,.show-for-landscape{display:none !important}table.show-for-portrait,table.hide-for-landscape{display:table !important}thead.show-for-portrait,thead.hide-for-landscape{display:table-header-group !important}tbody.show-for-portrait,tbody.hide-for-landscape{display:table-row-group !important}tr.show-for-portrait,tr.hide-for-landscape{display:table-row !important}td.show-for-portrait,td.hide-for-landscape,th.show-for-portrait,th.hide-for-landscape{display:table-cell !important}}.show-for-touch{display:none !important}.hide-for-touch{display:inherit !important}.touch .show-for-touch{display:inherit !important}.touch .hide-for-touch{display:none !important}table.hide-for-touch{display:table !important}.touch table.show-for-touch{display:table !important}thead.hide-for-touch{display:table-header-group !important}.touch thead.show-for-touch{display:table-header-group !important}tbody.hide-for-touch{display:table-row-group !important}.touch tbody.show-for-touch{display:table-row-group !important}tr.hide-for-touch{display:table-row !important}.touch tr.show-for-touch{display:table-row !important}td.hide-for-touch{display:table-cell !important}.touch td.show-for-touch{display:table-cell !important}th.hide-for-touch{display:table-cell !important}.touch th.show-for-touch{display:table-cell !important}@media print{.show-for-print{display:block}.hide-for-print{display:none}table.show-for-print{display:table !important}thead.show-for-print{display:table-header-group !important}tbody.show-for-print{display:table-row-group !important}tr.show-for-print{display:table-row !important}td.show-for-print{display:table-cell !important}th.show-for-print{display:table-cell !important}} diff --git a/css/images/_notes/dwsync.xml b/css/images/_notes/dwsync.xml deleted file mode 100644 index 9866ef3..0000000 --- a/css/images/_notes/dwsync.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/css/images/details_close.png b/css/images/details_close.png deleted file mode 100644 index 9c7d698..0000000 Binary files a/css/images/details_close.png and /dev/null differ diff --git a/css/images/details_open.png b/css/images/details_open.png deleted file mode 100644 index c0edf44..0000000 Binary files a/css/images/details_open.png and /dev/null differ diff --git a/css/images/sort_asc.png b/css/images/sort_asc.png deleted file mode 100644 index e1ba61a..0000000 Binary files a/css/images/sort_asc.png and /dev/null differ diff --git a/css/images/sort_asc_disabled.png b/css/images/sort_asc_disabled.png deleted file mode 100644 index fb11dfe..0000000 Binary files a/css/images/sort_asc_disabled.png and /dev/null differ diff --git a/css/images/sort_both.png b/css/images/sort_both.png deleted file mode 100644 index af5bc7c..0000000 Binary files a/css/images/sort_both.png and /dev/null differ diff --git a/css/images/sort_desc.png b/css/images/sort_desc.png deleted file mode 100644 index 0e156de..0000000 Binary files a/css/images/sort_desc.png and /dev/null differ diff --git a/css/images/sort_desc_disabled.png b/css/images/sort_desc_disabled.png deleted file mode 100644 index c9fdd8a..0000000 Binary files a/css/images/sort_desc_disabled.png and /dev/null differ diff --git a/css/jquery.steps.css b/css/jquery.steps.css deleted file mode 100644 index 6c5155a..0000000 --- a/css/jquery.steps.css +++ /dev/null @@ -1,383 +0,0 @@ -/* - Common -*/ - -.wizard, -.tabcontrol -{ - display: block; - width: 100%; - overflow: hidden; -} - -.wizard a, -.tabcontrol a -{ - outline: 0; -} - -.wizard ul, -.tabcontrol ul -{ - list-style: none !important; - padding: 0; - margin: 0; -} - -.wizard ul > li, -.tabcontrol ul > li -{ - display: block; - padding: 0; -} - -/* Accessibility */ -.wizard > .steps .current-info, -.tabcontrol > .steps .current-info -{ - position: absolute; - left: -999em; -} - -.wizard > .content > .title, -.tabcontrol > .content > .title -{ - position: absolute; - left: -999em; -} - - - -/* - Wizard -*/ - -.wizard > .steps -{ - position: relative; - display: block; - width: 100%; -} - -.wizard.vertical > .steps -{ - display: inline; - float: left; - width: 30%; -} - -.wizard > .steps .number -{ - font-size: 1.429em; -} - -.wizard > .steps > ul > li -{ - width: 25%; -} - -.wizard > .steps > ul > li, -.wizard > .actions > ul > li -{ - float: left; -} - -.wizard.vertical > .steps > ul > li -{ - float: none; - width: 100%; -} - -.wizard > .steps a, -.wizard > .steps a:hover, -.wizard > .steps a:active -{ - display: block; - width: auto; - margin: 0 0.5em 0.5em; - padding: 1em 1em; - text-decoration: none; - - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} - -.wizard > .steps .disabled a, -.wizard > .steps .disabled a:hover, -.wizard > .steps .disabled a:active -{ - background: #eee; - color: #aaa; - cursor: default; -} - -.wizard > .steps .current a, -.wizard > .steps .current a:hover, -.wizard > .steps .current a:active -{ - background: #f16c65; - color: #fff; - cursor: default; -} - -.wizard > .steps .done a, -.wizard > .steps .done a:hover, -.wizard > .steps .done a:active -{ - background: rgba(248, 168, 164, 1); - color: #fff; -} - -.wizard > .steps .error a, -.wizard > .steps .error a:hover, -.wizard > .steps .error a:active -{ - background: #f16c65; - color: #fff; -} - -.wizard > .content -{ - /*background: #eee;*/ - display: block; - margin: 0.5em; - min-height: 20em; - overflow: hidden; - position: relative; - width: auto; - - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} - -.wizard.vertical > .content -{ - display: inline; - float: left; - margin: 0 2.5% 0.5em 2.5%; - width: 65%; -} - -.wizard > .content > .body -{ - float: left; - position: absolute; - width: 95%; - height: 95%; - padding: 2.5%; - padding-left:0px; -} - -.wizard > .content > .body ul -{ - list-style: disc !important; -} - -.wizard > .content > .body ul > li -{ - display: list-item; -} - -.wizard > .content > .body > iframe -{ - border: 0 none; - width: 100%; - height: 100%; -} - -.wizard > .content > .body input -{ - display: block; - border: 1px solid #ccc; -} - -.wizard > .content > .body input[type="checkbox"] -{ - display: inline-block; -} - -.wizard > .content > .body input.error -{ - background: rgb(251, 227, 228); - border: 1px solid #fbc2c4; - color: #8a1f11; -} - -.wizard > .content > .body label -{ - display: inline-block; - margin-bottom: 0.5em; -} - -.wizard > .content > .body label.error -{ - color: #8a1f11; - display: inline-block; - margin-left: 1.5em; -} - -.wizard > .actions -{ - position: relative; - display: block; - text-align: right; - width: 100%; -} - -.wizard.vertical > .actions -{ - display: inline; - float: right; - margin: 0 2.5%; - width: 95%; -} - -.wizard > .actions > ul -{ - display: inline-block; - text-align: right; -} - -.wizard > .actions > ul > li -{ - margin: 0 0.5em; -} - -.wizard.vertical > .actions > ul > li -{ - margin: 0 0 0 1em; -} - -.wizard > .actions a, -.wizard > .actions a:hover, -.wizard > .actions a:active -{ - background: #f16c65; - color: #fff; - display: block; - padding: 0.5em 1em; - text-decoration: none; - - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} - -.wizard > .actions .disabled a, -.wizard > .actions .disabled a:hover, -.wizard > .actions .disabled a:active -{ - background: #eee; - color: #aaa; -} - -.wizard > .loading -{ -} - -.wizard > .loading .spinner -{ -} - - - -/* - Tabcontrol -*/ - -.tabcontrol > .steps -{ - position: relative; - display: block; - width: 100%; -} - -.tabcontrol > .steps > ul -{ - position: relative; - margin: 6px 0 0 0; - top: 1px; - z-index: 1; -} - -.tabcontrol > .steps > ul > li -{ - float: left; - margin: 5px 2px 0 0; - padding: 1px; - - -webkit-border-top-left-radius: 5px; - -webkit-border-top-right-radius: 5px; - -moz-border-radius-topleft: 5px; - -moz-border-radius-topright: 5px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} - -.tabcontrol > .steps > ul > li:hover -{ - background: #edecec; - border: 1px solid #bbb; - padding: 0; -} - -.tabcontrol > .steps > ul > li.current -{ - background: #fff; - border: 1px solid #bbb; - border-bottom: 0 none; - padding: 0 0 1px 0; - margin-top: 0; -} - -.tabcontrol > .steps > ul > li > a -{ - color: #5f5f5f; - display: inline-block; - border: 0 none; - margin: 0; - padding: 10px 30px; - text-decoration: none; -} - -.tabcontrol > .steps > ul > li > a:hover -{ - text-decoration: none; -} - -.tabcontrol > .steps > ul > li.current > a -{ - padding: 15px 30px 10px 30px; -} - -.tabcontrol > .content -{ - position: relative; - display: inline-block; - width: 100%; - height: 35em; - overflow: hidden; - border-top: 1px solid #bbb; - padding-top: 20px; -} - -.tabcontrol > .content > .body -{ - float: left; - position: absolute; - width: 95%; - height: 95%; - padding: 2.5%; -} - -.tabcontrol > .content > .body ul -{ - list-style: disc !important; -} - -.tabcontrol > .content > .body ul > li -{ - display: list-item; -} \ No newline at end of file diff --git a/css/jquery.tagsinput.css b/css/jquery.tagsinput.css deleted file mode 100644 index e316ac4..0000000 --- a/css/jquery.tagsinput.css +++ /dev/null @@ -1,59 +0,0 @@ -div.tagsinput { - border: 1px solid #CCC; - background: #FFF; - padding: 5px; - width: 300px; - height: 100px; - overflow-y: auto; -} - - div.tagsinput span.tag { - border: 1px solid #f16c65; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - display: block; - float: left; - padding: 5px; - text-decoration: none; - background: #f16c65; - color: #fff; - margin-right: 5px; - margin-bottom: 5px; - font-size: 13px; - } - - div.tagsinput span.tag a { - font-weight: bold; - color: #fff; - text-decoration: none; - font-size: 11px; - } - - div.tagsinput input { - width: 80px; - margin: 0px; - font-size: 13px; - border: 1px solid transparent; - padding: 5px; - background: transparent; - color: #000; - outline: 0px; - margin-right: 5px; - margin-bottom: 5px; - } - - div.tagsinput div { - display: block; - float: left; - } - -.tags_clear { - clear: both; - width: 100%; - height: 0px; -} - -.not_valid { - background: #FBD8DB !important; - color: #90111A !important; -} diff --git a/css/plugins/morris/_notes/dwsync.xml b/css/plugins/morris/_notes/dwsync.xml deleted file mode 100644 index b497fbe..0000000 --- a/css/plugins/morris/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/css/plugins/morris/morris-0.4.3.min.css b/css/plugins/morris/morris-0.4.3.min.css deleted file mode 100644 index 99a7134..0000000 --- a/css/plugins/morris/morris-0.4.3.min.css +++ /dev/null @@ -1,2 +0,0 @@ -.morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;} -.morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;} \ No newline at end of file diff --git a/css/responsive-tables.css b/css/responsive-tables.css deleted file mode 100644 index f0a905c..0000000 --- a/css/responsive-tables.css +++ /dev/null @@ -1,38 +0,0 @@ -/* Foundation v2.1.4 http://foundation.zurb.com */ -/* Artfully masterminded by ZURB */ - -/* -------------------------------------------------- - Table of Contents ------------------------------------------------------ -:: Shared Styles -:: Page Name 1 -:: Page Name 2 -*/ - - -/* ----------------------------------------- - Shared Styles ------------------------------------------ */ - -table th { font-weight: bold; } -table td, table th { padding: 9px 10px; text-align: left; } - -/* Mobile */ -@media only screen and (max-width: 767px) { - - table.responsive { margin-bottom: 0; } - - .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; } - .pinned table { border-right: none; border-left: none; width: 100%; } - .pinned table th, .pinned table td { white-space: nowrap; } - .pinned td:last-child { border-bottom: 0; } - - div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; } - div.table-wrapper div.scrollable { margin-left: 35%; } - div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; } - - table.responsive td, table.responsive th { position: relative; white-space: nowrap; overflow: hidden; } - table.responsive th:first-child, table.responsive td:first-child, table.responsive td:first-child, table.responsive.pinned td { display: none; } - - -} diff --git a/css/style.css b/css/style.css deleted file mode 100644 index 64d3162..0000000 --- a/css/style.css +++ /dev/null @@ -1,742 +0,0 @@ -body { - font-family: 'Roboto', 'sans-serif'; - color: #84888a !important; - background-color: #f3f3f3; -} - -a { - color: #656C70; -} - -.border { - border: 1px solid #e1e1e1; -} - -a:hover, .side-nav li a:not(.button):hover, .side-nav li a:not(.button):focus { - color: #f16c65; -} - -.tabs-content { - margin-bottom: 1.5rem; - width: 100%; - border: 1px solid #E2E2E2; - border-top: 0px; -} - -.error_message { - color: red; - font-size: 20px; -} - -.top-bar-picture { - height: 40px; - border-radius: 50%; - width: 40px; -} - -.side-nav { - border-right: 1px solid #E6E0E6; - padding-bottom: 0px; -} - - .side-nav > li > ul { - margin-left: 0px; - } - - - .side-nav .accordion dd > a { - background: #FFFFFF; - color: #656C70; - padding: 0px; - } - -.top-part-no-padding { - padding-left: 0px; - padding-right: 0px; -} - -.top-menu { - background-color: #2d2f3b; - min-height: 70px; - padding-top: 10px; -} - -.toggles { - font-size: 1.3rem; - color: #fff; - display: none; - margin-left: 5px; - position: relative; -} - - .toggles:hover { - cursor: pointer; - } - -@media all and (max-width:1025px) { - .toggles { - font-size: 1.3rem; - color: #fff; - display: inline !important; - } - - .side-nav { - display: none; - } -} - -@media all and (min-width:1026px) { - .side-nav { - display: block !important; - } -} - -.logo-bg { - background-color: #f16c65; - min-height: 70px; - padding-top: 10px; -} - -.power-off { - font-size: 2rem; - top: 7px; - position: relative; - border-left: 1px solid #746E6E; - padding-left: 18px; -} - - .power-off:hover { - color: #f16c65; - } - -@media all and (max-width:640px) { - .power-off { - padding-left: 0px; - border: 0px; - } - - .top-menu { - min-height: 126px; - } -} - -body, html { - position: relative; - min-height: 100%; -} - -h1, h2, h3, h4, h5, h6 { - font-family: 'Roboto', 'sans-serif'; - font-weight: 300; - color: #656C70 !important; -} - -p, div { - font-size: .90rem; - font-weight: 300; -} - -div { - line-height: 1.2rem; -} - -table tr th, table tr td { - padding: 0.5625rem 0.625rem; - font-size: 0.875rem; - color: #656C70; -} - -.wrapper { -} - -.content-bg { - background: #f3f3f3; -} - -a { - font-weight: 300; - font-family: 'Roboto', 'sans-serif'; -} - -.side-nav { - background: #fff; -} - - .side-nav li { - list-style: none; - } - - .side-nav > li { - padding: 10px; - border-bottom: 1px solid #ebeaeb; - vertical-align: middle; - } - - .side-nav li a:not(.button) { - color: #444; - } - - .side-nav .active { - color: #f16c65; - } - - .side-nav li.active > a:first-child:not(.button) { - color: #f16c65; - font-weight: bold; - } - -.no-padding .columns { - padding: 0px; -} - -.row.full-width { - width: 100%; - max-width: 100%; -} - -.bar-menu { - background: #fff; - border: 1px solid #D8D8D8; - border-bottom: 2px solid #e1e1e1; - width: 97%; - margin: auto; - margin-top: 10px; - margin-bottom: 10px; - height: 50px; -} - -input[type="text"] { - box-shadow: none; -} - -.search-text { - -moz-border-radius: 20px; - border-radius: 20px; - background: #1E202B!important; - border: 1px solid #1E202B!important; - text-align: left; - margin-top: 7px !important; - color: #fff !important; -} -/*color*/ -.blue { - color: #2d2f3b !important; -} - -.blue-bg { - background: #2d2f3b; - color: #fff; -} - .blue-bg:hover { - background: #282A38; - } - -.white { - color: #fff !important; -} - -.white-bg { - background: #fff; -} - -.light-grey-bg { - background-color: #f3f3f3; -} - - - -.light-dark-bg { - background-color: rgba(215, 215, 215, 1); -} - -.light-grey-bg-pattern { - background-color: #f3f3f3; - padding-bottom: 20px; -} - -.coral { - color: #f16c65 !important; -} - -.coral-bg { - background: #f16c65; - color: #fff; -} - - .coral-bg:hover { - background: #FC5850; - } - -.white-bg-btn { - background: #fff; - color: #444; - border: 1px solid #e1e1e1; -} - - .white-bg-btn:hover { - border-color: #FC5850; - background: #fff; - color: #FC5850; - } - -.width-100 { - width: 100%; -} - -.width-95 { - width: 95%; -} - - -.fb-blue { - background: #013567; -} - -.google-plus { - background: #dd4c39; -} - -.dribbble { - background: #e14c86; -} -/*end color*/ - -/*top menu*/ -#top-menu { - min-height: 70px; - position: relative; - z-index: 9; -} - -.page-name { - margin-bottom: 10px; - margin-top: 24px; -} - - .page-name span { - margin-top: 17px; - margin-left: 10px; - } - -.sbar-menu { - color: #788288 !important; - background-color: #fcfcfd; - border: 1px solid #d2dae1; - border-bottom-color: #cbd5dd; - text-align: center; - padding: 5px; - -webkit-border-radius: 20px; - -moz-border-radius: 20px; - border-radius: 20px; - font-size: 1rem; - -webkit-box-shadow: 0 1px 1px rgba(90,90,90,0.1); - box-shadow: 0 1px 1px rgba(90,90,90,0.1); -} -/*end top menu*/ - - -/*login*/ - -#login input[type="text"], #login input[type="password"] { - margin: 0px; - height: 50px; -} - -.border-radius-top { - -webkit-border-top-left-radius: 3px; - -webkit-border-top-right-radius: 3px; - -moz-border-radius-topleft: 3px; - -moz-border-radius-topright: 3px; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - border-bottom: 0px !important; -} - - -.border-radius-bottom { - -webkit-border-bottom-right-radius: 3px; - -webkit-border-bottom-left-radius: 3px; - -moz-border-radius-bottomright: 3px; - -moz-border-radius-bottomleft: 3px; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} - -.no-radius { - border-radius: 0px; - border-bottom: 0px !important; -} - -/*end login*/ - - - -/*pricing*/ -.pricing-table .title { - background-color: #fff; - padding: 0.9375rem 1.25rem; - text-align: center; - color: #444; - font-weight: 300; - font-size: 1.5rem; - font-family: 'Lato', 'sans-serif'; -} - -.custom-price { - width: 100px; - height: 100px; - border-radius: 50%; - background-color: #f16c65; - margin: auto; - color: #fff; - padding-top: 40px; - text-align: center; -} - -.pricing-table { - border: solid 1px #dddddd; - margin-left: 0; - margin-bottom: 1.25rem; - background-color: #fff; -} - - -.recommended { - background-color: #2d2f3b; - height: 470px; -} - -.not-recommended { - margin-top: 20px; -} - -.recommended .description, .recommended .bullet-item, .recommended .title, .recommended .cta-button { - background-color: #2d2f3b; - color: #fff; - border: 0px; -} - -#pricing { - padding: 1rem; - width: 100%; - margin: auto; -} - - #pricing .columns { - padding: 0px; - } -/*end pricing*/ - -/*staff*/ - -.btn-padding { - padding: 10px; -} - -.staff-detail { - background: #fff; - text-align: center; - border: 1px solid #e1e1e1; - border-bottom: 2px solid #e1e1e1; - margin-top: 5px; -} - - .staff-detail .button { - margin-bottom: .7rem; - } - -.staff-info { - padding: 10px 10px 0px 10px; - height: 110px; -} - .staff-info h5 { - margin-top:20px; - } - -.staff-social-info { - margin-top: -35px; - position: relative; -} - - .staff-social-info i { - font-size: 20px; - color: #fff; - padding: 5px 10px 5px 10px; - border-radius: 50%; - } -/*end staff*/ - -/*invoice*/ -.custom-table, .custom-table-border { - background: none; - margin-bottom: 1.25rem; - border: 0px; - border-collapse: collapse; - border-spacing: 0; -} - - .custom-table tr.even, .custom-table tr.alt, .custom-table-border tr:nth-of-type(2n) { - background: none repeat scroll 0% 0% #fff; - } - - .custom-table td { - padding: 1.563rem 3.625rem; - text-align: center; - } - - .custom-table-border tr.even, .custom-table-border tr.alt, .custom-table-border tr:nth-of-type(2n) { - background: none; - } - - .custom-table-border td { - border-bottom: 1px solid #e1e1e1; - } - - .custom-table tr td:nth-child(1) { - padding-left: 2px; - text-align: left; - } - -#payment-method i { - font-size: 44px; - margin-right: 25px; - text-align: right; -} -/*end invoice*/ - -/*form*/ -.custom-panel { - box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05); - -webkit-border-top-left-radius: 3px; - -webkit-border-top-right-radius: 3px; - -moz-border-radius-topleft: 3px; - -moz-border-radius-topright: 3px; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - border: 1px solid #e1e1e1; - border-top: 0px; - margin-top: 15px; -} - -.custom-panel-heading { - padding: 10px; - background: rgba(90,90,90,0.1); -} - -.custom-panel-body { - background: #fff; - padding: 10px; -} - - - - -/*general*/ - -.check-radio label { - display: inline-block; - cursor: pointer; - position: relative; - padding-left: 25px; - margin-right: 15px; - font-size: 13px; -} - -.check-radio input[type=radio], .check-radio input[type=checkbox] { - display: none; -} - -.check-radio label:before { - content: ""; - display: inline-block; - width: 16px; - height: 16px; - margin-right: 10px; - position: absolute; - left: 0; - bottom: 1px; - background-color: #aaa; - box-shadow: inset 0px 2px 3px 0px rgba(0, 0, 0, .3), 0px 1px 0px 0px rgba(255, 255, 255, .8); -} - -.check-radio .radio label:before { - border-radius: 8px; -} - -.check-radio .checkbox label { - margin-bottom: 10px; -} - - .check-radio .checkbox label:before { - border-radius: 3px; - } - -.check-radio input[type=radio]:checked + label:before { - content: "\2022"; - color: #f3f3f3; - font-size: 30px; - text-align: center; - line-height: 18px; -} - -.check-radio input[type=checkbox]:checked + label:before { - content: "\2713"; - text-shadow: 1px 1px 1px rgba(0, 0, 0, .2); - font-size: 15px; - color: #f3f3f3; - text-align: center; - line-height: 15px; -} - -.range-slider-active-segment { - display: inline-block; - position: absolute; - height: 0.875rem; -} - -.range-orange { - background: none repeat scroll 0% 0% #d9450c; -} - -.range-green { - background: none repeat scroll 0% 0% #0a9d11; -} - -.range-purple { - background: none repeat scroll 0% 0% #8d0b8a; -} - - -.range-slider-handle { - display: inline-block; - position: absolute; - z-index: 1; - top: -0.425rem; - width: 2rem; - height: 1.375rem; - cursor: pointer; - width: 30px; - height: 30px; - background-color: #FFF; - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 50%; -} -/*end slider*/ -/*end form*/ -/*inbox*/ -#inbox i { - margin-right:5px; -} -/*end inbox*/ - -/*dashboard*/ -.stats { - background: #fff; - padding: 10px; - margin-top: 5px; -} - - .stats i { - padding: 10px; - color: #fff; - font-size: 2rem; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - } - -.orders { - background: #07a5bb; -} - -.sales { - background: #ee0c0c; -} - -.user { - background: #11ba77; -} - -.profit { - background: #ddd915; -} - -.notification span { - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - padding: 1px 6px 1px 6px; - font-size: .6rem; - top: -10px; - position: relative; -} - -.notification i { - color: #fff; - font-size: 1.8rem; - top: 10px; - position: relative; - text-align: center; -} - -.mail { - background: #07a5bb; - color: #fff; -} - -.megaphone { - background: #0bba1a; - color: #fff; -} -/*end dashboard*/ - -/*profile item*/ -.profile-item { - padding: 10px; -} - -.profile-picture { - border-radius: 50%; - width: 150px; - height: 150px; -} - -.ongoing-task { - margin-top: 10px; - padding: 10px; -} - - .ongoing-task .yellow-progress { - background: #d4b70a; - } - - .ongoing-task .purple-progress { - background: #a00c98; - } - - - -.profile-info .content { - padding: 10px; -} - -.profile-info img { - width: 50px; - height: 50px; -} - -.profile-info dd a { - color: #656C70; -} - -@media all and (max-width:1024px) { - .profile-item { - text-align: center; - } - - .profile-item h4 { - margin-top: 5px; - } -} -/* end profile item*/ diff --git a/css/throbber.gif b/css/throbber.gif deleted file mode 100644 index 1b5b2fd..0000000 Binary files a/css/throbber.gif and /dev/null differ diff --git a/css/todos.css b/css/todos.css deleted file mode 100644 index 4b068ba..0000000 --- a/css/todos.css +++ /dev/null @@ -1,215 +0,0 @@ - -#todoapp { - background: #fff; - width: 520px; - padding: 20px; - margin: 0 auto; - margin-bottom: 40px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; - -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; - -o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; - box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; - -webkit-border-radius: 0 0 5px 5px; - -moz-border-radius: 0 0 5px 5px; - -ms-border-radius: 0 0 5px 5px; - -o-border-radius: 0 0 5px 5px; - border-radius: 0 0 5px 5px; -} - - #todoapp h1 { - font-size: 36px; - font-weight: bold; - text-align: center; - padding: 0 0 10px 0; - } - - #todoapp input[type="text"] { - width: 466px; - font-size: 24px; - font-family: inherit; - line-height: 1.4em; - border: 0; - outline: none; - padding: 6px; - border: 1px solid #999999; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; - -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; - -o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; - box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; - } - - #todoapp input::-webkit-input-placeholder { - font-style: italic; - } - -#main { - display: none; -} - -#todo-list { - margin: 10px 0; - padding: 0; - list-style: none; -} - - #todo-list li { - padding: 10px 20px 10px 10px; - position: relative; - font-size: 24px; - background:#e1e1e1; - margin-top:5px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - } - - #todo-list li:hover { - background:#f16c65; - } - - #todo-list li:hover label { - color:#fff; - } - - #todo-list li:last-child { - border-bottom: none; - } - - #todo-list li.done label { - text-decoration: line-through; - } -.donebg { - background: #f16c65 !important; - color:#fff; -} -.donebg label{ - color:#fff; -} - #todo-list .destroy { - position: absolute; - right: 5px; - top: 20px; - display: none; - cursor: pointer; - width: 20px; - height: 20px; - background: url(../img/destroy.png) no-repeat; - } - - #todo-list li:hover .destroy { - display: block; - } - - #todo-list .destroy:hover { - background-position: 0 -20px; - } - - #todo-list li.editing { - border-bottom: none; - margin-top: -1px; - padding: 0; - } - - #todo-list li.editing:last-child { - margin-bottom: -1px; - } - - #todo-list li.editing .edit { - display: block; - width: 444px; - padding: 13px 15px 14px 20px; - margin: 0; - } - - #todo-list li.editing .view { - display: none; - } - - #todo-list li .view label { - word-break: break-word; - } - - #todo-list li .edit { - display: none; - } - -#todoapp footer { - display: none; - margin: 0 -20px -20px -20px; - overflow: hidden; - color: #555555; - background: #f4fce8; - border-top: 1px solid #ededed; - padding: 0 20px; - line-height: 37px; - -webkit-border-radius: 0 0 5px 5px; - -moz-border-radius: 0 0 5px 5px; - -ms-border-radius: 0 0 5px 5px; - -o-border-radius: 0 0 5px 5px; - border-radius: 0 0 5px 5px; -} - -#clear-completed { - float: right; - line-height: 20px; - text-decoration: none; - background: rgba(0, 0, 0, 0.1); - color: #555555; - font-size: 11px; - margin-top: 8px; - margin-bottom: 8px; - padding: 0 10px 1px; - cursor: pointer; - -webkit-border-radius: 12px; - -moz-border-radius: 12px; - -ms-border-radius: 12px; - -o-border-radius: 12px; - border-radius: 12px; - -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; - -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; - -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; - -o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; - box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; -} - - #clear-completed:hover { - background: rgba(0, 0, 0, 0.15); - -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; - -moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; - -ms-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; - -o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; - box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; - } - - #clear-completed:active { - position: relative; - top: 1px; - } - -#todo-count span { - font-weight: bold; -} - -#instructions { - margin: 10px auto; - color: #777777; - text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; - text-align: center; -} - - #instructions a { - color: #336699; - } - -#credits { - margin: 30px auto; - color: #999; - text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; - text-align: center; -} - - #credits a { - color: #888; - } diff --git a/css/tree.min.css b/css/tree.min.css deleted file mode 100644 index 4440528..0000000 --- a/css/tree.min.css +++ /dev/null @@ -1 +0,0 @@ -.jstree-node,.jstree-children,.jstree-container-ul{display:block;margin:0;padding:0;list-style-type:none;list-style-image:none}.jstree-node{white-space:nowrap}.jstree-anchor{display:inline-block;color:#000;white-space:nowrap;padding:0 4px 0 1px;margin:0;vertical-align:top}.jstree-anchor:focus{outline:0}.jstree-anchor,.jstree-anchor:link,.jstree-anchor:visited,.jstree-anchor:hover,.jstree-anchor:active{text-decoration:none;color:inherit}.jstree-icon{display:inline-block;text-decoration:none;margin:0;padding:0;vertical-align:top;text-align:center}.jstree-icon:empty{display:inline-block;text-decoration:none;margin:0;padding:0;vertical-align:top;text-align:center}.jstree-ocl{cursor:pointer}.jstree-leaf>.jstree-ocl{cursor:default}.jstree .jstree-open>.jstree-children{display:block}.jstree .jstree-closed>.jstree-children,.jstree .jstree-leaf>.jstree-children{display:none}.jstree-anchor>.jstree-themeicon{margin-right:2px}.jstree-no-icons .jstree-themeicon,.jstree-anchor>.jstree-themeicon-hidden{display:none}.jstree-rtl .jstree-anchor{padding:0 1px 0 4px}.jstree-rtl .jstree-anchor>.jstree-themeicon{margin-left:2px;margin-right:0}.jstree-rtl .jstree-node{margin-left:0}.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0}.jstree-wholerow-ul{position:relative;display:inline-block;min-width:100%}.jstree-wholerow-ul .jstree-leaf>.jstree-ocl{cursor:pointer}.jstree-wholerow-ul .jstree-anchor,.jstree-wholerow-ul .jstree-icon{position:relative}.jstree-wholerow-ul .jstree-wholerow{width:100%;cursor:pointer;position:absolute;left:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vakata-context{display:none}.vakata-context,.vakata-context ul{margin:0;padding:2px;position:absolute;background:#f5f5f5;border:1px solid #979797;-moz-box-shadow:5px 5px 4px -4px #666;-webkit-box-shadow:2px 2px 2px #999;box-shadow:2px 2px 2px #999}.vakata-context ul{list-style:none;left:100%;margin-top:-2.7em;margin-left:-4px}.vakata-context .vakata-context-right ul{left:auto;right:100%;margin-left:auto;margin-right:-4px}.vakata-context li{list-style:none;display:inline}.vakata-context li>a{display:block;padding:0 2em;text-decoration:none;width:auto;color:#000;white-space:nowrap;line-height:2.4em;-moz-text-shadow:1px 1px 0 #fff;-webkit-text-shadow:1px 1px 0 #fff;text-shadow:1px 1px 0 #fff;-moz-border-radius:1px;-webkit-border-radius:1px;border-radius:1px}.vakata-context li>a:hover{position:relative;background-color:#e8eff7;-moz-box-shadow:0 0 2px #0a6aa1;-webkit-box-shadow:0 0 2px #0a6aa1;box-shadow:0 0 2px #0a6aa1}.vakata-context li>a.vakata-context-parent{background-image:url(data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAIORI4JlrqN1oMSnmmZDQUAOw==);background-position:right center;background-repeat:no-repeat}.vakata-context li>a:focus{outline:0}.vakata-context .vakata-context-hover>a{position:relative;background-color:#e8eff7;-moz-box-shadow:0 0 2px #0a6aa1;-webkit-box-shadow:0 0 2px #0a6aa1;box-shadow:0 0 2px #0a6aa1}.vakata-context .vakata-context-separator>a,.vakata-context .vakata-context-separator>a:hover{background:#fff;border:0;border-top:1px solid #e2e3e3;height:1px;min-height:1px;max-height:1px;padding:0;margin:0 0 0 2.4em;border-left:1px solid #e0e0e0;-moz-text-shadow:0 0 0 transparent;-webkit-text-shadow:0 0 0 transparent;text-shadow:0 0 0 transparent;-moz-box-shadow:0 0 0 transparent;-webkit-box-shadow:0 0 0 transparent;box-shadow:0 0 0 transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.vakata-context .vakata-contextmenu-disabled a,.vakata-context .vakata-contextmenu-disabled a:hover{color:silver;background-color:transparent;border:0;box-shadow:0 0 0}.vakata-context li>a>i{text-decoration:none;display:inline-block;width:2.4em;height:2.4em;background:0 0;margin:0 0 0 -2em;vertical-align:top;text-align:center;line-height:2.4em}.vakata-context li>a>i:empty{width:2.4em;line-height:2.4em}.vakata-context li>a .vakata-contextmenu-sep{display:inline-block;width:1px;height:2.4em;background:#fff;margin:0 .5em 0 0;border-left:1px solid #e2e3e3}.vakata-context .vakata-contextmenu-shortcut{font-size:.8em;color:silver;opacity:.5;display:none}.vakata-context-rtl ul{left:auto;right:100%;margin-left:auto;margin-right:-4px}.vakata-context-rtl li>a.vakata-context-parent{background-image:url(data:image/gif;base64,R0lGODlhCwAHAIAAACgoKP///yH5BAEAAAEALAAAAAALAAcAAAINjI+AC7rWHIsPtmoxLAA7);background-position:left center;background-repeat:no-repeat}.vakata-context-rtl .vakata-context-separator>a{margin:0 2.4em 0 0;border-left:0;border-right:1px solid #e2e3e3}.vakata-context-rtl .vakata-context-left ul{right:auto;left:100%;margin-left:-4px;margin-right:auto}.vakata-context-rtl li>a>i{margin:0 -2em 0 0}.vakata-context-rtl li>a .vakata-contextmenu-sep{margin:0 0 0 .5em;border-left-color:#fff;background:#e2e3e3}#jstree-marker{position:absolute;top:0;left:0;margin:-5px 0 0 0;padding:0;border-right:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid;width:0;height:0;font-size:0;line-height:0}#jstree-dnd{line-height:16px;margin:0;padding:4px}#jstree-dnd .jstree-icon,#jstree-dnd .jstree-copy{display:inline-block;text-decoration:none;margin:0 2px 0 0;padding:0;width:16px;height:16px}#jstree-dnd .jstree-ok{background:green}#jstree-dnd .jstree-er{background:red}#jstree-dnd .jstree-copy{margin:0 2px}.jstree-default .jstree-node,.jstree-default .jstree-icon{background-repeat:no-repeat;background-color:transparent}.jstree-default .jstree-anchor,.jstree-default .jstree-wholerow{transition:background-color .15s,box-shadow .15s}.jstree-default .jstree-hovered{background:#e7f4f9;border-radius:2px;box-shadow:inset 0 0 1px #ccc}.jstree-default .jstree-clicked{background:#beebff;border-radius:2px;box-shadow:inset 0 0 1px #999}.jstree-default .jstree-no-icons .jstree-anchor>.jstree-themeicon{display:none}.jstree-default .jstree-disabled{background:0 0;color:#666}.jstree-default .jstree-disabled.jstree-hovered{background:0 0;box-shadow:none}.jstree-default .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default .jstree-disabled>.jstree-icon{opacity:.8;filter:url("data:image/svg+xml;utf8,#jstree-grayscale");filter:gray;-webkit-filter:grayscale(100%)}.jstree-default .jstree-search{font-style:italic;color:#8b0000;font-weight:700}.jstree-default .jstree-no-checkboxes .jstree-checkbox{display:none!important}.jstree-default.jstree-checkbox-no-clicked .jstree-clicked{background:0 0;box-shadow:none}.jstree-default.jstree-checkbox-no-clicked .jstree-clicked.jstree-hovered{background:#e7f4f9}.jstree-default.jstree-checkbox-no-clicked>.jstree-wholerow-ul .jstree-wholerow-clicked{background:0 0}.jstree-default.jstree-checkbox-no-clicked>.jstree-wholerow-ul .jstree-wholerow-clicked.jstree-wholerow-hovered{background:#e7f4f9}.jstree-default>.jstree-striped{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAMAAAB/qqA+AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMNAMM9s3UAAAAXSURBVHjajcEBAQAAAIKg/H/aCQZ70AUBjAATb6YPDgAAAABJRU5ErkJggg==) left top repeat}.jstree-default>.jstree-wholerow-ul .jstree-hovered,.jstree-default>.jstree-wholerow-ul .jstree-clicked{background:0 0;box-shadow:none;border-radius:0}.jstree-default .jstree-wholerow{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.jstree-default .jstree-wholerow-hovered{background:#e7f4f9}.jstree-default .jstree-wholerow-clicked{background:#beebff;background:-moz-linear-gradient(top,#beebff 0,#a8e4ff 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#beebff),color-stop(100%,#a8e4ff));background:-webkit-linear-gradient(top,#beebff 0,#a8e4ff 100%);background:-o-linear-gradient(top,#beebff 0,#a8e4ff 100%);background:-ms-linear-gradient(top,#beebff 0,#a8e4ff 100%);background:linear-gradient(to bottom,#beebff 0,#a8e4ff 100%)}.jstree-default .jstree-node{min-height:24px;line-height:24px;margin-left:24px;min-width:24px}.jstree-default .jstree-anchor{line-height:24px;height:24px}.jstree-default .jstree-icon{width:24px;height:24px;line-height:24px}.jstree-default .jstree-icon:empty{width:24px;height:24px;line-height:24px}.jstree-default.jstree-rtl .jstree-node{margin-right:24px}.jstree-default .jstree-wholerow{height:24px}.jstree-default .jstree-node,.jstree-default .jstree-icon{background-image:url(32px.png)}.jstree-default .jstree-node{background-position:-292px -4px;background-repeat:repeat-y}.jstree-default .jstree-last{background:0 0}.jstree-default .jstree-open>.jstree-ocl{background-position:-132px -4px}.jstree-default .jstree-closed>.jstree-ocl{background-position:-100px -4px}.jstree-default .jstree-leaf>.jstree-ocl{background-position:-68px -4px}.jstree-default .jstree-themeicon{background-position:-260px -4px}.jstree-default>.jstree-no-dots .jstree-node,.jstree-default>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-36px -4px}.jstree-default>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-4px -4px}.jstree-default .jstree-disabled{background:0 0}.jstree-default .jstree-disabled.jstree-hovered{background:0 0}.jstree-default .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default .jstree-checkbox{background-position:-164px -4px}.jstree-default .jstree-checkbox:hover{background-position:-164px -36px}.jstree-default.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default .jstree-checked>.jstree-checkbox{background-position:-228px -4px}.jstree-default.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default .jstree-checked>.jstree-checkbox:hover{background-position:-228px -36px}.jstree-default .jstree-anchor>.jstree-undetermined{background-position:-196px -4px}.jstree-default .jstree-anchor>.jstree-undetermined:hover{background-position:-196px -36px}.jstree-default>.jstree-striped{background-size:auto 48px}.jstree-default.jstree-rtl .jstree-node{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==);background-position:100% 1px;background-repeat:repeat-y}.jstree-default.jstree-rtl .jstree-last{background:0 0}.jstree-default.jstree-rtl .jstree-open>.jstree-ocl{background-position:-132px -36px}.jstree-default.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-100px -36px}.jstree-default.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-68px -36px}.jstree-default.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-36px -36px}.jstree-default.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-4px -36px}.jstree-default .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url(throbber.gif) center center no-repeat}.jstree-default .jstree-file{background:url(32px.png) -100px -68px no-repeat}.jstree-default .jstree-folder{background:url(32px.png) -260px -4px no-repeat}.jstree-default>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default{line-height:24px;padding:0 4px}#jstree-dnd.jstree-default .jstree-ok,#jstree-dnd.jstree-default .jstree-er{background-image:url(32px.png);background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default i{background:0 0;width:24px;height:24px;line-height:24px}#jstree-dnd.jstree-default .jstree-ok{background-position:-4px -68px}#jstree-dnd.jstree-default .jstree-er{background-position:-36px -68px}.jstree-default.jstree-rtl .jstree-node{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==)}.jstree-default.jstree-rtl .jstree-last{background:0 0}.jstree-default-small .jstree-node{min-height:18px;line-height:18px;margin-left:18px;min-width:18px}.jstree-default-small .jstree-anchor{line-height:18px;height:18px}.jstree-default-small .jstree-icon{width:18px;height:18px;line-height:18px}.jstree-default-small .jstree-icon:empty{width:18px;height:18px;line-height:18px}.jstree-default-small.jstree-rtl .jstree-node{margin-right:18px}.jstree-default-small .jstree-wholerow{height:18px}.jstree-default-small .jstree-node,.jstree-default-small .jstree-icon{background-image:url(32px.png)}.jstree-default-small .jstree-node{background-position:-295px -7px;background-repeat:repeat-y}.jstree-default-small .jstree-last{background:0 0}.jstree-default-small .jstree-open>.jstree-ocl{background-position:-135px -7px}.jstree-default-small .jstree-closed>.jstree-ocl{background-position:-103px -7px}.jstree-default-small .jstree-leaf>.jstree-ocl{background-position:-71px -7px}.jstree-default-small .jstree-themeicon{background-position:-263px -7px}.jstree-default-small>.jstree-no-dots .jstree-node,.jstree-default-small>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default-small>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-39px -7px}.jstree-default-small>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-7px -7px}.jstree-default-small .jstree-disabled{background:0 0}.jstree-default-small .jstree-disabled.jstree-hovered{background:0 0}.jstree-default-small .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-small .jstree-checkbox{background-position:-167px -7px}.jstree-default-small .jstree-checkbox:hover{background-position:-167px -39px}.jstree-default-small.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-small .jstree-checked>.jstree-checkbox{background-position:-231px -7px}.jstree-default-small.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-small .jstree-checked>.jstree-checkbox:hover{background-position:-231px -39px}.jstree-default-small .jstree-anchor>.jstree-undetermined{background-position:-199px -7px}.jstree-default-small .jstree-anchor>.jstree-undetermined:hover{background-position:-199px -39px}.jstree-default-small>.jstree-striped{background-size:auto 36px}.jstree-default-small.jstree-rtl .jstree-node{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==);background-position:100% 1px;background-repeat:repeat-y}.jstree-default-small.jstree-rtl .jstree-last{background:0 0}.jstree-default-small.jstree-rtl .jstree-open>.jstree-ocl{background-position:-135px -39px}.jstree-default-small.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-103px -39px}.jstree-default-small.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-71px -39px}.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-39px -39px}.jstree-default-small.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:-7px -39px}.jstree-default-small .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-small>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url(throbber.gif) center center no-repeat}.jstree-default-small .jstree-file{background:url(32px.png) -103px -71px no-repeat}.jstree-default-small .jstree-folder{background:url(32px.png) -263px -7px no-repeat}.jstree-default-small>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-small{line-height:18px;padding:0 4px}#jstree-dnd.jstree-default-small .jstree-ok,#jstree-dnd.jstree-default-small .jstree-er{background-image:url(32px.png);background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-small i{background:0 0;width:18px;height:18px;line-height:18px}#jstree-dnd.jstree-default-small .jstree-ok{background-position:-7px -71px}#jstree-dnd.jstree-default-small .jstree-er{background-position:-39px -71px}.jstree-default-small.jstree-rtl .jstree-node{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAACAQMAAABv1h6PAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMHBgAAiABBI4gz9AAAAABJRU5ErkJggg==)}.jstree-default-small.jstree-rtl .jstree-last{background:0 0}.jstree-default-large .jstree-node{min-height:32px;line-height:32px;margin-left:32px;min-width:32px}.jstree-default-large .jstree-anchor{line-height:32px;height:32px}.jstree-default-large .jstree-icon{width:32px;height:32px;line-height:32px}.jstree-default-large .jstree-icon:empty{width:32px;height:32px;line-height:32px}.jstree-default-large.jstree-rtl .jstree-node{margin-right:32px}.jstree-default-large .jstree-wholerow{height:32px}.jstree-default-large .jstree-node,.jstree-default-large .jstree-icon{background-image:url(32px.png)}.jstree-default-large .jstree-node{background-position:-288px 0;background-repeat:repeat-y}.jstree-default-large .jstree-last{background:0 0}.jstree-default-large .jstree-open>.jstree-ocl{background-position:-128px 0}.jstree-default-large .jstree-closed>.jstree-ocl{background-position:-96px 0}.jstree-default-large .jstree-leaf>.jstree-ocl{background-position:-64px 0}.jstree-default-large .jstree-themeicon{background-position:-256px 0}.jstree-default-large>.jstree-no-dots .jstree-node,.jstree-default-large>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default-large>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-32px 0}.jstree-default-large>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:0 0}.jstree-default-large .jstree-disabled{background:0 0}.jstree-default-large .jstree-disabled.jstree-hovered{background:0 0}.jstree-default-large .jstree-disabled.jstree-clicked{background:#efefef}.jstree-default-large .jstree-checkbox{background-position:-160px 0}.jstree-default-large .jstree-checkbox:hover{background-position:-160px -32px}.jstree-default-large.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-large .jstree-checked>.jstree-checkbox{background-position:-224px 0}.jstree-default-large.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-large .jstree-checked>.jstree-checkbox:hover{background-position:-224px -32px}.jstree-default-large .jstree-anchor>.jstree-undetermined{background-position:-192px 0}.jstree-default-large .jstree-anchor>.jstree-undetermined:hover{background-position:-192px -32px}.jstree-default-large>.jstree-striped{background-size:auto 64px}.jstree-default-large.jstree-rtl .jstree-node{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAACAQMAAAB49I5GAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjAAMOBgAAGAAJMwQHdQAAAABJRU5ErkJggg==);background-position:100% 1px;background-repeat:repeat-y}.jstree-default-large.jstree-rtl .jstree-last{background:0 0}.jstree-default-large.jstree-rtl .jstree-open>.jstree-ocl{background-position:-128px -32px}.jstree-default-large.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-96px -32px}.jstree-default-large.jstree-rtl .jstree-leaf>.jstree-ocl{background-position:-64px -32px}.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-node,.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-open>.jstree-ocl{background-position:-32px -32px}.jstree-default-large.jstree-rtl>.jstree-no-dots .jstree-closed>.jstree-ocl{background-position:0 -32px}.jstree-default-large .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-large>.jstree-container-ul .jstree-loading>.jstree-ocl{background:url(throbber.gif) center center no-repeat}.jstree-default-large .jstree-file{background:url(32px.png) -96px -64px no-repeat}.jstree-default-large .jstree-folder{background:url(32px.png) -256px 0 no-repeat}.jstree-default-large>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}#jstree-dnd.jstree-default-large{line-height:32px;padding:0 4px}#jstree-dnd.jstree-default-large .jstree-ok,#jstree-dnd.jstree-default-large .jstree-er{background-image:url(32px.png);background-repeat:no-repeat;background-color:transparent}#jstree-dnd.jstree-default-large i{background:0 0;width:32px;height:32px;line-height:32px}#jstree-dnd.jstree-default-large .jstree-ok{background-position:0 -64px}#jstree-dnd.jstree-default-large .jstree-er{background-position:-32px -64px}.jstree-default-large.jstree-rtl .jstree-node{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==)}.jstree-default-large.jstree-rtl .jstree-last{background:0 0}@media (max-width:768px){#jstree-dnd.jstree-dnd-responsive{line-height:40px;font-weight:700;font-size:1.1em;text-shadow:1px 1px #fff}#jstree-dnd.jstree-dnd-responsive>i{background:0 0;width:40px;height:40px}#jstree-dnd.jstree-dnd-responsive>.jstree-ok{background-image:url(40px.png);background-position:0 -200px;background-size:120px 240px}#jstree-dnd.jstree-dnd-responsive>.jstree-er{background-image:url(40px.png);background-position:-40px -200px;background-size:120px 240px}#jstree-marker.jstree-dnd-responsive{border-left-width:10px;border-top-width:10px;border-bottom-width:10px;margin-top:-10px}}@media (max-width:768px){.jstree-default-responsive .jstree-icon{background-image:url(40px.png)}.jstree-default-responsive .jstree-node,.jstree-default-responsive .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default-responsive .jstree-node{min-height:40px;line-height:40px;margin-left:40px;min-width:40px;white-space:nowrap}.jstree-default-responsive .jstree-anchor{line-height:40px;height:40px}.jstree-default-responsive .jstree-icon,.jstree-default-responsive .jstree-icon:empty{width:40px;height:40px;line-height:40px}.jstree-default-responsive>.jstree-container-ul>.jstree-node{margin-left:0}.jstree-default-responsive.jstree-rtl .jstree-node{margin-left:0;margin-right:40px}.jstree-default-responsive.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0}.jstree-default-responsive .jstree-ocl,.jstree-default-responsive .jstree-themeicon,.jstree-default-responsive .jstree-checkbox{background-size:120px 240px}.jstree-default-responsive .jstree-leaf>.jstree-ocl{background:0 0}.jstree-default-responsive .jstree-open>.jstree-ocl{background-position:0 0!important}.jstree-default-responsive .jstree-closed>.jstree-ocl{background-position:0 -40px!important}.jstree-default-responsive.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-40px 0!important}.jstree-default-responsive .jstree-themeicon{background-position:-40px -40px}.jstree-default-responsive .jstree-checkbox,.jstree-default-responsive .jstree-checkbox:hover{background-position:-40px -80px}.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-responsive .jstree-checked>.jstree-checkbox,.jstree-default-responsive .jstree-checked>.jstree-checkbox:hover{background-position:0 -80px}.jstree-default-responsive .jstree-anchor>.jstree-undetermined,.jstree-default-responsive .jstree-anchor>.jstree-undetermined:hover{background-position:0 -120px}.jstree-default-responsive .jstree-anchor{font-weight:700;font-size:1.1em;text-shadow:1px 1px #fff}.jstree-default-responsive>.jstree-striped{background:0 0}.jstree-default-responsive .jstree-wholerow{border-top:1px solid rgba(255,255,255,.7);border-bottom:1px solid rgba(64,64,64,.2);background:#ebebeb;height:40px}.jstree-default-responsive .jstree-wholerow-hovered{background:#e7f4f9}.jstree-default-responsive .jstree-wholerow-clicked{background:#beebff}.jstree-default-responsive .jstree-children .jstree-last>.jstree-wholerow{box-shadow:inset 0 -6px 3px -5px #666}.jstree-default-responsive .jstree-children .jstree-open>.jstree-wholerow{box-shadow:inset 0 6px 3px -5px #666;border-top:0}.jstree-default-responsive .jstree-children .jstree-open+.jstree-open{box-shadow:none}.jstree-default-responsive .jstree-node,.jstree-default-responsive .jstree-icon,.jstree-default-responsive .jstree-node>.jstree-ocl,.jstree-default-responsive .jstree-themeicon,.jstree-default-responsive .jstree-checkbox{background-image:url(40px.png);background-size:120px 240px}.jstree-default-responsive .jstree-node{background-position:-80px 0;background-repeat:repeat-y}.jstree-default-responsive .jstree-last{background:0 0}.jstree-default-responsive .jstree-leaf>.jstree-ocl{background-position:-40px -120px}.jstree-default-responsive .jstree-last>.jstree-ocl{background-position:-40px -160px}.jstree-default-responsive .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0}.jstree-default-responsive .jstree-file{background:url(40px.png) 0 -160px no-repeat;background-size:120px 240px}.jstree-default-responsive .jstree-folder{background:url(40px.png) -40px -40px no-repeat;background-size:120px 240px}.jstree-default-responsive>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}} \ No newline at end of file diff --git a/csv/_notes/dwsync.xml b/csv/_notes/dwsync.xml deleted file mode 100644 index 40088aa..0000000 --- a/csv/_notes/dwsync.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/csv/asdasd.txt b/csv/asdasd.txt deleted file mode 100644 index e69de29..0000000 diff --git a/csv/custom-2017_02_28_16_11_37.csv b/csv/custom-2017_02_28_16_11_37.csv deleted file mode 100644 index 1a87bfb..0000000 --- a/csv/custom-2017_02_28_16_11_37.csv +++ /dev/null @@ -1,33 +0,0 @@ -Advertiser;; -Art of Click;38;123,09 -AlliTapp;1;0 -Persona.ly;1;0 -BlackFox;1;0 -Epom Market;1;0 -Instal;1;0 -Seko Media;1;0 -Kwanko (Swelen);150;147 -Nanalab;111;766 -Ad3traffic;1;0 -Adooya Crossrider CPA;1;0 -Marvel Media;210;84 -MoBrain (Headway);130;57,62 -Buongiorno (taplinks);50;435,5 -IDB Group;2;0 -Image Perfect;1;0 -Maverick CPA;1;0 -Mobilogia;1;5,22 -Mobsai;37;127,65 -Adxperience;1;0 -JungleTap;13;9,1 -Laddez;1;0 -Trooperads;1;0 -MobChain;1;0 -Mobobeat CPI;3;5,4 -Adacts;1;0 -Glize;3;17,25 -Adstacks;1;0 -App Promoters;1;0 -Gold360;137;54,8 -Crowmobi;1;0 -Superads;1;0 diff --git a/csv/custom-2017_02_28_16_12_36.csv b/csv/custom-2017_02_28_16_12_36.csv deleted file mode 100644 index 3c3d6ba..0000000 --- a/csv/custom-2017_02_28_16_12_36.csv +++ /dev/null @@ -1,66 +0,0 @@ -Advertiser;conversions;revenue -Addictive Ads;1;0 -Digital Turbine;45;109,4 -Art of Click;76;121,95 -AlliTapp;55;86,95 -Avazu;33;29,2 -MiniMob;734;1345,06 -Persona.ly;12;13,92 -StartApp;473;637,85 -SmartLead;14;7,15 -Tracsion;122;111,03 -WinClap;217;235,77 -DCyphermedia;34;46,4 -Appave;57;68,4 -Instal;725;820,96 -Appcoach;210;162,66 -Adwool;1;0 -Eptonic;1;0 -Leadzinmarketing AFFTRACK;21;34,2 -Mobrider;15;18,88 -Layroute;2;0 -Miri Media;1;0 -Revenuemob;1;0 -Grand Oro LP;1;0 -AdsFast;2;5,55 -Applift;1494;3435,72 -MiskyAds;2;0 -Mobquid CPA;2724;3130,3 -Basebone;13;4,8 -Collectcent;189;521,6 -Click2commission CPA;20;18 -Mobipium;1;0 -Affimobiz;1;0 -CliqDigital;2;0 -Naranya CPA;9;0 -MoBrain (Headway);778;359,66 -Buongiorno (taplinks);15;168,7 -Digital Galaxy;254;1892,4 -Digital Virgo;133;146,3 -Image Perfect;12;9,6 -Neomobile;92;27,3 -Mobilogia;24;8,4 -Linkadia CPA;1;0 -Mobsai;195;669,3 -AddValue Media CPI;1;1,4 -Adxperience;463;1349,9 -Go-Rilla;17;11,4 -JungleTap;60;39,79 -Laddez;1;1,1 -MappStreet;52;45,3 -ReGaming;1;1,83 -Rekket;17;29,92 -Adstract;36;29,07 -DobleVia Latam;1;0 -Admitad;107;3834 -CPITraffic;1;1,95 -Sabia Media;1;1,82 -Zenna;12;19,7 -3DM;8;15,9 -Avazu CPA;10;240 -Ximobi;5;46,5 -Affle CPA;2;2,2 -Mobquid;11782;8594,1 -Superads;47;211,5 -Softerik;1;0 -Doozymob;1;0 diff --git a/csv/total-csv.csv b/csv/total-csv.csv deleted file mode 100644 index 2741fe3..0000000 --- a/csv/total-csv.csv +++ /dev/null @@ -1,84 +0,0 @@ -Mobquid;11782;8594,1 -Admitad;107;3834 -Applift;1494;3435,72 -Mobquid CPA;2724;3130,3 -Digital Galaxy;254;1892,4 -Adxperience;464;1349,9 -MiniMob;734;1345,06 -Instal;726;820,96 -Mobsai;232;796,95 -Nanalab;111;766 -StartApp;473;637,85 -Buongiorno (taplinks);65;604,2 -Collectcent;189;521,6 -MoBrain (Headway);908;417,28 -Art of Click;114;245,04 -Avazu CPA;10;240 -WinClap;217;235,77 -Superads;48;211,5 -Appcoach;210;162,66 -Kwanko (Swelen);150;147 -Digital Virgo;133;146,3 -Tracsion;122;111,03 -Digital Turbine;45;109,4 -AlliTapp;56;86,95 -Marvel Media;210;84 -Appave;57;68,4 -Gold360;137;54,8 -JungleTap;73;48,89 -Ximobi;5;46,5 -DCyphermedia;34;46,4 -MappStreet;52;45,3 -Leadzinmarketing AFFTRACK;21;34,2 -Rekket;17;29,92 -Avazu;33;29,2 -Adstract;36;29,07 -Neomobile;92;27,3 -Zenna;12;19,7 -Mobrider;15;18,88 -Click2commission CPA;20;18 -Glize;3;17,25 -3DM;8;15,9 -Persona.ly;13;13,92 -Mobilogia;25;13,62 -Go-Rilla;17;11,4 -Image Perfect;13;9,6 -SmartLead;14;7,15 -AdsFast;2;5,55 -Mobobeat CPI;3;5,4 -Basebone;13;4,8 -Affle CPA;2;2,2 -CPITraffic;1;1,95 -ReGaming;1;1,83 -Sabia Media;1;1,82 -AddValue Media CPI;1;1,4 -Laddez;2;1,1 -Ad3traffic;1;0 -Adacts;1;0 -Addictive Ads;1;0 -Adooya Crossrider CPA;1;0 -Adstacks;1;0 -Adwool;1;0 -Affimobiz;1;0 -App Promoters;1;0 -BlackFox;1;0 -CliqDigital;2;0 -Crowmobi;1;0 -DobleVia Latam;1;0 -Doozymob;1;0 -Epom Market;1;0 -Eptonic;1;0 -Grand Oro LP;1;0 -IDB Group;2;0 -Layroute;2;0 -Linkadia CPA;1;0 -Maverick CPA;1;0 -Miri Media;1;0 -MiskyAds;2;0 -MobChain;1;0 -Mobipium;1;0 -Naranya CPA;9;0 -Revenuemob;1;0 -Seko Media;1;0 -Softerik;1;0 -Trooperads;1;0 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..45b431c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +services: + vitrina: + build: . + ports: + - "3000:3000" + env_file: .env + environment: + DATABASE_URL: file:/data/vitrina.db + DATA_DIR: /data + STORAGE_DRIVER: local + volumes: + - vitrina-data:/data + restart: unless-stopped + +volumes: + vitrina-data: diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..6c854b4 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,93 @@ +# Architecture + +Vitrina wraps a catalog-driven generation studio with the objects an +e-commerce team actually works with — products, a brand kit, presets, +channels, campaigns — and the plumbing needed to sell it: accounts, +workspaces, credits, billing. + +``` +browser ──── server actions ────► platform API (Higgsfield) + │ │ ▲ + │ ├── credits ledger │ Authorization: Key id:secret + │ ├── run log │ (operator key or workspace BYOK) + │ └── libsql DB ───────┘ + └──── /api/upload | /api/blob ───► local disk | Vercel Blob +``` + +## Layers + +| Layer | Path | Depends on | +| --- | --- | --- | +| Domain (vertical as data) | `src/domain` | catalog types only | +| Generation engine | `src/generation` | domain (pricing), server (runs, credits, session) | +| Server services & actions | `src/server` | db, domain | +| Studio surface | `src/studio` | generation, domain, server actions | +| App pages | `src/app`, `src/ui` | everything above | + +Dependencies point downward. The domain never imports from the server or +the UI, so its tests run in plain Node and it can be swapped for another +industry without touching the rest (see `VERTICAL.md`). + +## One generation, end to end + +1. The composer holds a **job**: product, preset, channel (`src/studio/stores/job.ts`). + Picking a preset selects the model it prefers; a preset or channel writes + the model's settings (`resolveSettings`); the product's photos are attached + under the role the preset wants. +2. On Generate, the studio assembles the plane (model, media, settings) and + compiles the prompt: `compilePrompt(preset.template, { product, brand, channel, scene })`. + With no preset, the free template folds the product's appearance and the + brand's style and avoid-list around the visitor's words. +3. `submitGeneration` (server action) checks the session, resolves the key + (workspace BYOK, else operator `PLATFORM_API_KEY`), prices the run + (`creditsFor`), **reserves credits atomically**, submits to the platform, + and inserts one running row per expected result. A submit failure refunds. +4. The browser polls `getGenerationStatuses` every 4 s for every request in + flight (one round trip). The **server** settles terminal answers: rows are + re-cut to one per delivered URL, failures are recorded and refunded once + (the ledger's `(requestId, reason)` uniqueness guarantees it). A request + nobody polls for ten minutes is abandoned and refunded by the same path. +5. Tiles bloom in the grid; the viewer shows the scene, the final prompt, + product / preset / channel, and Approve / Reject. Approved runs are what a + campaign export packs, filed `brand/product/channel/preset-NN.ext`. + +## Data model + +`user`, `session`, `account`, `verification` — better-auth. +`workspace` (plan, cached credit balance, Stripe ids, sealed BYOK key) · +`membership` · `invite` (tokened links, spent once, seats per plan) · +`brand_kit` · `product` · `product_asset` · `upload` · `campaign` · `run` · +`credit_ledger` · `stripe_event`. + +A user can belong to several workspaces; the `vt_workspace` cookie names the +one on screen and `workspaceForUser` falls back to the first membership. + +`workspace.credit_balance` is a cache of `sum(credit_ledger.delta)` and is +only written inside `src/server/credits.ts`, in the same statement or batch +as the ledger row. + +## Keys and money + +- **Operator mode**: the installation's `PLATFORM_API_KEY` pays the platform; + customers pay in credits (Stripe subscriptions grant a monthly allowance). +- **BYOK**: a workspace owner pastes their own platform key; it is sealed with + AES-256-GCM under `APP_SECRET` and runs cost zero credits. +- Prices and credit grants live in `src/domain/plans.ts`; Stripe price ids in + the environment. The webhook (`/api/billing/webhook`) is idempotent by + event id. + +## Storage + +Uploads are public URLs the platform fetches. `local` writes under +`DATA_DIR/uploads//-` and serves via `/api/files` +(unauthenticated, unguessable path). `blob` issues scoped Vercel Blob client +tokens to signed-in members only. + +## Testing + +- `vitest`: domain (prompt compiler, settings resolution against the real + catalog, pricing, export paths), credits ledger races and idempotency, + Stripe event application, sealing. +- Playwright: sign-up → brand kit → product photo → studio job rail → + generate → review → refund on failure → campaign plan → export zip, all + against `scripts/mock-platform.mjs`. diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md new file mode 100644 index 0000000..3c24f32 --- /dev/null +++ b/docs/DEPLOY.md @@ -0,0 +1,66 @@ +# Deploying Vitrina + +## Environment + +Copy `.env.example` to `.env` and fill in: + +| Variable | Required | Notes | +| --- | --- | --- | +| `HF_API_BASE_URL` | yes | Origin of the generation platform API | +| `APP_SECRET` | yes | `openssl rand -base64 32`; signs sessions, seals BYOK keys | +| `APP_URL` | yes | Public origin; checkout redirects and local file URLs | +| `PLATFORM_API_KEY` | SaaS mode | Your platform key `id:secret`; leave empty to force BYOK | +| `DATABASE_URL` | no | `file:./data/vitrina.db` (default) or a libsql/Turso URL | +| `DATABASE_AUTH_TOKEN` | Turso | | +| `STORAGE_DRIVER`, `NEXT_PUBLIC_STORAGE_DRIVER` | no | `local` (default) or `blob`; set both the same | +| `BLOB_READ_WRITE_TOKEN` | blob | Vercel Blob token | +| `DATA_DIR` | local | Where the DB file and uploads live | +| `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` | billing | | +| `STRIPE_PRICE_STARTER`, `STRIPE_PRICE_PRO`, `STRIPE_PRICE_STUDIO` | billing | Recurring monthly prices | +| `RESEND_API_KEY`, `MAIL_FROM` | mail | Enables password reset by email; absent, the link is hidden | +| `SIGNUPS` | no | `closed` refuses new accounts on a private install | + +Migrations run automatically when the server boots. `pnpm start` serves the +standalone build (the same artifact the Dockerfile ships). + +## Docker (single host) + +```bash +cp .env.example .env # fill it in +docker compose up -d --build +``` + +The database and uploads live in the `vitrina-data` volume. The platform +must be able to fetch `APP_URL/api/files/...`, so `APP_URL` has to be the +public HTTPS origin, not localhost. + +Grant credits without Stripe: + +```bash +docker compose exec vitrina node scripts/credits.mjs owner@example.com 500 "welcome" +``` + +## Vercel + +1. Import the repo. Build command `pnpm build`, Node 22. +2. Database: Turso (`DATABASE_URL=libsql://…`, `DATABASE_AUTH_TOKEN`). A + file database does not survive on serverless. +3. Storage: create a Blob store, set `BLOB_READ_WRITE_TOKEN`, + `STORAGE_DRIVER=blob`, `NEXT_PUBLIC_STORAGE_DRIVER=blob`. +4. Set the remaining variables from the table. + +## Stripe + +1. Create three recurring products/prices; put their ids in + `STRIPE_PRICE_*`. Amounts should match `src/domain/plans.ts` (or edit it). +2. Add a webhook endpoint `APP_URL/api/billing/webhook` for + `checkout.session.completed`, `invoice.paid`, + `customer.subscription.updated`, `customer.subscription.deleted`; set + `STRIPE_WEBHOOK_SECRET`. +3. Locally: `stripe listen --forward-to localhost:3000/api/billing/webhook`. + +## Health + +`GET /login` answers 200 when the server is up (the Dockerfile's healthcheck +uses it). Logs are plain stdout; generation calls are logged as +`[platform] request` / `[platform] response`. diff --git a/docs/VERTICAL.md b/docs/VERTICAL.md new file mode 100644 index 0000000..b95d604 --- /dev/null +++ b/docs/VERTICAL.md @@ -0,0 +1,28 @@ +# Changing the industry + +The product is a thin, opinionated layer over a general studio. Everything +that makes it "for e-commerce" is data in `src/domain/`: + +| File | What it decides | +| --- | --- | +| `presets.ts` | The jobs the industry asks for by name — label, prompt skeleton, preferred models, settings, which media role the subject's photo takes, recommended channels | +| `channels.ts` | Where results go — ratio preferences, video lengths, composition guidance | +| `brand.ts` | What the subject and the brand are made of (schemas, categories) | +| `pricing.ts`, `plans.ts` | What a run costs and what is sold | +| `index.ts` | `VERTICAL` — the product's name and nouns | + +To ship, say, a real-estate edition: + +1. Replace the presets: "listing hero", "twilight exterior", "virtual staging", + "walkthrough clip". The template tokens stay the same + (`{{product.name}}`, `{{product.appearance}}`, `{{brand.styleLine}}`, + `{{channel.guidance}}`, `{{scene}}` …); rename what "product" means in + `VERTICAL.subjectNoun` and the schema fields in `brand.ts`. +2. Replace the channels: MLS main photo, Instagram, a YouTube tour. +3. Run `pnpm test` — the preset test proves every preset still resolves to a + real model and settings the catalog accepts. +4. Reword the landing page (`src/app/page.tsx`) and the sample prompts in + `src/studio/data.ts`. + +Nothing in `src/generation`, `src/server`, `src/studio` or billing needs to +change. diff --git a/docs/screenshots/campaign.png b/docs/screenshots/campaign.png new file mode 100644 index 0000000..de6d3ce Binary files /dev/null and b/docs/screenshots/campaign.png differ diff --git a/docs/screenshots/landing.png b/docs/screenshots/landing.png new file mode 100644 index 0000000..126d09b Binary files /dev/null and b/docs/screenshots/landing.png differ diff --git a/docs/screenshots/studio.png b/docs/screenshots/studio.png new file mode 100644 index 0000000..555b205 Binary files /dev/null and b/docs/screenshots/studio.png differ diff --git a/docs/sessions/README.md b/docs/sessions/README.md new file mode 100644 index 0000000..c4dc0a8 --- /dev/null +++ b/docs/sessions/README.md @@ -0,0 +1,3 @@ +# Session state files + +Written by the `reset-total` skill before a compact or restart. One file per day; the newest supersedes the rest. Never contains secrets. diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..5c4caca --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "drizzle-kit"; + +export default defineConfig({ + dialect: "sqlite", + schema: "./src/db/schema.ts", + out: "./drizzle", + dbCredentials: { url: process.env.DATABASE_URL ?? "file:./data/vitrina.db" }, +}); diff --git a/drizzle/0000_init.sql b/drizzle/0000_init.sql new file mode 100644 index 0000000..ace0781 --- /dev/null +++ b/drizzle/0000_init.sql @@ -0,0 +1,209 @@ +CREATE TABLE `account` ( + `id` text PRIMARY KEY NOT NULL, + `account_id` text NOT NULL, + `provider_id` text NOT NULL, + `user_id` text NOT NULL, + `access_token` text, + `refresh_token` text, + `id_token` text, + `access_token_expires_at` integer, + `refresh_token_expires_at` integer, + `scope` text, + `password` text, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `account_user_idx` ON `account` (`user_id`);--> statement-breakpoint +CREATE TABLE `brand_kit` ( + `workspace_id` text PRIMARY KEY NOT NULL, + `name` text DEFAULT '' NOT NULL, + `tagline` text DEFAULT '' NOT NULL, + `tone` text DEFAULT '' NOT NULL, + `style` text DEFAULT '' NOT NULL, + `palette` text DEFAULT '[]' NOT NULL, + `audience` text DEFAULT '' NOT NULL, + `avoid` text DEFAULT '' NOT NULL, + `logo_url` text DEFAULT '' NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `campaign` ( + `id` text PRIMARY KEY NOT NULL, + `workspace_id` text NOT NULL, + `product_id` text NOT NULL, + `name` text NOT NULL, + `scene` text DEFAULT '' NOT NULL, + `preset_ids` text DEFAULT '[]' NOT NULL, + `channel_ids` text DEFAULT '[]' NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`product_id`) REFERENCES `product`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `campaign_workspace_idx` ON `campaign` (`workspace_id`);--> statement-breakpoint +CREATE TABLE `credit_ledger` ( + `id` text PRIMARY KEY NOT NULL, + `workspace_id` text NOT NULL, + `delta` integer NOT NULL, + `reason` text NOT NULL, + `request_id` text, + `stripe_event_id` text, + `note` text, + `created_at` integer NOT NULL, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `ledger_workspace_idx` ON `credit_ledger` (`workspace_id`,`created_at`);--> statement-breakpoint +CREATE UNIQUE INDEX `ledger_request_reason_idx` ON `credit_ledger` (`request_id`,`reason`);--> statement-breakpoint +CREATE TABLE `membership` ( + `workspace_id` text NOT NULL, + `user_id` text NOT NULL, + `role` text DEFAULT 'editor' NOT NULL, + `created_at` integer NOT NULL, + PRIMARY KEY(`workspace_id`, `user_id`), + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `membership_user_idx` ON `membership` (`user_id`);--> statement-breakpoint +CREATE TABLE `product` ( + `id` text PRIMARY KEY NOT NULL, + `workspace_id` text NOT NULL, + `name` text NOT NULL, + `slug` text NOT NULL, + `category` text DEFAULT 'Other' NOT NULL, + `description` text DEFAULT '' NOT NULL, + `features` text DEFAULT '[]' NOT NULL, + `appearance` text DEFAULT '' NOT NULL, + `audience` text DEFAULT '' NOT NULL, + `price_label` text DEFAULT '' NOT NULL, + `archived_at` integer, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `product_workspace_idx` ON `product` (`workspace_id`);--> statement-breakpoint +CREATE UNIQUE INDEX `product_workspace_slug_idx` ON `product` (`workspace_id`,`slug`);--> statement-breakpoint +CREATE TABLE `product_asset` ( + `id` text PRIMARY KEY NOT NULL, + `product_id` text NOT NULL, + `url` text NOT NULL, + `kind` text DEFAULT 'image' NOT NULL, + `name` text DEFAULT '' NOT NULL, + `position` integer DEFAULT 0 NOT NULL, + `created_at` integer NOT NULL, + FOREIGN KEY (`product_id`) REFERENCES `product`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `product_asset_product_idx` ON `product_asset` (`product_id`);--> statement-breakpoint +CREATE TABLE `run` ( + `id` text PRIMARY KEY NOT NULL, + `workspace_id` text NOT NULL, + `user_id` text, + `request_id` text NOT NULL, + `product_id` text, + `campaign_id` text, + `preset_id` text, + `channel_id` text, + `surface` text NOT NULL, + `model_id` text NOT NULL, + `model_label` text NOT NULL, + `prompt` text NOT NULL, + `scene` text DEFAULT '' NOT NULL, + `ratio` text NOT NULL, + `meta` text DEFAULT '' NOT NULL, + `badge` text, + `urls` text DEFAULT '[]' NOT NULL, + `status` text NOT NULL, + `error` text, + `favorite` integer DEFAULT false NOT NULL, + `review` text DEFAULT 'pending' NOT NULL, + `settings` text DEFAULT '{}' NOT NULL, + `credits` integer DEFAULT 0 NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + `deleted_at` integer, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE set null, + FOREIGN KEY (`product_id`) REFERENCES `product`(`id`) ON UPDATE no action ON DELETE set null, + FOREIGN KEY (`campaign_id`) REFERENCES `campaign`(`id`) ON UPDATE no action ON DELETE set null +); +--> statement-breakpoint +CREATE INDEX `run_workspace_created_idx` ON `run` (`workspace_id`,`created_at`);--> statement-breakpoint +CREATE INDEX `run_request_idx` ON `run` (`request_id`);--> statement-breakpoint +CREATE INDEX `run_campaign_idx` ON `run` (`campaign_id`);--> statement-breakpoint +CREATE TABLE `session` ( + `id` text PRIMARY KEY NOT NULL, + `expires_at` integer NOT NULL, + `token` text NOT NULL, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + `ip_address` text, + `user_agent` text, + `user_id` text NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `session_token_unique` ON `session` (`token`);--> statement-breakpoint +CREATE INDEX `session_user_idx` ON `session` (`user_id`);--> statement-breakpoint +CREATE TABLE `stripe_event` ( + `id` text PRIMARY KEY NOT NULL, + `type` text NOT NULL, + `processed_at` integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE `upload` ( + `id` text PRIMARY KEY NOT NULL, + `workspace_id` text NOT NULL, + `url` text NOT NULL, + `kind` text DEFAULT 'image' NOT NULL, + `name` text DEFAULT '' NOT NULL, + `created_at` integer NOT NULL, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `upload_workspace_idx` ON `upload` (`workspace_id`);--> statement-breakpoint +CREATE TABLE `user` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `email` text NOT NULL, + `email_verified` integer DEFAULT false NOT NULL, + `image` text, + `created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL, + `updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);--> statement-breakpoint +CREATE TABLE `verification` ( + `id` text PRIMARY KEY NOT NULL, + `identifier` text NOT NULL, + `value` text NOT NULL, + `expires_at` integer NOT NULL, + `created_at` integer DEFAULT (unixepoch() * 1000) NOT NULL, + `updated_at` integer DEFAULT (unixepoch() * 1000) NOT NULL +); +--> statement-breakpoint +CREATE INDEX `verification_identifier_idx` ON `verification` (`identifier`);--> statement-breakpoint +CREATE TABLE `workspace` ( + `id` text PRIMARY KEY NOT NULL, + `name` text NOT NULL, + `slug` text NOT NULL, + `owner_id` text NOT NULL, + `plan_id` text DEFAULT 'free' NOT NULL, + `credit_balance` integer DEFAULT 0 NOT NULL, + `stripe_customer_id` text, + `stripe_subscription_id` text, + `plan_renews_at` integer, + `platform_key_sealed` text, + `created_at` integer NOT NULL, + `updated_at` integer NOT NULL, + FOREIGN KEY (`owner_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `workspace_slug_unique` ON `workspace` (`slug`);--> statement-breakpoint +CREATE INDEX `workspace_owner_idx` ON `workspace` (`owner_id`); \ No newline at end of file diff --git a/drizzle/0001_invites.sql b/drizzle/0001_invites.sql new file mode 100644 index 0000000..d557d8e --- /dev/null +++ b/drizzle/0001_invites.sql @@ -0,0 +1,17 @@ +CREATE TABLE `invite` ( + `id` text PRIMARY KEY NOT NULL, + `workspace_id` text NOT NULL, + `token` text NOT NULL, + `role` text DEFAULT 'editor' NOT NULL, + `created_by` text, + `expires_at` integer NOT NULL, + `accepted_by` text, + `accepted_at` integer, + `created_at` integer NOT NULL, + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`created_by`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE set null, + FOREIGN KEY (`accepted_by`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE set null +); +--> statement-breakpoint +CREATE UNIQUE INDEX `invite_token_unique` ON `invite` (`token`);--> statement-breakpoint +CREATE INDEX `invite_workspace_idx` ON `invite` (`workspace_id`); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..ce06d2d --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,1488 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "d9431c1a-0bb1-4e02-bf20-016d69723266", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "account_user_idx": { + "name": "account_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "brand_kit": { + "name": "brand_kit", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "tagline": { + "name": "tagline", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "tone": { + "name": "tone", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "style": { + "name": "style", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "palette": { + "name": "palette", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "avoid": { + "name": "avoid", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "brand_kit_workspace_id_workspace_id_fk": { + "name": "brand_kit_workspace_id_workspace_id_fk", + "tableFrom": "brand_kit", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "campaign": { + "name": "campaign", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scene": { + "name": "scene", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "preset_ids": { + "name": "preset_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "channel_ids": { + "name": "channel_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "campaign_workspace_idx": { + "name": "campaign_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "campaign_workspace_id_workspace_id_fk": { + "name": "campaign_workspace_id_workspace_id_fk", + "tableFrom": "campaign", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "campaign_product_id_product_id_fk": { + "name": "campaign_product_id_product_id_fk", + "tableFrom": "campaign", + "tableTo": "product", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "credit_ledger": { + "name": "credit_ledger", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delta": { + "name": "delta", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "ledger_workspace_idx": { + "name": "ledger_workspace_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "ledger_request_reason_idx": { + "name": "ledger_request_reason_idx", + "columns": [ + "request_id", + "reason" + ], + "isUnique": true + } + }, + "foreignKeys": { + "credit_ledger_workspace_id_workspace_id_fk": { + "name": "credit_ledger_workspace_id_workspace_id_fk", + "tableFrom": "credit_ledger", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "membership": { + "name": "membership", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'editor'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "membership_user_idx": { + "name": "membership_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "membership_workspace_id_workspace_id_fk": { + "name": "membership_workspace_id_workspace_id_fk", + "tableFrom": "membership", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "membership_user_id_user_id_fk": { + "name": "membership_user_id_user_id_fk", + "tableFrom": "membership", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "membership_workspace_id_user_id_pk": { + "columns": [ + "workspace_id", + "user_id" + ], + "name": "membership_workspace_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "product": { + "name": "product", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Other'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "features": { + "name": "features", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "appearance": { + "name": "appearance", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "price_label": { + "name": "price_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "product_workspace_idx": { + "name": "product_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "product_workspace_slug_idx": { + "name": "product_workspace_slug_idx", + "columns": [ + "workspace_id", + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "product_workspace_id_workspace_id_fk": { + "name": "product_workspace_id_workspace_id_fk", + "tableFrom": "product", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "product_asset": { + "name": "product_asset", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'image'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "product_asset_product_idx": { + "name": "product_asset_product_idx", + "columns": [ + "product_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "product_asset_product_id_product_id_fk": { + "name": "product_asset_product_id_product_id_fk", + "tableFrom": "product_asset", + "tableTo": "product", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "run": { + "name": "run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "campaign_id": { + "name": "campaign_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "preset_id": { + "name": "preset_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model_id": { + "name": "model_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model_label": { + "name": "model_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scene": { + "name": "scene", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "ratio": { + "name": "ratio", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "badge": { + "name": "badge", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "urls": { + "name": "urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "favorite": { + "name": "favorite", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "review": { + "name": "review", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "settings": { + "name": "settings", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "credits": { + "name": "credits", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "run_workspace_created_idx": { + "name": "run_workspace_created_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "run_request_idx": { + "name": "run_request_idx", + "columns": [ + "request_id" + ], + "isUnique": false + }, + "run_campaign_idx": { + "name": "run_campaign_idx", + "columns": [ + "campaign_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "run_workspace_id_workspace_id_fk": { + "name": "run_workspace_id_workspace_id_fk", + "tableFrom": "run", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "run_user_id_user_id_fk": { + "name": "run_user_id_user_id_fk", + "tableFrom": "run", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "run_product_id_product_id_fk": { + "name": "run_product_id_product_id_fk", + "tableFrom": "run", + "tableTo": "product", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "run_campaign_id_campaign_id_fk": { + "name": "run_campaign_id_campaign_id_fk", + "tableFrom": "run", + "tableTo": "campaign", + "columnsFrom": [ + "campaign_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "session_user_idx": { + "name": "session_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "stripe_event": { + "name": "stripe_event", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "processed_at": { + "name": "processed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "upload": { + "name": "upload", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'image'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "upload_workspace_idx": { + "name": "upload_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "upload_workspace_id_workspace_id_fk": { + "name": "upload_workspace_id_workspace_id_fk", + "tableFrom": "upload", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspace": { + "name": "workspace", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'free'" + }, + "credit_balance": { + "name": "credit_balance", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_renews_at": { + "name": "plan_renews_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "platform_key_sealed": { + "name": "platform_key_sealed", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "workspace_slug_unique": { + "name": "workspace_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "workspace_owner_idx": { + "name": "workspace_owner_idx", + "columns": [ + "owner_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000..2fae251 --- /dev/null +++ b/drizzle/meta/0001_snapshot.json @@ -0,0 +1,1617 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "7d6b00bc-7b80-4c01-a74d-9b0c6e94bc8d", + "prevId": "d9431c1a-0bb1-4e02-bf20-016d69723266", + "tables": { + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "account_user_idx": { + "name": "account_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "brand_kit": { + "name": "brand_kit", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "tagline": { + "name": "tagline", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "tone": { + "name": "tone", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "style": { + "name": "style", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "palette": { + "name": "palette", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "avoid": { + "name": "avoid", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "logo_url": { + "name": "logo_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "brand_kit_workspace_id_workspace_id_fk": { + "name": "brand_kit_workspace_id_workspace_id_fk", + "tableFrom": "brand_kit", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "campaign": { + "name": "campaign", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scene": { + "name": "scene", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "preset_ids": { + "name": "preset_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "channel_ids": { + "name": "channel_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "campaign_workspace_idx": { + "name": "campaign_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "campaign_workspace_id_workspace_id_fk": { + "name": "campaign_workspace_id_workspace_id_fk", + "tableFrom": "campaign", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "campaign_product_id_product_id_fk": { + "name": "campaign_product_id_product_id_fk", + "tableFrom": "campaign", + "tableTo": "product", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "credit_ledger": { + "name": "credit_ledger", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delta": { + "name": "delta", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_event_id": { + "name": "stripe_event_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "ledger_workspace_idx": { + "name": "ledger_workspace_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "ledger_request_reason_idx": { + "name": "ledger_request_reason_idx", + "columns": [ + "request_id", + "reason" + ], + "isUnique": true + } + }, + "foreignKeys": { + "credit_ledger_workspace_id_workspace_id_fk": { + "name": "credit_ledger_workspace_id_workspace_id_fk", + "tableFrom": "credit_ledger", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invite": { + "name": "invite", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'editor'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accepted_by": { + "name": "accepted_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invite_token_unique": { + "name": "invite_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "invite_workspace_idx": { + "name": "invite_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invite_workspace_id_workspace_id_fk": { + "name": "invite_workspace_id_workspace_id_fk", + "tableFrom": "invite", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invite_created_by_user_id_fk": { + "name": "invite_created_by_user_id_fk", + "tableFrom": "invite", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "invite_accepted_by_user_id_fk": { + "name": "invite_accepted_by_user_id_fk", + "tableFrom": "invite", + "tableTo": "user", + "columnsFrom": [ + "accepted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "membership": { + "name": "membership", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'editor'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "membership_user_idx": { + "name": "membership_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "membership_workspace_id_workspace_id_fk": { + "name": "membership_workspace_id_workspace_id_fk", + "tableFrom": "membership", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "membership_user_id_user_id_fk": { + "name": "membership_user_id_user_id_fk", + "tableFrom": "membership", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "membership_workspace_id_user_id_pk": { + "columns": [ + "workspace_id", + "user_id" + ], + "name": "membership_workspace_id_user_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "product": { + "name": "product", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Other'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "features": { + "name": "features", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "appearance": { + "name": "appearance", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "audience": { + "name": "audience", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "price_label": { + "name": "price_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "archived_at": { + "name": "archived_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "product_workspace_idx": { + "name": "product_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "product_workspace_slug_idx": { + "name": "product_workspace_slug_idx", + "columns": [ + "workspace_id", + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "product_workspace_id_workspace_id_fk": { + "name": "product_workspace_id_workspace_id_fk", + "tableFrom": "product", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "product_asset": { + "name": "product_asset", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'image'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "product_asset_product_idx": { + "name": "product_asset_product_idx", + "columns": [ + "product_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "product_asset_product_id_product_id_fk": { + "name": "product_asset_product_id_product_id_fk", + "tableFrom": "product_asset", + "tableTo": "product", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "run": { + "name": "run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "product_id": { + "name": "product_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "campaign_id": { + "name": "campaign_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "preset_id": { + "name": "preset_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel_id": { + "name": "channel_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model_id": { + "name": "model_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "model_label": { + "name": "model_label", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scene": { + "name": "scene", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "ratio": { + "name": "ratio", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "badge": { + "name": "badge", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "urls": { + "name": "urls", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "favorite": { + "name": "favorite", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "review": { + "name": "review", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "settings": { + "name": "settings", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "credits": { + "name": "credits", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "run_workspace_created_idx": { + "name": "run_workspace_created_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "run_request_idx": { + "name": "run_request_idx", + "columns": [ + "request_id" + ], + "isUnique": false + }, + "run_campaign_idx": { + "name": "run_campaign_idx", + "columns": [ + "campaign_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "run_workspace_id_workspace_id_fk": { + "name": "run_workspace_id_workspace_id_fk", + "tableFrom": "run", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "run_user_id_user_id_fk": { + "name": "run_user_id_user_id_fk", + "tableFrom": "run", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "run_product_id_product_id_fk": { + "name": "run_product_id_product_id_fk", + "tableFrom": "run", + "tableTo": "product", + "columnsFrom": [ + "product_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "run_campaign_id_campaign_id_fk": { + "name": "run_campaign_id_campaign_id_fk", + "tableFrom": "run", + "tableTo": "campaign", + "columnsFrom": [ + "campaign_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "session_user_idx": { + "name": "session_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "stripe_event": { + "name": "stripe_event", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "processed_at": { + "name": "processed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "upload": { + "name": "upload", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'image'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "upload_workspace_idx": { + "name": "upload_workspace_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "upload_workspace_id_workspace_id_fk": { + "name": "upload_workspace_id_workspace_id_fk", + "tableFrom": "upload", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(unixepoch() * 1000)" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspace": { + "name": "workspace", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "plan_id": { + "name": "plan_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'free'" + }, + "credit_balance": { + "name": "credit_balance", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan_renews_at": { + "name": "plan_renews_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "platform_key_sealed": { + "name": "platform_key_sealed", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "workspace_slug_unique": { + "name": "workspace_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "workspace_owner_idx": { + "name": "workspace_owner_idx", + "columns": [ + "owner_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..fb7adbb --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,20 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1789690768798, + "tag": "0000_init", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1789693351102, + "tag": "0001_invites", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/e2e/studio.spec.ts b/e2e/studio.spec.ts new file mode 100644 index 0000000..4f1ebed --- /dev/null +++ b/e2e/studio.spec.ts @@ -0,0 +1,196 @@ +import { expect, test, type Page } from "@playwright/test"; + +const PNG = Buffer.from( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", + "base64", +); + +const email = `ada-${Date.now()}@example.com`; +const password = "correct-horse-battery"; + +async function register(page: Page) { + await page.goto("/register"); + await page.getByLabel("Your name").fill("Ada"); + await page.getByLabel("Email").fill(email); + await page.getByLabel("Password").fill(password); + await page.getByRole("button", { name: "Create account" }).click(); + await expect(page).toHaveURL(/\/app$/); +} + +/** Signs the shared account in, creating it when a test runs on its own. */ +async function signIn(page: Page) { + await page.goto("/login"); + await page.getByLabel("Email").fill(email); + await page.getByLabel("Password").fill(password); + await page.getByRole("button", { name: "Sign in" }).click(); + await page.waitForTimeout(500); + if (!/\/app$/.test(page.url())) await register(page); + await expect(page).toHaveURL(/\/app$/); +} + +async function ensureProduct(page: Page) { + await page.goto("/app/products"); + if (await page.getByRole("link", { name: /Aurora ceramic mug/ }).count()) return; + await page.goto("/app/products/new"); + await page.getByLabel("Name").fill("Aurora ceramic mug"); + await page.getByRole("button", { name: "Create product" }).click(); + await expect(page).toHaveURL(/\/app\/products\/[0-9a-f-]+$/); +} + +test.describe.configure({ mode: "serial" }); + +test("sign up lands in the studio with trial credits", async ({ page }) => { + await register(page); + await expect(page).toHaveURL(/\/app$/); + await expect(page.getByRole("heading", { name: /product images land here/i })).toBeVisible(); + await expect(page.locator(".vt-credits-n")).toHaveText("30"); +}); + +test("a product with a photo, a brand kit, and a generated pack shot", async ({ page }) => { + await signIn(page); + + /* Brand kit. */ + await page.goto("/app/brand"); + await page.getByLabel("Brand name").fill("Aurora Home"); + await page.getByLabel("Visual style").fill("minimal scandinavian, soft daylight"); + await page.getByLabel("Never show").fill("text, clutter"); + await page.getByRole("button", { name: "Save brand kit" }).click(); + await expect(page.getByText("Saved")).toBeVisible(); + + /* Product. */ + await page.goto("/app/products/new"); + await page.getByLabel("Name").fill("Aurora ceramic mug"); + await page.getByLabel("Category").selectOption("Home & furniture"); + await page.getByLabel("How it looks").fill("matte sage green glaze, speckled"); + await page.getByRole("button", { name: "Create product" }).click(); + await expect(page).toHaveURL(/\/app\/products\/[0-9a-f-]+$/); + await page.locator('.vt-photo--add input[type="file"]').setInputFiles({ name: "mug.png", mimeType: "image/png", buffer: PNG }); + await expect(page.locator(".vt-photo img")).toHaveCount(1); + await expect(page.locator(".vt-photo img")).toHaveAttribute("src", /\/api\/files\//); + + /* Studio: product + preset, then generate. */ + await page.goto("/app"); + await page.getByRole("tab", { name: "Image" }).click(); + await page.getByRole("button", { name: "Change product" }).click(); + await page.getByRole("button", { name: /Aurora ceramic mug/ }).click(); + await page.getByRole("button", { name: "Change preset" }).click(); + await page.getByRole("button", { name: /^Pack shot/ }).click(); + await expect(page.locator(".vt-jobpill-value", { hasText: "Pack shot" })).toBeVisible(); + await expect(page.locator(".vt-strip-item")).toHaveCount(1); + + await page.getByRole("button", { name: "Show final prompt" }).click(); + await expect(page.locator(".vt-preview-text")).toContainText("Professional e-commerce product photograph of Aurora ceramic mug"); + await expect(page.locator(".vt-preview-text")).toContainText("Avoid: text, clutter."); + + await page.getByRole("textbox", { name: "Prompt" }).fill("front three-quarter view"); + await page.getByRole("button", { name: /^Generate/ }).click(); + await expect(page.locator(".vt-skeleton")).toHaveCount(1); + await expect(page.locator(".vt-tile-media")).toHaveCount(1, { timeout: 30_000 }); + await expect(page.locator(".vt-tile-media")).toHaveAttribute("src", /localhost:4110\/media\//); + /* Flux at 2k costs 2 credits. */ + await expect(page.locator(".vt-credits-n")).toHaveText("28"); + + /* Approve from the viewer; the tile carries the mark. */ + await page.locator(".vt-tile-open").click(); + const viewer = page.locator(".vt-viewer"); + await viewer.getByRole("button", { name: "Approve", exact: true }).click(); + await expect(viewer.getByRole("button", { name: "Approved", exact: true })).toBeVisible(); + await viewer.getByRole("button", { name: "Close" }).click(); + await expect(page.locator(".vt-tile-review[data-review='approved']")).toHaveCount(1); + + /* Survives a reload: the run log lives on the server. */ + await page.reload(); + await expect(page.locator(".vt-tile-media")).toHaveCount(1); + await expect(page.locator(".vt-credits-n")).toHaveText("28"); + + /* A failing run is refunded. */ + await page.getByRole("textbox", { name: "Prompt" }).fill("MOCK_FAIL this one"); + await page.getByRole("button", { name: /^Generate/ }).click(); + await expect(page.locator(".vt-tile--failed")).toHaveCount(1, { timeout: 30_000 }); + await expect(page.locator(".vt-credits-n")).toHaveText("28"); +}); + +test("a campaign generates its plan and exports approved files", async ({ page }) => { + await signIn(page); + await ensureProduct(page); + + await page.goto("/app/campaigns/new"); + await page.getByLabel("Campaign name").fill("Spring launch"); + await page.getByRole("textbox", { name: /^Scene/ }).fill("a bright kitchen at breakfast"); + /* Defaults: pack shot + lifestyle, instagram feed + amazon → 4 runs. */ + await expect(page.locator(".vt-form-note")).toContainText("4 runs"); + await page.getByRole("button", { name: "Create campaign" }).click(); + await expect(page).toHaveURL(/\/app\/campaigns\/[0-9a-f-]+$/); + await expect(page.locator(".vt-pair")).toHaveCount(4); + + await page.getByRole("button", { name: /^Generate 4 runs/ }).click(); + await expect(page.locator(".vt-result[data-status='completed']")).toHaveCount(4, { timeout: 45_000 }); + await expect(page.locator(".vt-channel-title", { hasText: "Amazon main image" })).toBeVisible(); + + const approve = page.locator(".vt-result[data-status='completed']").first().getByRole("button", { name: "Approve", exact: true }); + await approve.click(); + await expect(page.getByRole("button", { name: /Export 1 approved/ })).toBeEnabled(); + + const download = page.waitForEvent("download"); + await page.getByRole("button", { name: /Export 1 approved/ }).click(); + const file = await download; + expect(file.suggestedFilename()).toBe("spring-launch-export.zip"); + + /* The settings page shows the ledger with the campaign's debits. */ + await page.goto("/app/settings"); + await expect(page.locator(".vt-table tbody tr").first()).toContainText("Generation"); +}); + +test("signed-out visitors are sent to sign in from every app page", async ({ page }) => { + for (const path of ["/app", "/app/products", "/app/campaigns/new", "/app/settings"]) { + await page.goto(path); + await expect(page).toHaveURL(/\/login$/); + } +}); + +test("an invite link seats a second person, and the owner can switch workspaces", async ({ page, browser }) => { + await signIn(page); + + /* The trial plan has one seat; a link cannot be minted until there is room. */ + await page.goto("/app/settings"); + await expect(page.getByRole("button", { name: "New invite link" })).toBeDisabled(); + + /* Seats come with a plan. The e2e database has no Stripe, so the plan is + flipped by hand the way the webhook would. */ + const { createClient } = await import("@libsql/client"); + const client = createClient({ url: `file:${process.cwd()}/data/e2e/vitrina.db` }); + await client.execute({ sql: "update workspace set plan_id = 'starter' where owner_id = (select id from user where email = ?)", args: [email] }); + client.close(); + + await page.reload(); + await page.getByRole("button", { name: "New invite link" }).click(); + const link = await page.locator(".vt-member-link").first().textContent(); + expect(link).toMatch(/\/join\//); + + /* Somebody else opens the link, signs up, and lands in the owner's workspace. */ + const guest = await browser.newContext(); + const guestPage = await guest.newPage(); + await guestPage.goto(link!); + await expect(guestPage).toHaveURL(/\/register\?next=/); + await guestPage.getByLabel("Your name").fill("Bob"); + await guestPage.getByLabel("Email").fill(`bob-${Date.now()}@example.com`); + await guestPage.getByLabel("Password").fill(password); + await guestPage.getByRole("button", { name: "Create account" }).click(); + await expect(guestPage).toHaveURL(/\/app$/); + await expect(guestPage.locator(".vt-side-ws-select")).toBeVisible(); + await expect(guestPage.locator(".vt-side-ws-select option:checked")).toHaveText("Aurora Home"); + await guest.close(); + + await page.reload(); + await expect(page.locator(".vt-member-name", { hasText: "Bob" })).toBeVisible(); + await expect(page.getByText("2 of 2 seats used")).toBeVisible(); + + /* A second brand under the same account, then back. */ + await page.getByPlaceholder("New brand name").fill("Second brand"); + await page.getByRole("button", { name: "Create workspace" }).click(); + await expect(page).toHaveURL(/\/app\/brand$/); + await expect(page.locator(".vt-side-ws-select option:checked")).toHaveText("Second brand"); + await page.locator(".vt-side-ws-select").selectOption({ label: "Aurora Home" }); + await expect(page).toHaveURL(/\/app$/); + await expect(page.locator(".vt-side-ws-select option:checked")).toHaveText("Aurora Home"); +}); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..d8788e3 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,16 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +export default defineConfig([ + ...nextVitals, + ...nextTs, + globalIgnores([".next/**", "out/**", "dist/**", "node_modules/**", "drizzle/**", "playwright-report/**", "test-results/**", "data/**"]), + { + rules: { + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], + /* The project does not use the React Compiler; the virtualizer is fine. */ + "react-hooks/incompatible-library": "off", + }, + }, +]); diff --git a/examples/advanced/REST.html b/examples/advanced/REST.html deleted file mode 100644 index 56c9716..0000000 --- a/examples/advanced/REST.html +++ /dev/null @@ -1,597 +0,0 @@ - - - - - - - Editor example - REST interface - - - - - - - - - - - - - - - - - - -
-
-

Editor example REST interface

-
-

REST interfaces are popular in CRUD applications as it provides a clean and well defined interface between the client and server. Editor can be fully integrated - with a REST environment through its ability to specify different URLs for the create, edit and remove actions of Editor. This is done by giving ajax as an object with the - create, edit and remove properties specified with the URL to use for each action, as shown in this example.

-

In addition to being able to specify a unique URL for each action, you can also specify full jQuery Ajax options for each action by giving the Ajax properties - as an object. In this example the type option is used to specify the HTTP method to be used for each action, as REST interfaces typically require. - Note also that the server returns a 400 Bad request response to invalid data, which is correctly handled by Editor.

-
-
- - - - - - - - - - - - - - - - - - - - - -
NamePositionOfficeExtn.Start dateSalary
NamePositionOfficeExtn.Start dateSalary
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: { - create: { - type: 'POST', - url: '../php/rest/create.php' - }, - edit: { - type: 'PUT', - url: '../php/rest/edit.php?id=_id_' - }, - remove: { - type: 'DELETE', - url: '../php/rest/remove.php?id=_id_' - } - }, - table: "#example", - fields: [ { - label: "First name:", - name: "first_name" - }, { - label: "Last name:", - name: "last_name" - }, { - label: "Position:", - name: "position" - }, { - label: "Office:", - name: "office" - }, { - label: "Extension:", - name: "extn" - }, { - label: "Start date:", - name: "start_date" - }, { - label: "Salary:", - name: "salary" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/rest/get.php", - columns: [ - { data: null, render: function ( data, type, row ) { - // Combine the first and last names into a single table field - return data.first_name+' '+data.last_name; - } }, - { data: "position" }, - { data: "office" }, - { data: "extn" }, - { data: "start_date" }, - { data: "salary", render: $.fn.dataTable.render.number( ',', '.', 0, '$' ) } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/_notes/dwsync.xml b/examples/advanced/_notes/dwsync.xml deleted file mode 100644 index c33d745..0000000 --- a/examples/advanced/_notes/dwsync.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/advanced/compoundKey.html b/examples/advanced/compoundKey.html deleted file mode 100644 index 7322119..0000000 --- a/examples/advanced/compoundKey.html +++ /dev/null @@ -1,544 +0,0 @@ - - - - - - - Editor example - Compound database primary key - - - - - - - - - - - - - - - - - - -
-
-

Editor example Compound database primary key

-
-

Editor's PHP and .NET server-side libraries support the use of compound keys (as of v1.6). On the - client-side no special configuration is required, while on the server-side you simply need to use an array as the optional third parameter for the - Editor class constructor with the column names that make up the primary key.

-

It is important to note that when designing a form that uses a compound key, the data that makes up the primary key must either be included in the form, or - generated in the server-side script. It cannot currently use data generated by the database.

-

In this example the staff member id and the date make up the compound primary key for the users_vists database table.

-
-
- - - - - - - - - - - - - - - -
NameSiteVisit date
NameSiteVisit date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/compoundKey.php', - table: '#example', - fields: [ { - label: 'Staff member:', - name: 'users_visits.user_id', - type: 'select' - }, { - label: 'Visiting site:', - name: 'users_visits.site_id', - type: 'select' - }, { - label: 'Date:', - name: 'users_visits.visit_date', - type: 'datetime' - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: { - url: '../php/compoundKey.php', - type: 'POST' - }, - columns: [ - { data: null, render: function ( data, type, row ) { - return data.users.first_name+' '+data.users.last_name; - } }, - { data: 'sites.name' }, - { data: 'users_visits.visit_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/deepObjects.html b/examples/advanced/deepObjects.html deleted file mode 100644 index a955381..0000000 --- a/examples/advanced/deepObjects.html +++ /dev/null @@ -1,570 +0,0 @@ - - - - - - - Editor example - Complex (nested) JSON data source - - - - - - - - - - - - - - - - - - -
-
-

Editor example Complex (nested) JSON data source

-
-

Like DataTables, Editor has the ability to work with virtually any JSON data source. In DataTables the columns.data option is used to specify - the property name for the row's data source object that should be used for a columns' data. In Editor the fields.name and fields.data options can be used (to - clarify the difference between fields.data and fields.name, the former is what is used to read the value from the DataTables row's data, while the latter is the name of - the field that is submitted to the server - typically they will be the same, with the data property taking the value of the name automatically unless otherwise - specified).

-

This example shows uses data from the server in the structure:

-
{
-	"DT_RowId": "row_1",
-	"users": {
-		"first_name": "Quynn",
-		"last_name": "Contreras",
-		"phone": "1-971-977-4681",
-		"site": "1"
-	},
-	"sites": {
-		"name": "Edinburgh"
-	}
-}
-
-

The columns.data, fields.data and fields.name options can be given as - Javascript dotted object notation strings, to be able to read the data required. For example, to get the first name parameter from the above data source object, - use users.first_name as is done in the Editor initialisation in this example.

-

This can be exceptionally useful when working with joined tables.

-
-
- - - - - - - - - - - - - - - - - -
First nameLast namePhone #Location
First nameLast namePhone #Location
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/join.php", - table: "#example", - fields: [ { - label: "First name:", - name: "users.first_name" - }, { - label: "Last name:", - name: "users.last_name" - }, { - label: "Phone #:", - name: "users.phone" - }, { - label: "Site:", - name: "users.site", - type: "select" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: { - url: "../php/join.php", - type: 'POST' - }, - columns: [ - { data: "users.first_name" }, - { data: "users.last_name" }, - { data: "users.phone" }, - { data: "sites.name" } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/formOnlyData.html b/examples/advanced/formOnlyData.html deleted file mode 100644 index 1f89bfc..0000000 --- a/examples/advanced/formOnlyData.html +++ /dev/null @@ -1,562 +0,0 @@ - - - - - - - Editor example - Data shown only in the form - - - - - - - - - - - - - - - - - - -
-
-

Editor example Data shown only in the form

-
-

It can often be useful to have more information available in your form that you show in your table (the table for example would show a summary, while the form, - with more screen real estate available to it can show detailed information). This is readily done with DataTables and Editor through the fact that DataTables can - use objects as a data source, and the columns.data option is used to specify which object properties are required for each column. The data objects provide - all the information needed for both the form and table, but only a limited selection of the available properties are actually used in the table, while the form - makes full use of the whole object.

-

This example (which is of course grossly simplified) shows four fields only in the table, while the form provides editing abilities for eight different - fields.

-
-
- - - - - - - - - -
NamePositionOfficeSalary
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - "ajax": "../php/staff.php", - "table": "#example", - "fields": [ { - "label": "First name:", - "name": "first_name" - }, { - "label": "Last name:", - "name": "last_name" - }, { - "label": "Position:", - "name": "position" - }, { - "label": "Office:", - "name": "office" - }, { - "label": "Extension:", - "name": "extn" - }, { - "label": "Start date:", - "name": "start_date" - }, { - "label": "Salary:", - "name": "salary" - }, { - "label": "Age:", - "name": "age" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/staff.php", - columns: [ - { data: null, render: function ( data, type, row ) { - // Combine the first and last names into a single table field - return data.first_name+' '+data.last_name; - } }, - { data: "position" }, - { data: "office" }, - { data: "salary", render: $.fn.dataTable.render.number( ',', '.', 0, '$' ) } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/htmlTable.html b/examples/advanced/htmlTable.html deleted file mode 100644 index a670fc3..0000000 --- a/examples/advanced/htmlTable.html +++ /dev/null @@ -1,955 +0,0 @@ - - - - - - - Editor example - DOM sourced table - - - - - - - - - - - - - - - - - - -
-
-

Editor example DOM sourced table

-
-

Although many of the Editor examples show the data for the table being loaded by Ajax (ajax) this is by no means mandatory. Editor will work with any data source that DataTables can use, with the - only additional requirement that each row has a unique ID (allowing the server to identify which rows to update, delete, etc).

-

This example shows Editor being applied to a plain HTML table (generated from the database, although it could come from absolutely anywhere). Ajax requests are - still used for create, edit and remove actions, but not for loading the initial data.

-

This example uses DataTables ability to use read DOM source tables into a JSON object data store, with the keys specified by columns.data. This makes working with the - data in the table very easy since it is named object parameters rather than arrays where indexes must be used. Please note that this feature requires DataTables 1.10.8 or newer.

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
First nameLast namePositionOfficeSalary
TigerNixonSystem ArchitectEdinburgh$320,800
GarrettWintersAccountantTokyo$170,750
AshtonCoxJunior Technical AuthorSan Francisco$86,000
CedricKellySenior Javascript DeveloperEdinburgh$433,060
AiriSatouAccountantTokyo$162,700
BrielleWilliamsonIntegration SpecialistNew York$372,000
HerrodChandlerSales AssistantSan Francisco$137,500
RhonaDavidsonIntegration SpecialistTokyo$327,900
ColleenHurstJavascript DeveloperSan Francisco$205,500
SonyaFrostSoftware EngineerEdinburgh$103,600
JenaGainesOffice ManagerLondon$90,560
QuinnFlynnSupport LeadEdinburgh$342,000
ChardeMarshallRegional DirectorSan Francisco$470,600
HaleyKennedySenior Marketing DesignerLondon$313,500
TatyanaFitzpatrickRegional DirectorLondon$385,750
MichaelSilvaMarketing DesignerLondon$198,500
PaulByrdChief Financial Officer (CFO)New York$725,000
GloriaLittleSystems AdministratorNew York$237,500
BradleyGreerSoftware EngineerLondon$132,000
DaiRiosPersonnel LeadEdinburgh$217,500
JenetteCaldwellDevelopment LeadNew York$345,000
YuriBerryChief Marketing Officer (CMO)New York$675,000
CaesarVancePre-Sales SupportNew York$106,450
DorisWilderSales AssistantSidney$85,600
AngelicaRamosChief Executive Officer (CEO)London$1,200,000
GavinJoyceDeveloperEdinburgh$92,575
JenniferChangRegional DirectorSingapore$357,650
BrendenWagnerSoftware EngineerSan Francisco$206,850
FionaGreenChief Operating Officer (COO)San Francisco$850,000
ShouItouRegional MarketingTokyo$163,000
MichelleHouseIntegration SpecialistSidney$95,400
SukiBurksDeveloperLondon$114,500
PrescottBartlettTechnical AuthorLondon$145,000
GavinCortezTeam LeaderSan Francisco$235,500
MartenaMccrayPost-Sales supportEdinburgh$324,050
UnityButlerMarketing DesignerSan Francisco$85,675
HowardHatfieldOffice ManagerSan Francisco$164,500
HopeFuentesSecretarySan Francisco$109,850
VivianHarrellFinancial ControllerSan Francisco$452,500
TimothyMooneyOffice ManagerLondon$136,200
JacksonBradshawDirectorNew York$645,750
OliviaLiangSupport EngineerSingapore$234,500
BrunoNashSoftware EngineerLondon$163,500
SakuraYamamotoSupport EngineerTokyo$139,575
ThorWaltonDeveloperNew York$98,540
FinnCamachoSupport EngineerSan Francisco$87,500
SergeBaldwinData CoordinatorSingapore$138,575
ZenaidaFrankSoftware EngineerNew York$125,250
ZoritaSerranoSoftware EngineerSan Francisco$115,000
JenniferAcostaJunior Javascript DeveloperEdinburgh$75,650
CaraStevensSales AssistantNew York$145,600
HermioneButlerRegional DirectorLondon$356,250
LaelGreerSystems AdministratorLondon$103,500
JonasAlexanderDeveloperSan Francisco$86,500
ShadDeckerRegional DirectorEdinburgh$183,000
MichaelBruceJavascript DeveloperSingapore$183,000
DonnaSniderCustomer SupportNew York$112,000
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/staff-html.php', - table: '#example', - fields: [ { - label: "First name:", - name: "first_name" - }, { - label: "Last name:", - name: "last_name" - }, { - label: "Position:", - name: "position" - }, { - label: "Office:", - name: "office" - }, { - label: "Salary:", - name: "salary", - data: function (row, type, val) { - // Strip the formatting from the salary number - if ( type === 'set' ) { - row.salary = val; - } - return row.salary.replace(/[^0-9]/g, ''); - } - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - columns: [ - { data: "first_name" }, - { data: "last_name" }, - { data: "position" }, - { data: "office" }, - { data: "salary" } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/index.html b/examples/advanced/index.html deleted file mode 100644 index 557b016..0000000 --- a/examples/advanced/index.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - Editor examples - Editor advanced initialisation examples - - -
-
-

Editor example Editor advanced initialisation examples

-
-

The examples in this section show more advanced initialisation and configuration techniques than the simple examples. Examples here show - more complex join operations, using REST interfaces and how to show data in the table or form only, among others.

-

These examples, along with the API examples will help you get the most out of Editor.

-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/joinArray.html b/examples/advanced/joinArray.html deleted file mode 100644 index 123b4b1..0000000 --- a/examples/advanced/joinArray.html +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - Editor example - Join tables - one-to-many join - - - - - - - - - - - - - - - - - - -
-
-

Editor example Join tables - one-to-many join

-
-

When using a joined database tables, there are times when you might wish to use a one-to-many relationship for your data, and display that as an editable - interface for your system's users.

-

On the client-side, Editor achieved this quite simply by making use of DataTables' powerful columns.data and columns.render options to display the - data and the fields.name option - in Editor. These options all provide the ability to use array based data through Javascript square bracket notation ([]) - see the DataTables columns.data documentation for detailed - information on the syntax that can be used for these options.

-

In this case each record in our users database table can have different access permissions granted. The one-to-many link is provided by a join - table on the database side.

-

For the editing interface Editor presents this as a checkbox list of options which the editor can use to select the options they want to assign to a particular - user.

-

On the server-side the Editor PHP and .NET libraries both provide a Join class that can be used to construct complex CRUD queries for one-to-many - data manipulation. For further information about using the PHP libraries to build an editable DataTable with JOINed tables, please see the join documentation - (PHP | .NET).

-
-
- - - - - - - - - - - - - - - - - -
First nameLast nameLocationPermissions
First nameLast nameLocationPermissions
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/joinArray.php", - table: "#example", - fields: [ { - label: "First name:", - name: "users.first_name" - }, { - label: "Last name:", - name: "users.last_name" - }, { - label: "Site:", - name: "users.site", - type: "select" - }, { - "label": "Permissions:", - "name": "permission[].id", - "type": "checkbox" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: { - url: "../php/joinArray.php", - type: 'POST' - }, - columns: [ - { data: "users.first_name" }, - { data: "users.last_name" }, - { data: "sites.name" }, - { data: "permission", render: "[, ].name" } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/joinLinkTable.html b/examples/advanced/joinLinkTable.html deleted file mode 100644 index fc90bc1..0000000 --- a/examples/advanced/joinLinkTable.html +++ /dev/null @@ -1,570 +0,0 @@ - - - - - - - Editor example - Join tables - link table - - - - - - - - - - - - - - - - - - -
-
-

Editor example Join tables - link table

-
-

Data in relational databases is often stored over multiple tables, partitioned by the data type and then joined together using SQL JOIN queries. In one form of - joined table, a 'link' table is used linking records between two different types of data.

-

In this example, which extends the simple join example, the users and dept tables are linked by the - user_dept table. When the data is read, we read from all three tables (plus the sites table to show multiple joins working together). The - returned data structure for each row in this example is:

-
{
-	"DT_RowId": "row_1",
-	"users": {
-		"first_name": "Quynn",
-		"last_name": "Contreras",
-		"site": "1"
-	},
-	"sites": {
-		"name": "Edinburgh"
-	},
-	"user_dept": {
-		"dept_id": "1"
-	},
-	"dept": {
-		"name": "IT"
-	}
-}
-
-

For editing we want to write to three users fields and the user_dept field. The Editor instance is configured with these four fields - using the fields.name option - and the Editor server-side libraries preform the required SQL operations for us on create, edit and delete.

-

See the join documentation (PHP | .NET) - for further information about joins with Editor's framework libraries.

-
-
- - - - - - - - - - - - - - - - - -
First nameLast nameLocationDepartment
First nameLast nameLocationDepartment
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/joinLinkTable.php", - table: "#example", - fields: [ { - label: "First name:", - name: "users.first_name" - }, { - label: "Last name:", - name: "users.last_name" - }, { - label: "Site:", - name: "users.site", - type: "select" - }, { - label: "Department:", - name: "user_dept.dept_id", - type: "select" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: { - url: "../php/joinLinkTable.php", - type: 'POST' - }, - columns: [ - { data: "users.first_name" }, - { data: "users.last_name" }, - { data: "sites.name" }, - { data: "dept.name" } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/joinSelf.html b/examples/advanced/joinSelf.html deleted file mode 100644 index d4d798f..0000000 --- a/examples/advanced/joinSelf.html +++ /dev/null @@ -1,565 +0,0 @@ - - - - - - - Editor example - Join tables - self referencing join - - - - - - - - - - - - - - - - - - -
-
-

Editor example Join tables - self referencing join

-
-

Extending the simple join example, here we show how a self referencing SQL table can be used with Editor. Self referencing - tables can be very useful when there is a hierarchy of like objects in the database.

-

In this particular example each staff member has a manager, who is also a member of staff. Using a self referencing join we can display each staff member and - their manager in the table, while Editor allows these fields to be easily editable.

-

In SQL terms, the users table has a column manager which references the id column in its own table. In an SQL statement - we use the as keyword to alias the joined table, which is exactly how the Editor server-side libraries also operate.

-

The data structure returned by the server for each row in this table is:

-
{
-	"DT_RowId": "row_1",
-	"users": {
-		"first_name": "Quynn",
-		"last_name": "Contreras",
-		"manager": "1"
-	},
-	"manager": {
-		"first_name": "Quynn",
-		"last_name": "Contreras"
-	}
-}
-
-

The three fields we want to edit are in the users object, but we also use the manager properties for display in the table (through the - use of columns.data and columns.render).

-

See the join documentation (PHP | .NET) - for further information about joins with Editor's framework libraries.

-
-
- - - - - - - - - - - - - - - -
First nameLast nameManager
First nameLast nameManager
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/joinSelf.php", - table: "#example", - fields: [ { - label: "First name:", - name: "users.first_name" - }, { - label: "Last name:", - name: "users.last_name" - }, { - label: "Manager:", - name: "users.manager", - type: "select" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/joinSelf.php", - columns: [ - { data: "users.first_name" }, - { data: "users.last_name" }, - { - data: "manager", - render: function ( val, type, row ) { - return val.first_name ? - val.first_name +' '+ val.last_name : - ''; - }, - defaultContent: "" - } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/jsonId.html b/examples/advanced/jsonId.html deleted file mode 100644 index fede392..0000000 --- a/examples/advanced/jsonId.html +++ /dev/null @@ -1,576 +0,0 @@ - - - - - - - Editor example - Row ID source specification - - - - - - - - - - - - - - - - - - -
-
-

Editor example Row ID source specification

-
-

By default Editor will use the tr element's id attribute to identify the row to the server (more specifically this will typically - contain the database's primary key to uniquely identity the row). While this default method can be very useful, particularly if you use the DT_RowId - JSON option to set the id, you may wish to read the row's id from the JSON data source, rather than the DOM. This is easily done using the idSrc option.

-

The example below, with idSrc is - set to id is suitable for cases where the data source objects look like: { "id": 2, "first_name": "Fiona", ...}. Like other data source - options the idSrc option can be given - in dotted object notation to read nested objects.

-
-
- - - - - - - - - - - - - - - - - - - - - -
NamePositionOfficeExtn.Start dateSalary
NamePositionOfficeExtn.Start dateSalary
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - "ajax": "../php/jsonId.php", - "table": "#example", - "idSrc": "id", - "fields": [ { - "label": "First name:", - "name": "first_name" - }, { - "label": "Last name:", - "name": "last_name" - }, { - "label": "Position:", - "name": "position" - }, { - "label": "Office:", - "name": "office" - }, { - "label": "Extension:", - "name": "extn" - }, { - "label": "Start date:", - "name": "start_date", - "type": "datetime" - }, { - "label": "Salary:", - "name": "salary" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/jsonId.php", - columns: [ - { data: null, render: function ( data, type, row ) { - // Combine the first and last names into a single table field - return data.first_name+' '+data.last_name; - } }, - { data: "position" }, - { data: "office" }, - { data: "extn" }, - { data: "start_date" }, - { data: "salary", render: $.fn.dataTable.render.number( ',', '.', 0, '$' ) } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/localstorage.html b/examples/advanced/localstorage.html deleted file mode 100644 index e394096..0000000 --- a/examples/advanced/localstorage.html +++ /dev/null @@ -1,631 +0,0 @@ - - - - - - - Editor example - Ajax override - using localStorage for the data source - - - - - - - - - - - - - - - - - - -
-
-

Editor example Ajax override - using localStorage for the data source

-
-

This example shows how the ajax - initialisation option can be used to replace the default Ajax call that Editor makes and instead use the browser's localStorage abilities to save the - state of the table locally on the browser. This means that the user effectively has persistent storage, but it is available only to them on their current - browser.

-

The code in this example shows the ajax option as a function that implements everything that is required by - Editor for data storage and retrieval. The 'create', 'edit' and 'remove' actions are each handled by storing the submitted data in a local variable, which is - then stored in local storage for data persistence.

-

Note that this example fully supports Editor's multi-row editing capability as it fully implements the client / - server data interchange format Editor uses.

-

Although this particular use case is fairly limited, it does show how Editor's ajax option can be used to intercept and manage the data requests that Editor makes. Expanding on this almost any - data storage system could be used from Firebase to WebSockets.

-
-
- - - - - - - - - - - - - -
ItemStatus
ItemStatus
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - // Object that will contain the local state - var todo = {}; - - // Create or update the todo localStorage entry - if ( localStorage.getItem('todo') ) { - todo = JSON.parse( localStorage.getItem('todo') ); - } - - // Set up the editor - editor = new $.fn.dataTable.Editor( { - table: "#example", - fields: [ - { - label: "Item:", - name: "item" - }, { - label: "Status:", - name: "status", - type: "radio", - def: "To do", - options: [ 'To do', 'Done' ] - } - ], - ajax: function ( method, url, d, successCallback, errorCallback ) { - var output = { data: [] }; - - if ( d.action === 'create' ) { - // Create new row(s), using the current time and loop index as - // the row id - var dateKey = +new Date(); - - $.each( d.data, function (key, value) { - var id = dateKey+''+key; - - value.DT_RowId = id; - todo[ id ] = value; - output.data.push( value ); - } ); - } - else if ( d.action === 'edit' ) { - // Update each edited item with the data submitted - $.each( d.data, function (id, value) { - value.DT_RowId = id; - $.extend( todo[ id ], value ); - output.data.push( todo[ id ] ); - } ); - } - else if ( d.action === 'remove' ) { - // Remove items from the object - $.each( d.data, function (id) { - delete todo[ id ]; - } ); - } - - // Store the latest `todo` object for next reload - localStorage.setItem( 'todo', JSON.stringify(todo) ); - - // Show Editor what has changed - successCallback( output ); - } - } ); - - // Initialise the DataTable - $('#example').DataTable( { - dom: "Bfrtip", - data: $.map( todo, function (value, key) { - return value; - } ), - columns: [ - { data: "item" }, - { data: "status" } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/multiItem.html b/examples/advanced/multiItem.html deleted file mode 100644 index 32b0d3b..0000000 --- a/examples/advanced/multiItem.html +++ /dev/null @@ -1,600 +0,0 @@ - - - - - - - Editor example - Multi-item editing (rows, columns, cells) - - - - - - - - - - - - - - - - - - -
-
-

Editor example Multi-item editing (rows, columns, cells)

-
-

This example demonstrates Editor's multi-item editing capabilities, showing specifically that multi-item editing is not limited to just to rows, but also - columns and cells can be selected and edited with a single click. Furthermore, it is possible to combine the selection of rows, columns and cells to provide - complex data updates with ease.

-

The item selection in this (and all other examples) is provided by the Select extension for DataTables. It has - the ability to select the individual components of the table - which item type is selected can be controlled through the select.items() method, or the selectRows, selectColumns and selectCells buttons that Select provides.

-

These buttons are utilised in this example to allow the end user to easily change what items will be selected.

-
-
- - - - - - - - - - - - - - - - - - - - - -
NamePositionOfficeExtn.Start dateSalary
NamePositionOfficeExtn.Start dateSalary
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/staff.php", - table: "#example", - fields: [ { - label: "First name:", - name: "first_name" - }, { - label: "Last name:", - name: "last_name" - }, { - label: "Position:", - name: "position" - }, { - label: "Office:", - name: "office" - }, { - label: "Extension:", - name: "extn" - }, { - label: "Start date:", - name: "start_date", - type: "datetime" - }, { - label: "Salary:", - name: "salary" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/staff.php", - columns: [ - { - data: null, - render: function ( data, type, row ) { - // Combine the first and last names into a single table field - return data.first_name+' '+data.last_name; - }, - editField: [ 'first_name', 'last_name' ] - }, - { data: "position" }, - { data: "office" }, - { data: "extn" }, - { data: "start_date" }, - { data: "salary", render: $.fn.dataTable.render.number( ',', '.', 0, '$' ) } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor }, - "selectRows", - "selectColumns", - "selectCells", - "selectNone" - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

a.buttons-select-rows, - a.buttons-select-none { - margin-left: 1em; - } -
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/tableOnlyData.html b/examples/advanced/tableOnlyData.html deleted file mode 100644 index bf239f0..0000000 --- a/examples/advanced/tableOnlyData.html +++ /dev/null @@ -1,521 +0,0 @@ - - - - - - - Editor example - Data shown in table only - - - - - - - - - - - - - - - - - - -
-
-

Editor example Data shown in table only

-
-

This example shows how Editor can handle the case where data is shown in the table, but is not available in the form to be edited. This can be useful where the - server generates some of the data to be shown in the table, for example a timestamp showing the last edited time, or an auto incrementing sequence. This is the - inverse of the form only data example.

-

The way this works is that Editor simply returns the details for the row on each create and edit action, in the row return parameter. This - information is then used to populate the DataTable.

-
-
- - - - - - - - - - - - - - - -
First nameLast nameUpdated date
First nameLast nameUpdated date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - "ajax": "../php/tableOnlyData.php", - "table": "#example", - "fields": [ { - "label": "First name:", - "name": "first_name" - }, { - "label": "Last name:", - "name": "last_name" - } - ] - } ); - - $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/tableOnlyData.php", - columns: [ - { data: "first_name" }, - { data: "last_name" }, - { data: "updated_date" } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/upload-many.html b/examples/advanced/upload-many.html deleted file mode 100644 index d2651b0..0000000 --- a/examples/advanced/upload-many.html +++ /dev/null @@ -1,577 +0,0 @@ - - - - - - - Editor example - File upload (many) - - - - - - - - - - - - - - - - - - -
-
-

Editor example File upload (many)

-
-

This example shows how the uploadMany - field type can be used to allow multiple images to be uploaded and associated with a single field in the form. Images can be added or removed from the collection - and this is designed to work with a one-to-many join in the backend database.

-

The upload options of Editor are extensively documented in the manual (Javascript, PHP and .NET) and details the various options - available.

-
-
- - - - - - - - - - - - - - - - - - - -
First nameLast namePhone #LocationImage
First nameLast namePhone #LocationImage
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/upload-many.php", - table: "#example", - fields: [ { - label: "First name:", - name: "users.first_name" - }, { - label: "Last name:", - name: "users.last_name" - }, { - label: "Phone #:", - name: "users.phone" - }, { - label: "Site:", - name: "users.site", - type: "select" - }, { - label: "Images:", - name: "files[].id", - type: "uploadMany", - display: function ( fileId, counter ) { - return '<img src="'+editor.file( 'files', fileId ).web_path+'"/>'; - }, - noFileText: 'No images' - } - ] - } ); - - var table = $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/upload-many.php", - columns: [ - { data: "users.first_name" }, - { data: "users.last_name" }, - { data: "users.phone" }, - { data: "sites.name" }, - { - data: "files", - render: function ( d ) { - return d.length ? - d.length+' image(s)' : - 'No image'; - }, - title: "Image" - } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/advanced/upload.html b/examples/advanced/upload.html deleted file mode 100644 index 5294bad..0000000 --- a/examples/advanced/upload.html +++ /dev/null @@ -1,585 +0,0 @@ - - - - - - - Editor example - File upload - - - - - - - - - - - - - - - - - - -
-
-

Editor example File upload

-
-

This example shows Editor being used with the upload fields type to give end users the ability to upload a file in the form. The upload field type allows just a single file to be - uploaded, while its companion input type uploadMany provides the ability to have multiple files uploaded for a single field.

-

The upload options of Editor are extensively documented in the manual (Javascript, PHP and .NET) and details the various options - available.

-

In this example an image file can be uploaded, limited to 500KB using server-side validation. To display the image a simple img tag is used, with information about the file to be displayed retrieved using the file() method which Editor makes available and is - automatically populated based on the server-side configuration.

-
-
- - - - - - - - - - - - - - - - - - - -
First nameLast namePhone #CityImage
First nameLast namePhone #CityImage
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: "../php/upload.php", - table: "#example", - fields: [ { - label: "First name:", - name: "first_name" - }, { - label: "Last name:", - name: "last_name" - }, { - label: "Phone #:", - name: "phone" - }, { - label: "City:", - name: "city" - }, { - label: "Image:", - name: "image", - type: "upload", - display: function ( file_id ) { - return '<img src="'+editor.file( 'files', file_id ).web_path+'"/>'; - }, - clearText: "Clear", - noImageText: 'No image' - } - ] - } ); - - var table = $('#example').DataTable( { - dom: "Bfrtip", - ajax: "../php/upload.php", - columns: [ - { data: "first_name" }, - { data: "last_name" }, - { data: "phone" }, - { data: "city" }, - { - data: "image", - render: function ( file_id ) { - return file_id ? - '<img src="'+editor.file( 'files', file_id ).web_path+'"/>' : - null; - }, - defaultContent: "No image", - title: "Image" - } - ], - select: true, - buttons: [ - { extend: "create", editor: editor }, - { extend: "edit", editor: editor }, - { extend: "remove", editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/_notes/dwsync.xml b/examples/dates/_notes/dwsync.xml deleted file mode 100644 index 0e4be19..0000000 --- a/examples/dates/_notes/dwsync.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/dates/dates.html b/examples/dates/dates.html deleted file mode 100644 index 4db1f9b..0000000 --- a/examples/dates/dates.html +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - - Editor example - Date picker - - - - - - - - - - - - - - - - - - -
-
-

Editor example Date picker

-
-

Editor features a built in date time picker through the datetime field type. This control presents the end user with a graphical calender that can be used to easily enter dates.

-

By default only the ISO8601 format YYYY-MM-DD is supported by the datetime input control, but custom date formats can be used by include the momentjs library on - your page.

-

This example shows the simple ISO8601 case for two date fields.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'datetime', - def: function () { return new Date(); } - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - def: function () { return new Date(); } - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/datetime.html b/examples/dates/datetime.html deleted file mode 100644 index cacbf2a..0000000 --- a/examples/dates/datetime.html +++ /dev/null @@ -1,554 +0,0 @@ - - - - - - - Editor example - Date and time input - - - - - - - - - - - - - - - - - - - -
-
-

Editor example Date and time input

-
-

Editor's built in datetime field type - provides a UI widget to assist with the user input of both dates and times - the components that are displayed in the input control are automatically determined - from the format string.

-

In this case we have two fields which contain both date and time components, shown in different formats to illustrate how the formatting options operate. The - Updated date field is shown in a US style date format with a 12 hour clock while the Reigstered date is shown with a more Euro style format and a - 24 hour clock.

-

The formats that can be used are defined by the momentjs library which must be included for the - datetime field type to operate with date time - formatted strings.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/datetime.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'datetime', - def: function () { return new Date(); }, - format: 'MM-DD-YYYY h:mm A', - fieldInfo: 'US style m-d-y date input with 12 hour clock' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - def: function () { return new Date(); }, - format: 'D MMM YYYY HH:mm', - fieldInfo: 'Euro style date with 24 hour clock' - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/datetime.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/formatting.html b/examples/dates/formatting.html deleted file mode 100644 index 1d6be37..0000000 --- a/examples/dates/formatting.html +++ /dev/null @@ -1,550 +0,0 @@ - - - - - - - Editor example - Formatted dates - - - - - - - - - - - - - - - - - - - -
-
-

Editor example Formatted dates

-
-

Editor's datetime field type makes entering - date and time information simple, but to increase user friendliness you may wish to use a custom date format (rather than the default ISO8601 format) - this can be - done with the momentjs library and specifying a format option for the field.

-

This example shows two different date fields which are displayed using different formats - the first is in the US m/d/y format while the second is a - more verbose format. Using momentjs allows you to select whatever format you require.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php?format=custom', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'datetime', - def: function () { return new Date(); }, - format: 'M/D/YYYY', - fieldInfo: 'US style m/d/y format' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - def: function () { return new Date(); }, - format: 'dddd D MMMM YYYY', - fieldInfo: 'Verbose date format' - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php?format=custom', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/html5.html b/examples/dates/html5.html deleted file mode 100644 index 157dbba..0000000 --- a/examples/dates/html5.html +++ /dev/null @@ -1,538 +0,0 @@ - - - - - - - Editor example - HTML5 date input - - - - - - - - - - - - - - - - - - -
-
-

Editor example HTML5 date input

-
-

The HTML5 specification calls for an input - element which supports date and time input types. While this would be an excellent solution rather than needing to rely upon a - Javascript library for the calender picker (such as Editor's built in datetime field type), only Chrome and Edge currently support these input options. IE, Firefox and Safari do not currently enjoy - support for these elements.

-

Fortunately, if we specify an input field has date type, if the browser doesn't support it, it will just - fall back to being a regular text input. User's won't see the calender when using a browser that doesn't support this input type, but they will still be able to - type the date in.

-

The date field type will automatically use an - HTML date input field - unless jQuery UI's date picker is available on the page, in which case that will be used.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'date' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'date' - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/i18n.html b/examples/dates/i18n.html deleted file mode 100644 index c75db3e..0000000 --- a/examples/dates/i18n.html +++ /dev/null @@ -1,552 +0,0 @@ - - - - - - - Editor example - Internationalisation - - - - - - - - - - - - - - - - - - -
-
-

Editor example Internationalisation

-
-

This example shows the datetime option being - used with translated text - in this case French. All of the text displayed by the datetime option can be modified (and therefore translated) through the i18n.datetime object.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'datetime', - def: function () { return new Date(); } - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - def: function () { return new Date(); } - } - ], - i18n: { - datetime: { - previous: 'Précédent', - next: 'Premier', - months: [ 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre' ], - weekdays: [ 'Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam' ] - } - } - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/index.html b/examples/dates/index.html deleted file mode 100644 index e651273..0000000 --- a/examples/dates/index.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - Editor examples - Date and time input examples - - -
-
-

Editor example Date and time input examples

-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/jqueryui-formatting.html b/examples/dates/jqueryui-formatting.html deleted file mode 100644 index 2015c89..0000000 --- a/examples/dates/jqueryui-formatting.html +++ /dev/null @@ -1,550 +0,0 @@ - - - - - - - Editor example - jQuery UI DatePicker with custom formatting - - - - - - - - - - - - - - - - - - - - -
-
-

Editor example jQuery UI DatePicker with custom formatting

-
-

Custom formats can be used with the jQuery UI DatePicker through the dateFormat option which can be specified for date field types. The formatting options are defined by jQuery UI's formatDate method.

-

This example shows two different date fields which are displayed using different formats - the first is in the US m/d/y format while the second is a - more verbose format.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php?format=custom', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'date', - def: function () { return new Date(); }, - dateFormat: 'm/d/yy' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'date', - def: function () { return new Date(); }, - dateFormat: 'DD d MM yy' - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php?format=custom', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/jqueryui.html b/examples/dates/jqueryui.html deleted file mode 100644 index 3ce91b1..0000000 --- a/examples/dates/jqueryui.html +++ /dev/null @@ -1,552 +0,0 @@ - - - - - - - Editor example - jQuery UI DatePicker - - - - - - - - - - - - - - - - - - - - -
-
-

Editor example jQuery UI DatePicker

-
-

The jQuery UI library provides a date picker component which gives a comprehensive calender picker - including - features that are not available in Editor's built in datetime calander input such as the ability to display multiple months at the same time.

-

jQuery UI's date picker can be used with Editor through the date field type which will automatically determine if the date picker is available on your page or not - if it is, it will be used - and if not the HTML5 date input type will be used.

-

This example simply shows the jQuery UI date picker being used with ISO8601 formatted dates.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Updated date:', - name: 'updated_date', - type: 'date', - def: function () { return new Date(); }, - dateFormat: $.datepicker.ISO_8601 - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'date', - def: function () { return new Date(); }, - dateFormat: $.datepicker.ISO_8601 - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/options-disable-days.html b/examples/dates/options-disable-days.html deleted file mode 100644 index 71b7c62..0000000 --- a/examples/dates/options-disable-days.html +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - - Editor example - Options - disable days - - - - - - - - - - - - - - - - - - -
-
-

Editor example Options - disable days

-
-

It can sometimes be useful to not allow selection of particular days - the datetime field provides that ability through the disableDays option. It can be provided as an array of day numbers in - the week to disable (Sunday = 0, Monday = 1, etc) or as a function to provide more fine grained control.

-

This example shows the disableDays option being used to disable date selection on Saturdays and Sundays. Note that validation of this information - should also be performed at the server-side since it is possible for the end user to type in an invalid date, rather than using the calender picker.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - opts: { - disableDays: [ 0, 6 ] - } - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/options-min-max.html b/examples/dates/options-min-max.html deleted file mode 100644 index c1f3a0b..0000000 --- a/examples/dates/options-min-max.html +++ /dev/null @@ -1,533 +0,0 @@ - - - - - - - Editor example - Options - min and max dates - - - - - - - - - - - - - - - - - - -
-
-

Editor example Options - min and max dates

-
-

You may wish to limit the range in which an end user can select a date - this can be done using the minDate and maxDate options - (specified in the opts object for the field). The two properties, which can be set together or alone, accept a Javascript Date object, - which you can create using new Date() and pass in an ISO8601 formatted date string.

-

This example shows the date range limited to 2011 - 2020 dates only. It is important that server-side validation of this input be used as the end user can enter - dates outside of this range simply using the keyboard.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - opts: { - minDate: new Date('2011-01-01'), - maxDate: new Date('2020-12-31') - } - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/options-week-numbers.html b/examples/dates/options-week-numbers.html deleted file mode 100644 index b4ca5a9..0000000 --- a/examples/dates/options-week-numbers.html +++ /dev/null @@ -1,530 +0,0 @@ - - - - - - - Editor example - Options - week numbers - - - - - - - - - - - - - - - - - - -
-
-

Editor example Options - week numbers

-
-

It can often be useful to see at a glance what week of the year a week falls into - this option is provided in the datetime calender picker through the - showWeekNumber option which is specified as part of an opts (i.e. options) object.

-

This example shows the week number enabled for the date picker using a simple ISO8601 date format.

-
-
- - - - - - - - - -
First nameLast nameUpdated dateRegistered date
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/dates.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'Registered date:', - name: 'registered_date', - type: 'datetime', - opts: { - showWeekNumber: true - } - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/dates.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'updated_date' }, - { data: 'registered_date' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/time-increment.html b/examples/dates/time-increment.html deleted file mode 100644 index 75a6a91..0000000 --- a/examples/dates/time-increment.html +++ /dev/null @@ -1,568 +0,0 @@ - - - - - - - Editor example - Time increment options - - - - - - - - - - - - - - - - - - - -
-
-

Editor example Time increment options

-
-

When using datetime's time picker options, - you may wish to limit the end user's ability to select particular times to certain intervals. This can be done using the minutesIncrement and - secondsIncrement options, as shown in this example.

-

In this case, the first field uses a ten minute increment, while the second uses a 15 minute and second increment.

-
-
- - - - - - - - - - -
First nameLast nameCityShift startShift end
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/time.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'City:', - name: 'city' - }, { - label: 'Shift start:', - name: 'shift_start', - type: 'datetime', - format: 'h:mm A', - fieldInfo: '12 hour clock format', - opts: { - minutesIncrement: 10 - } - }, { - label: 'Shift end:', - name: 'shift_end', - type: 'datetime', - format: 'HH:mm:ss', - fieldInfo: '24 hour clock format with seconds', - opts: { - minutesIncrement: 15, - secondsIncrement: 15 - } - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/time.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'city' }, - { data: 'shift_start' }, - { data: 'shift_end' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/dates/time.html b/examples/dates/time.html deleted file mode 100644 index 6632001..0000000 --- a/examples/dates/time.html +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - Editor example - Time inputs - - - - - - - - - - - - - - - - - - - -
-
-

Editor example Time inputs

-
-

Time fields can be augmented by the datetime - field type to add a GUI widget that can help users enter data. All that is required is that the format option be set to the format required and it - will automatically detect what elements it should display.

-

In this example the two time columns use different formats (you wouldn't do this normally, but it is useful for this example!) - the Shift start field - uses a 12 hour clock with an AM/PM selector, while the Shift end field uses a 24 hour with seconds. The formats are defined by momentjs which must be included for the datetime field type to operate with time formatted - strings.

-
-
- - - - - - - - - - -
First nameLast nameCityShift startShift end
-
    -
  • Javascript
  • -
  • HTML
  • -
  • CSS
  • -
  • Ajax
  • -
  • Server-side script
  • -
-
-
-

The Javascript shown below is used to initialise the table shown in this example:

var editor; // use a global for the submit and return data rendering in the examples - -$(document).ready(function() { - editor = new $.fn.dataTable.Editor( { - ajax: '../php/time.php', - table: '#example', - fields: [ { - label: 'First name:', - name: 'first_name' - }, { - label: 'Last name:', - name: 'last_name' - }, { - label: 'City:', - name: 'city' - }, { - label: 'Shift start:', - name: 'shift_start', - type: 'datetime', - format: 'h:mm A', - fieldInfo: '12 hour clock format' - }, { - label: 'Shift end:', - name: 'shift_end', - type: 'datetime', - format: 'HH:mm:ss', - fieldInfo: '24 hour clock format with seconds' - } - ] - } ); - - $('#example').DataTable( { - dom: 'Bfrtip', - ajax: '../php/time.php', - columns: [ - { data: 'first_name' }, - { data: 'last_name' }, - { data: 'city' }, - { data: 'shift_start' }, - { data: 'shift_end' } - ], - select: true, - buttons: [ - { extend: 'create', editor: editor }, - { extend: 'edit', editor: editor }, - { extend: 'remove', editor: editor } - ] - } ); -} ); -

In addition to the above code, the following Javascript library files are loaded for use in this example:

- -
-
-

The HTML shown below is the raw HTML table element, before it has been enhanced by DataTables:

-
-
-
-

This example uses a little bit of additional CSS beyond what is loaded from the library files (below), in order to correctly display the table. The - additional CSS used is shown below:

-
-

The following CSS library files are loaded for use in this example to provide the styling of the table:

- -
-
-

This table loads data by Ajax. The latest data that has been loaded is shown below. This data will update automatically as any additional data is - loaded.

-
-
-

The script used to perform the server-side processing for this table is shown below. Please note that this is just an example script using PHP. Server-side - processing scripts can be written in any language, using the protocol described in the DataTables - documentation.

-
-
-
-
-
- -
- - \ No newline at end of file diff --git a/examples/php/README.txt b/examples/php/README.txt deleted file mode 100644 index 5922c15..0000000 --- a/examples/php/README.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Editor PHP example scripts - -The files in this directory are the PHP server-side scripts that are used by the -Editor examples with a PHP back-end, making use of the provided PHP libraries in -the Editor package (see the `php` directory at the top level of the Editor -package). - -For full information on using the Editor PHP libraries, please refer to the -Editor manual online: - https://editor.datatables.net/manual/php - -If you are interested in using Editor with a server-side environment that does -not have PHP available, please refer to the client / server communication -protocol that Editor uses, for information on how to write server-script scripts -that will operate with Editor: - https://editor.datatables.net/manual/server - diff --git a/examples/php/_notes/dwsync.xml b/examples/php/_notes/dwsync.xml deleted file mode 100644 index d0628b0..0000000 --- a/examples/php/_notes/dwsync.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/php/checkbox.php b/examples/php/checkbox.php deleted file mode 100644 index 81c0240..0000000 --- a/examples/php/checkbox.php +++ /dev/null @@ -1,35 +0,0 @@ -fields( - Field::inst( 'first_name' ), - Field::inst( 'last_name' ), - Field::inst( 'phone' ), - Field::inst( 'city' ), - Field::inst( 'zip' ), - Field::inst( 'active' ) - ->setFormatter( function ( $val, $data, $opts ) { - return ! $val ? 0 : 1; - } ) - ) - ->process( $_POST ) - ->json(); - diff --git a/examples/php/compoundKey.php b/examples/php/compoundKey.php deleted file mode 100644 index baebf68..0000000 --- a/examples/php/compoundKey.php +++ /dev/null @@ -1,73 +0,0 @@ -debug( true ) - ->field( - Field::inst( 'users_visits.user_id' ) - ->options( Options::inst() - ->table( 'users' ) - ->value( 'id' ) - ->label( array('first_name', 'last_name') ) - ) - ->validator( 'Validate::dbValues' ), - Field::inst( 'users_visits.site_id' ) - ->options( Options::inst() - ->table( 'sites' ) - ->value( 'id' ) - ->label( 'name' ) - ) - ->validator( 'Validate::dbValues' ), - Field::inst( 'users_visits.visit_date' ), - Field::inst( 'sites.name' ) - ->set( false ), - Field::inst( 'users.first_name' ) - ->set( false ), - Field::inst( 'users.last_name' ) - ->set( false ) - ) - ->leftJoin( 'sites', 'users_visits.site_id', '=', 'sites.id' ) - ->leftJoin( 'users', 'users_visits.user_id', '=', 'users.id' ) - ->validator( function ($editor, $action, $data) { - if ( $action == Editor::ACTION_EDIT ) { - // Detect duplicates - foreach ($data['data'] as $key => $values) { - // Get the row's primary key components - $pkey = $editor->pkeyToArray( $key ); - - // Discount the row being edited - if ( $pkey['users_visits']['user_id'] != $values['users_visits']['user_id'] || - $pkey['users_visits']['visit_date'] != $values['users_visits']['visit_date'] ) - { - // Are there any rows that conflict? - $any = $editor->db()->any( 'users_visits', function ($q) use ($pkey, $values) { - $q->where( 'user_id', $values['users_visits']['user_id']); - $q->where( 'visit_date', $values['users_visits']['visit_date'] ); - } ); - - // If there was a matching row, then report an error - if ( $any ) { - return 'This staff member is already busy that day.'; - } - } - } - } - } ) - ->process($_POST) - ->json(); diff --git a/examples/php/dates.php b/examples/php/dates.php deleted file mode 100644 index 82d488b..0000000 --- a/examples/php/dates.php +++ /dev/null @@ -1,52 +0,0 @@ -fields( - Field::inst( 'first_name' ), - Field::inst( 'last_name' ), - Field::inst( 'updated_date' ) - ->validator( 'Validate::dateFormat', array( - 'empty' => false, - 'format' => $update - ) ) - ->getFormatter( 'Format::date_sql_to_format', $update ) - ->setFormatter( 'Format::date_format_to_sql', $update ), - Field::inst( 'registered_date' ) - ->validator( 'Validate::dateFormat', $registered ) - ->getFormatter( 'Format::date_sql_to_format', $registered ) - ->setFormatter( 'Format::date_format_to_sql', $registered ) - ) - ->process( $_POST ) - ->json(); diff --git a/examples/php/datetime.php b/examples/php/datetime.php deleted file mode 100644 index dbc8e84..0000000 --- a/examples/php/datetime.php +++ /dev/null @@ -1,53 +0,0 @@ -fields( - Field::inst( 'first_name' ), - Field::inst( 'last_name' ), - Field::inst( 'updated_date' ) - ->validator( 'Validate::dateFormat', array( - 'empty' => false, - 'format' => 'm-d-Y g:i A' - ) ) - ->getFormatter( 'Format::datetime', array( - 'from' => 'Y-m-d H:i:s', - 'to' => 'm-d-Y g:i A' - ) ) - ->setFormatter( 'Format::datetime', array( - 'from' => 'm-d-Y g:i A', - 'to' => 'Y-m-d H:i:s' - ) ), - Field::inst( 'registered_date' ) - ->validator( 'Validate::dateFormat', array( - 'format' => 'j M Y H:i' - ) ) - ->getFormatter( 'Format::datetime', array( - 'from' => 'Y-m-d H:i:s', - 'to' => 'j M Y H:i' - ) ) - ->setFormatter( 'Format::datetime', array( - 'from' => 'j M Y H:i', - 'to' => 'Y-m-d H:i:s' - ) ) - ) - ->process( $_POST ) - ->json(); diff --git a/examples/php/join.php b/examples/php/join.php deleted file mode 100644 index 0297294..0000000 --- a/examples/php/join.php +++ /dev/null @@ -1,36 +0,0 @@ -field( - Field::inst( 'users.first_name' ), - Field::inst( 'users.last_name' ), - Field::inst( 'users.phone' ), - Field::inst( 'users.site' ) - ->options( Options::inst() - ->table( 'sites' ) - ->value( 'id' ) - ->label( 'name' ) - ) - ->validator( 'Validate::dbValues' ), - Field::inst( 'sites.name' ) - ) - ->leftJoin( 'sites', 'sites.id', '=', 'users.site' ) - ->process($_POST) - ->json(); diff --git a/examples/php/joinArray.php b/examples/php/joinArray.php deleted file mode 100644 index 5ef9e99..0000000 --- a/examples/php/joinArray.php +++ /dev/null @@ -1,50 +0,0 @@ -field( - Field::inst( 'users.first_name' ), - Field::inst( 'users.last_name' ), - Field::inst( 'users.site' ) - ->options( Options::inst() - ->table( 'sites' ) - ->value( 'id' ) - ->label( 'name' ) - ), - Field::inst( 'sites.name' ) - ) - ->leftJoin( 'sites', 'sites.id', '=', 'users.site' ) - ->join( - Mjoin::inst( 'permission' ) - ->link( 'users.id', 'user_permission.user_id' ) - ->link( 'permission.id', 'user_permission.permission_id' ) - ->order( 'name asc' ) - ->fields( - Field::inst( 'id' ) - ->validator( 'Validate::required' ) - ->options( Options::inst() - ->table( 'permission' ) - ->value( 'id' ) - ->label( 'name' ) - ), - Field::inst( 'name' ) - ) - ) - ->process($_POST) - ->json(); diff --git a/examples/php/joinLinkTable.php b/examples/php/joinLinkTable.php deleted file mode 100644 index 6601028..0000000 --- a/examples/php/joinLinkTable.php +++ /dev/null @@ -1,44 +0,0 @@ -field( - Field::inst( 'users.first_name' ), - Field::inst( 'users.last_name' ), - Field::inst( 'users.site' ) - ->options( Options::inst() - ->table( 'sites' ) - ->value( 'id' ) - ->label( 'name' ) - ), - Field::inst( 'sites.name' ), - Field::inst( 'user_dept.dept_id' ) - ->options( Options::inst() - ->table( 'dept' ) - ->value( 'id' ) - ->label( 'name' ) - ), - Field::inst( 'dept.name' ) - ) - ->leftJoin( 'sites', 'sites.id', '=', 'users.site' ) - ->leftJoin( 'user_dept', 'users.id', '=', 'user_dept.user_id' ) - ->leftJoin( 'dept', 'user_dept.dept_id', '=', 'dept.id' ) - ->process($_POST) - ->json(); diff --git a/examples/php/joinSelf.php b/examples/php/joinSelf.php deleted file mode 100644 index 54a2a11..0000000 --- a/examples/php/joinSelf.php +++ /dev/null @@ -1,40 +0,0 @@ -debug( true ) - ->field( - Field::inst( 'users.first_name' ), - Field::inst( 'users.last_name' ), - Field::inst( 'users.manager' ) - ->options( Options::inst() - ->table( 'users' ) - ->value( 'id' ) - ->label( array('first_name', 'last_name') ) - ), - Field::inst( 'manager.first_name' ), - Field::inst( 'manager.last_name' ) - ) - ->leftJoin( 'users as manager', 'users.manager', '=', 'manager.id' ) - ->process($_POST) - ->json(); diff --git a/examples/php/jsonId.php b/examples/php/jsonId.php deleted file mode 100644 index 6e65c37..0000000 --- a/examples/php/jsonId.php +++ /dev/null @@ -1,52 +0,0 @@ -fields( - Field::inst( 'id' )->set(false), // ID is automatically set by the database on create - Field::inst( 'first_name' )->validator( 'Validate::notEmpty' ), - Field::inst( 'last_name' )->validator( 'Validate::notEmpty' ), - Field::inst( 'position' ), - Field::inst( 'email' ), - Field::inst( 'office' ), - Field::inst( 'extn' )->validator( 'Validate::numeric' ), - Field::inst( 'age' )->validator( 'Validate::numeric' ), - Field::inst( 'salary' )->validator( 'Validate::numeric' ), - Field::inst( 'start_date' ) - ->validator( 'Validate::dateFormat', array( - "format" => Format::DATE_ISO_8601, - "message" => "Please enter a date in the format yyyy-mm-dd" - ) ) - ->getFormatter( 'Format::date_sql_to_format', Format::DATE_ISO_8601 ) - ->setFormatter( 'Format::date_format_to_sql', Format::DATE_ISO_8601 ) - ) - ->process( $_POST ) - ->data(); - -// On 'read' remove the DT_RowId property so we can see fully how the `idSrc` -// option works on the client-side. -if ( Editor::action( $_POST ) === Editor::ACTION_READ ) { - for ( $i=0, $ien=count($out['data']) ; $i<$ien ; $i++ ) { - unset( $out['data'][$i]['DT_RowId'] ); - } -} - -// Send it back to the client -echo json_encode( $out ); diff --git a/examples/php/rest/_notes/dwsync.xml b/examples/php/rest/_notes/dwsync.xml deleted file mode 100644 index fb8385b..0000000 --- a/examples/php/rest/_notes/dwsync.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/examples/php/rest/create.php b/examples/php/rest/create.php deleted file mode 100644 index 5a804db..0000000 --- a/examples/php/rest/create.php +++ /dev/null @@ -1,18 +0,0 @@ -process($_POST) - ->data(); - -// If there is an error, indicate it like a REST service would with a status code -if ( isset($data['fieldErrors']) && count($data['fieldErrors']) ) { - http_response_code( 400 ); -} - -echo json_encode( $data ); \ No newline at end of file diff --git a/examples/php/rest/edit.php b/examples/php/rest/edit.php deleted file mode 100644 index 2e9560e..0000000 --- a/examples/php/rest/edit.php +++ /dev/null @@ -1,22 +0,0 @@ -process($args) - ->data(); - -// If there is an error, indicate it like a REST service would with a status code -if ( isset($data['fieldErrors']) && count($data['fieldErrors']) ) { - http_response_code( 400 ); -} - -echo json_encode( $data ); diff --git a/examples/php/rest/get.php b/examples/php/rest/get.php deleted file mode 100644 index 070eb3c..0000000 --- a/examples/php/rest/get.php +++ /dev/null @@ -1,12 +0,0 @@ -process($_POST) - ->json(); - diff --git a/examples/php/rest/remove.php b/examples/php/rest/remove.php deleted file mode 100644 index 92f2e3d..0000000 --- a/examples/php/rest/remove.php +++ /dev/null @@ -1,12 +0,0 @@ -process( $_GET ) - ->json(); - diff --git a/examples/php/rest/staff-rest.php b/examples/php/rest/staff-rest.php deleted file mode 100644 index 42fd889..0000000 --- a/examples/php/rest/staff-rest.php +++ /dev/null @@ -1,38 +0,0 @@ -fields( - Field::inst( 'first_name' )->validator( 'Validate::notEmpty' ), - Field::inst( 'last_name' )->validator( 'Validate::notEmpty' ), - Field::inst( 'position' ), - Field::inst( 'email' ), - Field::inst( 'office' ), - Field::inst( 'extn' )->validator( 'Validate::numeric' ), - Field::inst( 'age' )->validator( 'Validate::numeric' ), - Field::inst( 'salary' )->validator( 'Validate::numeric' ), - Field::inst( 'start_date' ) - ->validator( 'Validate::dateFormat', array( - "format" => Format::DATE_ISO_8601, - "message" => "Please enter a date in the format yyyy-mm-dd" - ) ) - ->getFormatter( 'Format::date_sql_to_format', Format::DATE_ISO_8601 ) - ->setFormatter( 'Format::date_format_to_sql', Format::DATE_ISO_8601 ) - ); diff --git a/examples/php/sequence.php b/examples/php/sequence.php deleted file mode 100644 index 27621de..0000000 --- a/examples/php/sequence.php +++ /dev/null @@ -1,52 +0,0 @@ -debug( true ) - ->fields( - Field::inst( 'title' )->validator( 'Validate::notEmpty' ), - Field::inst( 'author' )->validator( 'Validate::notEmpty' ), - Field::inst( 'duration' )->validator( 'Validate::notEmpty' ), - Field::inst( 'readingOrder' )->validator( 'Validate::numeric' ) - ) - ->on( 'preCreate', function ( $editor, $values ) { - // On create update all the other records to make room for our new one - $editor->db() - ->query( 'update', 'audiobooks' ) - ->set( 'readingOrder', 'readingOrder+1', false ) - ->where( 'readingOrder', $values['readingOrder'], '>=' ) - ->exec(); - } ) - ->on( 'preRemove', function ( $editor, $id, $values ) { - // On remove, the sequence needs to be updated to decrement all rows - // beyond the deleted row. Get the current reading order by id (don't - // use the submitted value in case of a multi-row delete). - $order = $editor->db() - ->select( 'audiobooks', 'readingOrder', array('id' => $id) ) - ->fetch(); - - $editor->db() - ->query( 'update', 'audiobooks' ) - ->set( 'readingOrder', 'readingOrder-1', false ) - ->where( 'readingOrder', $order['readingOrder'], '>' ) - ->exec(); - } ) - ->process( $_POST ) - ->json(); diff --git a/examples/php/staff-html.php b/examples/php/staff-html.php deleted file mode 100644 index 6b3e3e2..0000000 --- a/examples/php/staff-html.php +++ /dev/null @@ -1,34 +0,0 @@ -fields( - Field::inst( 'first_name' )->validator( 'Validate::notEmpty' ), - Field::inst( 'last_name' )->validator( 'Validate::notEmpty' ), - Field::inst( 'position' ), - Field::inst( 'office' ), - Field::inst( 'salary' ) - ->validator( 'Validate::numeric' ) - ->getFormatter( function ( $val ) { - return '$'.number_format($val); - } ) - ->setFormatter( 'Format::ifEmpty', null ) - ) - ->process( $_POST ) - ->json(); diff --git a/examples/php/staff.php b/examples/php/staff.php deleted file mode 100644 index 5156e8f..0000000 --- a/examples/php/staff.php +++ /dev/null @@ -1,83 +0,0 @@ -fields( - Field::inst( 'incomes.advertiser' ) - ->options( Options::inst() - ->table( 'advertisers' ) - ->value( 'id' ) - ->label( 'nombre' ) - ) - ->validator( 'Validate::dbValues' ), - Field::inst( 'advertisers.nombre' ), - Field::inst( 'incomes.am' ) - ->options( Options::inst() - ->table( 'advertisers' ) - ->value( 'id' ) - ->label( 'id_am' ) - ) - ->validator( 'Validate::dbValues' ), - Field::inst( 'advertisers.id_am' ), - Field::inst( 'incomes.clicks' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.conversion' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.revenue' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.platform_revenue' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.difference' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.scrubs' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.hold' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.validated' ) - ->validator( 'Validate::numeric' ) - ->setFormatter( 'Format::ifEmpty', null ), - Field::inst( 'incomes.status' ) - ->options( Options::inst() - ->table( 'status' ) - ->value( 'id' ) - ->label( 'name' ) - ) - ->validator( 'Validate::dbValues' ), - Field::inst( 'status.name' ), - Field::inst( 'incomes.notas_am' ), - Field::inst( 'incomes.notas_finance' ), - Field::inst( 'incomes.date' ) - //->getFormatter( 'Format::date_sql_to_format', 'Ymd' ) - //->setFormatter( 'Format::date_format_to_sql', 'Ymd' ) - ) - //->leftJoin( 'advertisers', 'advertisers.id', '=', 'incomes.advertisers' ) - ->leftJoin( 'advertisers', 'advertisers.id', '=', 'incomes.advertiser' ) - ->leftJoin( 'status', 'status.id', '=', 'incomes.status' ) - ->process( $_POST ) - ->json(); - - diff --git a/examples/php/standalone.php b/examples/php/standalone.php deleted file mode 100644 index ee51151..0000000 --- a/examples/php/standalone.php +++ /dev/null @@ -1,14 +0,0 @@ - array( $_POST['data']['keyless'] ) -) ); - diff --git a/examples/php/tableOnlyData.php b/examples/php/tableOnlyData.php deleted file mode 100644 index 5684894..0000000 --- a/examples/php/tableOnlyData.php +++ /dev/null @@ -1,31 +0,0 @@ -fields( - Field::inst( 'first_name' ), - Field::inst( 'last_name' ), - Field::inst( 'updated_date' ) - ->set( false ) - ->getFormatter( 'Format::date_sql_to_format', 'D, jS F Y' ) - ) - ->process( $_POST ) - ->json(); - diff --git a/examples/php/time.php b/examples/php/time.php deleted file mode 100644 index 4c99237..0000000 --- a/examples/php/time.php +++ /dev/null @@ -1,41 +0,0 @@ -fields( - Field::inst( 'first_name' ), - Field::inst( 'last_name' ), - Field::inst( 'city' ), - Field::inst( 'shift_start' ) - ->validator( 'Validate::dateFormat', array( - 'empty' => false, - 'format' => 'g:i A' - ) ) - ->getFormatter( 'Format::datetime', array( 'from' => 'H:i:s', 'to' => 'g:i A' ) ) - ->setFormatter( 'Format::datetime', array( 'from' => 'g:i A', 'to' => 'H:i:s' ) ), - Field::inst( 'shift_end' ) - ->validator( 'Validate::dateFormat', array( - 'empty' => false, - 'format' => 'H:i:s' - ) ) - ) - ->process( $_POST ) - ->json(); - diff --git a/examples/php/todo.php b/examples/php/todo.php deleted file mode 100644 index 1b75894..0000000 --- a/examples/php/todo.php +++ /dev/null @@ -1,32 +0,0 @@ -fields( - Field::inst( 'item' ), - Field::inst( 'done' ), - Field::inst( 'priority' ) - ) - ->process( $_POST ) - ->json(); - diff --git a/examples/php/upload-many.php b/examples/php/upload-many.php deleted file mode 100644 index 96233b4..0000000 --- a/examples/php/upload-many.php +++ /dev/null @@ -1,58 +0,0 @@ -fields( - Field::inst( 'users.first_name' ), - Field::inst( 'users.last_name' ), - Field::inst( 'users.phone' ), - Field::inst( 'users.site' ) - ->options( Options::inst() - ->table( 'sites' ) - ->value( 'id' ) - ->label( 'name' ) - ), - Field::inst( 'sites.name' ) - ) - ->join( - Mjoin::inst( 'files' ) - ->link( 'users.id', 'users_files.user_id' ) - ->link( 'files.id', 'users_files.file_id' ) - ->fields( - Field::inst( 'id' ) - ->upload( Upload::inst( $_SERVER['DOCUMENT_ROOT'].'/upload/__ID__.__EXTN__' ) - ->db( 'files', 'id', array( - 'filename' => Upload::DB_FILE_NAME, - 'filesize' => Upload::DB_FILE_SIZE, - 'web_path' => Upload::DB_WEB_PATH, - 'system_path' => Upload::DB_SYSTEM_PATH - ) ) - ->validator( function ( $file ) { - return$file['size'] >= 500000 ? - "Files must be smaller than 500K" : - null; - } ) - ->allowedExtensions( array( 'png', 'jpg' ), "Please upload an image" ) - ) - ) - ) - ->leftJoin( 'sites', 'sites.id', '=', 'users.site' ) - ->process( $_POST ) - ->json(); diff --git a/examples/php/upload.php b/examples/php/upload.php deleted file mode 100644 index b823c06..0000000 --- a/examples/php/upload.php +++ /dev/null @@ -1,46 +0,0 @@ -fields( - Field::inst( 'first_name' ), - Field::inst( 'last_name' ), - Field::inst( 'phone' ), - Field::inst( 'city' ), - Field::inst( 'image' ) - ->setFormatter( 'Format::ifEmpty', null ) - ->upload( Upload::inst( $_SERVER['DOCUMENT_ROOT'].'/upload/__ID__.__EXTN__' ) - ->db( 'files', 'id', array( - 'filename' => Upload::DB_FILE_NAME, - 'filesize' => Upload::DB_FILE_SIZE, - 'web_path' => Upload::DB_WEB_PATH, - 'system_path' => Upload::DB_SYSTEM_PATH - ) ) - ->validator( function ( $file ) { - return$file['size'] >= 500000 ? - "Files must be smaller than 500K" : - null; - } ) - ->allowedExtensions( array( 'png', 'jpg', 'gif' ), "Please upload an image" ) - ) - ) - ->process( $_POST ) - ->json(); diff --git a/examples/resources/_notes/dwsync.xml b/examples/resources/_notes/dwsync.xml deleted file mode 100644 index 8dd42d0..0000000 --- a/examples/resources/_notes/dwsync.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/examples/resources/demo.css b/examples/resources/demo.css deleted file mode 100644 index 4e3746e..0000000 --- a/examples/resources/demo.css +++ /dev/null @@ -1,373 +0,0 @@ - -@import url(http://fonts.googleapis.com/css?family=Raleway:100); - -body { - margin: 0; - padding: 0; -} - -body.dt-example-bootstrap { - font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; - font-size: 14px; - line-height: 1.42857143; -} - -div.container { - max-width: 980px; - margin: 0 auto; -} - -h1 { - font-family: 'HelveticaNeue-UltraLight', 'Helvetica Neue UltraLight', 'Raleway', 'Helvetica Neue', Arial, Helvetica, sans-serif; - font-weight: 100; - letter-spacing: 1px; - font-size: 3em; - line-height: 1em; -} - -h1 span { - font-size: 0.5em; - line-height: 1em; -} - -a { - cursor: pointer; - color: #3174c7; - text-decoration: none; -} - -p > a:hover { - text-decoration: underline; -} - -div.toc ul { - color: #4E6CA3; - list-style-type: none; - padding-left: 0; -} - -div.toc li { - padding: 0.2em 1em; - border-left: 4px solid transparent; - border-bottom: 1px solid #e6e6e6; -} - -div.toc li.active { - border-left: 4px solid #458ae0; -} - - -div.toc li:first-child { - border-top: 1px solid #efefef; -} - -div.toc li:last-child { - border-bottom: 1px solid #efefef; -} - - -div.epilogue { - text-align: center; -} - -p.copyright { - font-size: 0.8em; - padding-bottom: 2em; - margin-bottom: 0; -} - -.clear { - clear: both; - height: 0; -} - - -div.info { - margin-bottom: 2em; - - -webkit-column-count: 2; - -moz-column-count: 2; - -ms-column-count: 2; - -o-column-count: 2; - column-count: 2; - - - -webkit-column-rule: 1px solid #F3F3F3; - -moz-column-rule: 1px solid #F3F3F3; - -ms-column-rule: 1px solid #F3F3F3; - -o-column-rule: 1px solid #F3F3F3; - column-rule: 1px solid #F3F3F3; -} - -div.info > * { - -webkit-column-break-inside: avoid; - break-inside: avoid; -} - -div.info li { - margin-top: 0.75em; -} - -div.info p:first-child { - margin-top: 0; -} - -div.footer { - position: relative; - margin-top: 3em; - border-top: 1px solid #999; - background-color: #eee; -} - -div.footer > div.liner { - max-width: 960px; - margin: 0 auto; -} - -div.footer > div.gradient { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 6px; - - background: -moz-linear-gradient(top, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0) 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,0,0,0.2)), color-stop(100%,rgba(0,0,0,0))); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, rgba(0,0,0,0.2) 0%,rgba(0,0,0,0) 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, rgba(0,0,0,0.2) 0%,rgba(0,0,0,0) 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, rgba(0,0,0,0.2) 0%,rgba(0,0,0,0) 100%); /* IE10+ */ - background: linear-gradient(to bottom, rgba(0,0,0,0.2) 0%,rgba(0,0,0,0) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a6000000', endColorstr='#00000000',GradientType=0 ); /* IE6-9 */ -} - -div.toc { - -webkit-column-count: 2; - -moz-column-count: 2; - -ms-column-count: 2; - -o-column-count: 2; - column-count: 2; -} - -div.toc-group { - display: inline-block; - width: 100%; -} - -div.box { - overflow: auto; - height: 8em; - padding: 1em; - color: #444; - background-color: #fcfcfc; - border: 1px solid #e0e0e0; - margin-bottom: 2em; -} - - -code { - font-family: "Source Code Pro", Consolas, Menlo, Monaco, "Courier New", monospace; - padding: 1px 4px; - font-size: 0.8em; - - color: #444; - background-color: #fcfcfc; - - border: 1px solid #e0e0e0; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -code:after { - display: inline-block; - border-left: 1px solid rgba( 0, 0, 0, 0.2 ); - margin-left: 4px; - padding-left: 4px; - opacity: 0.5; -} - -a code { - text-decoration: underline; -} - -code.option { - color: #D14; /* red */ - background-color: #fcf6f8; - border: 1px solid #f7d6df; - white-space: nowrap; -} -code.option:after { content: 'Option'; } - -code.path { - color: #095c05; /* dark green */ - background-color: #eef7ed; - border: 1px solid #8ccb89; - white-space: nowrap; -} -code.path:after { content: 'Path'; } - -code.tag { - color: #c29f00; /* yellow */ - background-color: #fff9d7; - border: 1px solid #ffe700; - white-space: nowrap; -} -code.tag:after { content: 'Tag'; } - -code.api { - color: #0c199c; /* dark blue */ - background-color: #f4f5fc; - border: 1px solid #c6cbe9; - white-space: nowrap; -} -code.api:after { content: 'API'; } - -code.type { - color: #d119cf; /* pink */ - background-color: #faebfa; - border: 1px solid #f3aef2; - white-space: nowrap; -} -code.type:after { content: 'Type'; } - -code.event { - color: #2a839e; /* deep aqua */ - background-color: #f5fafb; - border: 1px solid #a8ddec; - white-space: nowrap; -} -code.event:after { content: 'Event'; } - -code.string { - color: #c05f1d; /* brown */ - background-color: #f5eee9; - border: 1px solid #dfc4b2; - white-space: nowrap; -} -code.string:after { content: 'String'; } - -code.field { - color: #ad1ee8; /* purple */ - background-color: #f9f1fc; - border: 1px solid #ebc9f7; - white-space: nowrap; -} -code.field:after { content: 'Field'; } - -code.button { - color: #464e50; /* grey */ - background-color: #f2f7f9; - border: 1px solid #b8c3c5; - white-space: nowrap; -} -code.button:after { content: 'Button'; } - -code.multiline { - display: inline-block; - width: 95%; -} - -div.syntaxhighlighter code:after { - display: none; -} - - -ul.tabs { - position: relative; - top: 1px; - height: 40px; - margin: 20px 20px 0 0; -} - - -ul.tabs li { - display: block; - float: left; - padding: 0 15px; - height: 40px; - font-size: 1.2em; - margin: 0 5px; - cursor: pointer; - line-height: 40px; - color: #121e32; - border: 1px solid white; - border-bottom: none; - margin-top: -1px; -} - -ul.tabs li.active { - border: 1px solid #ccc; - border-bottom: 1px solid white; - margin-top: 0; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} - -ul.tabs li:hover { - background-color: #fafafa; -} - -ul.tabs li.active:hover { - background-color: white; -} - -div.tabs { - clear: both; - margin-top: -1px; -} - -div.tabs>div { - padding: 15px; - border: 1px solid #ccc; - display: none; - border-radius: 5px; - box-shadow: 2px 2px 2px #bbb; -} - -div.tabs>div h1 { - border-bottom: none; - margin-top: 1em; -} - -div.tabs > div > p:first-child { - margin-top: 0; -} - -div.column_half { - float: left; - width: 49%; - padding-right: 1%; -} - - -@media only screen and (max-width : 979px) { - div.container, - div.footer { - padding: 0 1em; - } -} - -@media - screen and (max-width : 767px), - screen and (max-width : 768px) and (orientation: portrait) { - div.info { - -webkit-column-count: 1; - -moz-column-count: 1; - -ms-column-count: 1; - -o-column-count: 1; - column-count: 1; - } - - div.toc { - -webkit-column-count: 1; - -moz-column-count: 1; - -ms-column-count: 1; - -o-column-count: 1; - column-count: 1; - } - - h1 span { - display: block; - } -} - diff --git a/examples/resources/demo.js b/examples/resources/demo.js deleted file mode 100644 index 5217a4a..0000000 --- a/examples/resources/demo.js +++ /dev/null @@ -1,136 +0,0 @@ - -/*global SyntaxHighlighter*/ -SyntaxHighlighter.config.tagName = 'code'; - -if ( window.$ ) { - $(document).ready( function () { - if ( ! $.fn.dataTable ) { - return; - } - var dt110 = $.fn.dataTable.Api ? true : false; - - // Work around for WebKit bug 55740 - var info = $('div.info'); - - if ( info.height() < 115 ) { - info.css( 'min-height', '8em' ); - } - - var escapeHtml = function ( str ) { - return str.replace(/&/g, '&').replace(//g, '>'); - }; - - // css - var cssContainer = $('div.tabs div.css'); - if ( $.trim( cssContainer.find('code').text() ) === '' ) { - cssContainer.find('code, p:eq(0), div').css('display', 'none'); - } - - // init html - var table = $('

').append( $('table').clone() ).html(); - var demoHtml = $.trim( $('div.demo-html').html() ); - - if ( demoHtml ) { - demoHtml = demoHtml+'\n\n'; - } - - $('div.tabs div.table').append( - '\t\t\t'+ - escapeHtml( demoHtml + table )+ - '' - ); - //SyntaxHighlighter.highlight({}, $('#display-init-html')[0]); - - // Allow the demo code to run if DT 1.9 is used - if ( dt110 ) { - // json - var ajaxTab = $('ul.tabs li').eq(3).css('display', 'none'); - - $(document).on( 'init.dt', function ( e, settings ) { - if ( e.namespace !== 'dt' ) { - return; - } - - var api = new $.fn.dataTable.Api( settings ); - - var show = function ( str ) { - ajaxTab.css( 'display', 'block' ); - $('div.tabs div.ajax code').remove(); - $('div.tabs div.ajax div.syntaxhighlighter').remove(); - - // Old IE :-| - try { - str = JSON.stringify( str, null, 2 ); - } catch ( e ) {} - - $('div.tabs div.ajax').append( - $('').text( str ) - ); - - // This can be really slow for large builds - setTimeout( function () { - SyntaxHighlighter.highlight( {}, $('div.tabs div.ajax code')[0] ); - }, 500 ); - }; - - // First draw - var json = api.ajax.json(); - if ( json ) { - show( json ); - } - - // Subsequent draws - api.on( 'xhr.dt', function ( e, settings, json ) { - show( json ); - } ); - } ); - - // php - var phpTab = $('ul.tabs li').eq(4).css('display', 'none'); - - $(document).on( 'init.dt.demoSSP', function ( e, settings ) { - if ( e.namespace !== 'dt' ) { - return; - } - - if ( settings.oFeatures.bServerSide ) { - if ( $.isFunction( settings.ajax ) ) { - return; - } - $.ajax( { - url: '../resources/examples.php', - data: { - src: settings.sAjaxSource || settings.ajax.url || settings.ajax - }, - dataType: 'text', - type: 'post', - success: function ( txt ) { - phpTab.css( 'display', 'block' ); - $('div.tabs div.php').append( - ''+txt+'' - ); - SyntaxHighlighter.highlight( {}, $('div.tabs div.php code')[0] ); - } - } ); - } - } ); - } - else { - $('ul.tabs li').eq(3).css('display', 'none'); - $('ul.tabs li').eq(4).css('display', 'none'); - } - - // Tabs - $('ul.tabs').on( 'click', 'li', function () { - $('ul.tabs li.active').removeClass('active'); - $(this).addClass('active'); - - $('div.tabs>div') - .css('display', 'none') - .eq( $(this).index() ).css('display', 'block'); - } ); - $('ul.tabs li.active').click(); - } ); -} - - diff --git a/examples/resources/details_close.png b/examples/resources/details_close.png deleted file mode 100644 index 9c7d698..0000000 Binary files a/examples/resources/details_close.png and /dev/null differ diff --git a/examples/resources/details_open.png b/examples/resources/details_open.png deleted file mode 100644 index c0edf44..0000000 Binary files a/examples/resources/details_open.png and /dev/null differ diff --git a/examples/resources/examples.php b/examples/resources/examples.php deleted file mode 100644 index 303d3bd..0000000 --- a/examples/resources/examples.php +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/examples/resources/syntax/shCore.css b/examples/resources/syntax/shCore.css deleted file mode 100644 index 0788210..0000000 --- a/examples/resources/syntax/shCore.css +++ /dev/null @@ -1,395 +0,0 @@ -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ -.syntaxhighlighter a, -.syntaxhighlighter div, -.syntaxhighlighter code, -.syntaxhighlighter table, -.syntaxhighlighter table td, -.syntaxhighlighter table tr, -.syntaxhighlighter table tbody, -.syntaxhighlighter table thead, -.syntaxhighlighter table caption, -.syntaxhighlighter textarea { - -moz-border-radius: 0 0 0 0 !important; - -webkit-border-radius: 0 0 0 0 !important; - background: none !important; - border: 0 !important; - bottom: auto !important; - float: none !important; - height: auto !important; - left: auto !important; - line-height: 1.1em !important; - margin: 0 !important; - outline: 0 !important; - overflow: visible !important; - padding: 0 !important; - position: static !important; - right: auto !important; - text-align: left !important; - top: auto !important; - vertical-align: baseline !important; - width: auto !important; - box-sizing: content-box !important; - font-family: "Source Code Pro","Consolas","Monaco","Bitstream Vera Sans Mono","Courier New",Courier,monospace !important; - font-weight: normal !important; - font-style: normal !important; - font-size: 1em !important; - min-height: inherit !important; - min-height: auto !important; - white-space: nowrap; -} - -.syntaxhighlighter { - width: 100% !important; - margin: 1em 0 1em 0 !important; - position: relative !important; - overflow: auto !important; - font-size: 1em !important; - clear: both; - - box-shadow: inset 0 0 3px #555; - padding: 5px 3px; - background: #f8f8f8 !important; - box-sizing: border-box; -} -.syntaxhighlighter.source { - overflow: hidden !important; -} -.syntaxhighlighter .bold { - font-weight: bold !important; -} -.syntaxhighlighter .italic { - font-style: italic !important; -} -.syntaxhighlighter .line { - white-space: nowrap !important; -} -.syntaxhighlighter table { - width: 100% !important; -} -.syntaxhighlighter table caption { - text-align: left !important; - padding: .5em 0 0.5em 1em !important; -} -.syntaxhighlighter table td.code { - width: 100% !important; -} -.syntaxhighlighter table td.code .container { - position: relative !important; -} -.syntaxhighlighter table td.code .container textarea { - box-sizing: border-box !important; - position: absolute !important; - left: 0 !important; - top: 0 !important; - width: 100% !important; - height: 100% !important; - border: none !important; - background: white !important; - padding-left: 1em !important; - overflow: hidden !important; - white-space: pre !important; -} -.syntaxhighlighter table td.gutter .line { - text-align: right !important; - padding: 2px 0.5em 2px 1em !important; -} -.syntaxhighlighter table td.code .line { - padding: 2px 1em !important; -} -.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { - padding-left: 0em !important; -} -.syntaxhighlighter.show { - display: block !important; -} -.syntaxhighlighter.collapsed table { - display: none !important; -} -.syntaxhighlighter.collapsed .toolbar { - padding: 0.1em 0.8em 0em 0.8em !important; - font-size: 1em !important; - position: static !important; - width: auto !important; - height: auto !important; -} -.syntaxhighlighter.collapsed .toolbar span { - display: inline !important; - margin-right: 1em !important; -} -.syntaxhighlighter.collapsed .toolbar span a { - padding: 0 !important; - display: none !important; -} -.syntaxhighlighter.collapsed .toolbar span a.expandSource { - display: inline !important; -} -.syntaxhighlighter .toolbar { - position: absolute !important; - right: 10px !important; - top: 0 !important; - font-size: 10px !important; - z-index: 7 !important; -} -.syntaxhighlighter .toolbar span.title { - display: inline !important; -} -.syntaxhighlighter .toolbar a { - display: block !important; - text-align: center !important; - text-decoration: none !important; - padding-top: 1px !important; -} -.syntaxhighlighter .toolbar a.expandSource { - display: none !important; -} -.syntaxhighlighter.ie { - font-size: .9em !important; - padding: 1px 0 1px 0 !important; -} -.syntaxhighlighter.ie .toolbar { - line-height: 8px !important; -} -.syntaxhighlighter.ie .toolbar a { - padding-top: 0px !important; -} -.syntaxhighlighter.printing .line.alt1 .content, -.syntaxhighlighter.printing .line.alt2 .content, -.syntaxhighlighter.printing .line.highlighted .number, -.syntaxhighlighter.printing .line.highlighted.alt1 .content, -.syntaxhighlighter.printing .line.highlighted.alt2 .content { - background: none !important; -} -.syntaxhighlighter.printing .line .number { - color: #bbbbbb !important; -} -.syntaxhighlighter.printing .line .content { - color: black !important; -} -.syntaxhighlighter.printing .toolbar { - display: none !important; -} -.syntaxhighlighter.printing a { - text-decoration: none !important; -} -.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { - color: black !important; -} -.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { - color: #008200 !important; -} -.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { - color: blue !important; -} -.syntaxhighlighter.printing .keyword { - color: #006699 !important; - font-weight: bold !important; -} -.syntaxhighlighter.printing .preprocessor { - color: gray !important; -} -.syntaxhighlighter.printing .variable { - color: #aa7700 !important; -} -.syntaxhighlighter.printing .value { - color: #009900 !important; -} -.syntaxhighlighter.printing .functions { - color: #ff1493 !important; -} -.syntaxhighlighter.printing .constants { - color: #0066cc !important; -} -.syntaxhighlighter.printing .script { - font-weight: bold !important; -} -.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { - color: gray !important; -} -.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { - color: #ff1493 !important; -} -.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { - color: red !important; -} -.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { - color: black !important; -} - - - -/** - * SyntaxHighlighter - * http://alexgorbatchev.com/SyntaxHighlighter - * - * SyntaxHighlighter is donationware. If you are using it, please donate. - * http://alexgorbatchev.com/SyntaxHighlighter/donate.html - * - * @version - * 3.0.83 (July 02 2010) - * - * @copyright - * Copyright (C) 2004-2010 Alex Gorbatchev. - * - * @license - * Dual licensed under the MIT and GPL licenses. - */ -.syntaxhighlighter { - font-size: 13px !important; - overflow: visible !important; -} -.syntaxhighlighter .line.alt1 { - background-color: white !important; -} -.syntaxhighlighter .line.alt2 { - background-color: #F8F8F8 !important; -} -.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { - background-color: #e0e0e0 !important; -} -.syntaxhighlighter .line.highlighted.number { - color: black !important; -} -.syntaxhighlighter table caption { - color: black !important; -} -.syntaxhighlighter .gutter { -} -.syntaxhighlighter .gutter div { - color: #5C5C5C !important; -} -.syntaxhighlighter .gutter .line.alt1, .syntaxhighlighter .gutter .line.alt2 { - background-color: transparent !important; -} -.odd .syntaxhighlighter .gutter .line.alt1, .odd .syntaxhighlighter .gutter .line.alt2 { - background-color: #F2F2F2 !important; -} -.syntaxhighlighter .gutter .line { - border-right: 3px solid #4E6CA3 !important; -} -.syntaxhighlighter .gutter .line.highlighted { - background-color: #4E6CA3 !important; - color: white !important; -} -.syntaxhighlighter.printing .line .content { - border: none !important; -} -.syntaxhighlighter.collapsed { - overflow: visible !important; -} -.syntaxhighlighter.collapsed .toolbar { - color: blue !important; - background: white !important; - border: 1px solid #4E6CA3 !important; -} -.syntaxhighlighter.collapsed .toolbar a { - color: blue !important; -} -.syntaxhighlighter.collapsed .toolbar a:hover { - color: red !important; -} -.syntaxhighlighter .toolbar { - color: white !important; - border: none !important; -} -.syntaxhighlighter .toolbar a { - font: 100%/1.45em "Lucida Grande", Verdana, Arial, Helvetica, sans-serif !important; - color: white !important; - background: #4E6CA3 !important; - float: right !important; - padding: 2px 5px !important; - clear: both; -} -.syntaxhighlighter .toolbar a:hover { - color: #b7c5df !important; - background: #39568b !important; -} -.syntaxhighlighter .plain, .syntaxhighlighter .plain a { - color: black !important; -} -.syntaxhighlighter .comments, .syntaxhighlighter .comments a { - color: #008200 !important; -} -.syntaxhighlighter .string, .syntaxhighlighter .string a { - color: blue !important; -} -.syntaxhighlighter .keyword { - color: #006699 !important; -} -.syntaxhighlighter .preprocessor { - color: gray !important; -} -.syntaxhighlighter .variable { - color: #aa7700 !important; -} -.syntaxhighlighter .value { - color: #009900 !important; -} -.syntaxhighlighter .functions { - color: #ff1493 !important; -} -.syntaxhighlighter .constants { - color: #0066cc !important; -} -.syntaxhighlighter .script { - font-weight: bold !important; - color: #006699 !important; - background-color: none !important; -} -.syntaxhighlighter .color1, .syntaxhighlighter .color1 a { - color: gray !important; -} -.syntaxhighlighter .color2, .syntaxhighlighter .color2 a { - color: #ff1493 !important; -} -.syntaxhighlighter .color3, .syntaxhighlighter .color3 a { - color: red !important; -} - -.syntaxhighlighter .keyword { - font-weight: bold !important; -} - -.datatables_ref:hover { - text-decoration: underline; - cursor: pointer; - *cursor: hand; -} - -.syntaxhighlighter .dtapi { - color: #069; -} - -.syntaxhighlighter .dtapi:hover { - text-decoration: underline; - cursor: pointer; - *cursor: hand; -} - -.syntaxhighlighter table { - table-layout: fixed !important; -} - -.syntaxhighlighter table td.gutter { - width: 46px !important; /* enough for three digits */ -} - -.syntaxhighlighter table td.code { - width: auto !important; - overflow: auto !important; -} - diff --git a/examples/resources/syntax/shCore.js b/examples/resources/syntax/shCore.js deleted file mode 100644 index 16a37e8..0000000 --- a/examples/resources/syntax/shCore.js +++ /dev/null @@ -1,2138 +0,0 @@ -/*! - * XRegExp 2.0.0 MIT License - */ -var XRegExp;XRegExp=XRegExp||function(n){"use strict";function v(n,i,r){var u;for(u in t.prototype)t.prototype.hasOwnProperty(u)&&(n[u]=t.prototype[u]);return n.xregexp={captureNames:i,isNative:!!r},n}function g(n){return(n.global?"g":"")+(n.ignoreCase?"i":"")+(n.multiline?"m":"")+(n.extended?"x":"")+(n.sticky?"y":"")}function o(n,r,u){if(!t.isRegExp(n))throw new TypeError("type RegExp expected");var f=i.replace.call(g(n)+(r||""),h,"");return u&&(f=i.replace.call(f,new RegExp("["+u+"]+","g"),"")),n=n.xregexp&&!n.xregexp.isNative?v(t(n.source,f),n.xregexp.captureNames?n.xregexp.captureNames.slice(0):null):v(new RegExp(n.source,f),null,!0)}function a(n,t){var i=n.length;if(Array.prototype.lastIndexOf)return n.lastIndexOf(t);while(i--)if(n[i]===t)return i;return-1}function s(n,t){return Object.prototype.toString.call(n).toLowerCase()==="[object "+t+"]"}function d(n){return n=n||{},n==="all"||n.all?n={natives:!0,extensibility:!0}:s(n,"string")&&(n=t.forEach(n,/[^\s,]+/,function(n){this[n]=!0},{})),n}function ut(n,t,i,u){var o=p.length,s=null,e,f;y=!0;try{while(o--)if(f=p[o],(f.scope==="all"||f.scope===i)&&(!f.trigger||f.trigger.call(u))&&(f.pattern.lastIndex=t,e=r.exec.call(f.pattern,n),e&&e.index===t)){s={output:f.handler.call(u,e,i),match:e};break}}catch(h){throw h;}finally{y=!1}return s}function b(n){t.addToken=c[n?"on":"off"],f.extensibility=n}function tt(n){RegExp.prototype.exec=(n?r:i).exec,RegExp.prototype.test=(n?r:i).test,String.prototype.match=(n?r:i).match,String.prototype.replace=(n?r:i).replace,String.prototype.split=(n?r:i).split,f.natives=n}var t,c,u,f={natives:!1,extensibility:!1},i={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},r={},k={},p=[],e="default",rt="class",it={"default":/^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/,"class":/^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/},et=/\$(?:{([\w$]+)}|(\d\d?|[\s\S]))/g,h=/([\s\S])(?=[\s\S]*\1)/g,nt=/^(?:[?*+]|{\d+(?:,\d*)?})\??/,ft=i.exec.call(/()??/,"")[1]===n,l=RegExp.prototype.sticky!==n,y=!1,w="gim"+(l?"y":"");return t=function(r,u){if(t.isRegExp(r)){if(u!==n)throw new TypeError("can't supply flags when constructing one RegExp from another");return o(r)}if(y)throw new Error("can't call the XRegExp constructor within token definition functions");var l=[],a=e,b={hasNamedCapture:!1,captureNames:[],hasFlag:function(n){return u.indexOf(n)>-1}},f=0,c,s,p;if(r=r===n?"":String(r),u=u===n?"":String(u),i.match.call(u,h))throw new SyntaxError("invalid duplicate regular expression flag");for(r=i.replace.call(r,/^\(\?([\w$]+)\)/,function(n,t){if(i.test.call(/[gy]/,t))throw new SyntaxError("can't use flag g or y in mode modifier");return u=i.replace.call(u+t,h,""),""}),t.forEach(u,/[\s\S]/,function(n){if(w.indexOf(n[0])<0)throw new SyntaxError("invalid regular expression flag "+n[0]);});f"}else if(i)return"\\"+(+i+f);return n},e=[],r,u;if(!(s(n,"array")&&n.length))throw new TypeError("patterns must be a nonempty array");for(u=0;u1&&a(r,"")>-1&&(e=new RegExp(this.source,i.replace.call(g(this),"g","")),i.replace.call(String(t).slice(r.index),e,function(){for(var t=1;tr.index&&(this.lastIndex=r.index)}return this.global||(this.lastIndex=o),r},r.test=function(n){return!!r.exec.call(this,n)},r.match=function(n){if(t.isRegExp(n)){if(n.global){var u=i.match.apply(this,arguments);return n.lastIndex=0,u}}else n=new RegExp(n);return r.exec.call(n,this)},r.replace=function(n,r){var e=t.isRegExp(n),u,f,h,o;return e?(n.xregexp&&(u=n.xregexp.captureNames),n.global||(o=n.lastIndex)):n+="",s(r,"function")?f=i.replace.call(String(this),n,function(){var t=arguments,i;if(u)for(t[0]=new String(t[0]),i=0;in.length-3)throw new SyntaxError("backreference to undefined group "+t);return n[r]||""}throw new SyntaxError("invalid token "+t);})})),e&&(n.lastIndex=n.global?0:o),f},r.split=function(r,u){if(!t.isRegExp(r))return i.split.apply(this,arguments);var e=String(this),h=r.lastIndex,f=[],o=0,s;return u=(u===n?-1:u)>>>0,t.forEach(e,r,function(n){n.index+n[0].length>o&&(f.push(e.slice(o,n.index)),n.length>1&&n.indexu?f.slice(0,u):f},u=c.on,u(/\\([ABCE-RTUVXYZaeg-mopqyz]|c(?![A-Za-z])|u(?![\dA-Fa-f]{4})|x(?![\dA-Fa-f]{2}))/,function(n,t){if(n[1]==="B"&&t===e)return n[0];throw new SyntaxError("invalid escape "+n[0]);},{scope:"all"}),u(/\[(\^?)]/,function(n){return n[1]?"[\\s\\S]":"\\b\\B"}),u(/(?:\(\?#[^)]*\))+/,function(n){return i.test.call(nt,n.input.slice(n.index+n[0].length))?"":"(?:)"}),u(/\\k<([\w$]+)>/,function(n){var t=isNaN(n[1])?a(this.captureNames,n[1])+1:+n[1],i=n.index+n[0].length;if(!t||t>this.captureNames.length)throw new SyntaxError("backreference to undefined group "+n[0]);return"\\"+t+(i===n.input.length||isNaN(n.input.charAt(i))?"":"(?:)")}),u(/(?:\s+|#.*)+/,function(n){return i.test.call(nt,n.input.slice(n.index+n[0].length))?"":"(?:)"},{trigger:function(){return this.hasFlag("x")},customFlags:"x"}),u(/\./,function(){return"[\\s\\S]"},{trigger:function(){return this.hasFlag("s")},customFlags:"s"}),u(/\(\?P?<([\w$]+)>/,function(n){if(!isNaN(n[1]))throw new SyntaxError("can't use integer as capture name "+n[0]);return this.captureNames.push(n[1]),this.hasNamedCapture=!0,"("}),u(/\\(\d+)/,function(n,t){if(!(t===e&&/^[1-9]/.test(n[1])&&+n[1]<=this.captureNames.length)&&n[1]!=="0")throw new SyntaxError("can't use octal escape or backreference to undefined group "+n[0]);return n[0]},{scope:"all"}),u(/\((?!\?)/,function(){return this.hasFlag("n")?"(?:":(this.captureNames.push(null),"(")},{customFlags:"n"}),typeof exports!="undefined"&&(exports.XRegExp=t),t}() - - -/*! - * SyntaxHighlighter by Alex Gorbatchev - * https://github.com/alexgorbatchev/SyntaxHighlighter - MIT license - */ - -// -// Begin anonymous function. This is used to contain local scope variables without polutting global scope. -// -if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() { - -// CommonJS -if (typeof(require) != 'undefined' && typeof(XRegExp) == 'undefined') -{ - XRegExp = require('xregexp').XRegExp; -} - -// Shortcut object which will be assigned to the SyntaxHighlighter variable. -// This is a shorthand for local reference in order to avoid long namespace -// references to SyntaxHighlighter.whatever... -var sh = { - defaults : { - /** Additional CSS class names to be added to highlighter elements. */ - 'class-name' : '', - - /** First line number. */ - 'first-line' : 1, - - /** - * Pads line numbers. Possible values are: - * - * false - don't pad line numbers. - * true - automaticaly pad numbers with minimum required number of leading zeroes. - * [int] - length up to which pad line numbers. - */ - 'pad-line-numbers' : false, - - /** Lines to highlight. */ - 'highlight' : null, - - /** Title to be displayed above the code block. */ - 'title' : null, - - /** Enables or disables smart tabs. */ - 'smart-tabs' : true, - - /** Gets or sets tab size. */ - 'tab-size' : 4, - - /** Enables or disables gutter. */ - 'gutter' : true, - - /** Enables or disables toolbar. */ - 'toolbar' : true, - - /** Enables quick code copy and paste from double click. */ - 'quick-code' : true, - - /** Forces code view to be collapsed. */ - 'collapse' : false, - - /** Enables or disables automatic links. */ - 'auto-links' : true, - - /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */ - 'light' : false, - - 'unindent' : true, - - 'html-script' : false - }, - - config : { - space : ' ', - - /** Enables use of - - - - - - -

-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- picture -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-

Components

-
-
-
-
-
-
-
-
-
-

General Items

-
-
- - - Button - Button -
-
Radio Button
-
- - - - -
- -
Checkbox
-
- - - - - -
-
-
-
-
-
-
-
-

Slider

-
-
- Orange -
- - -
- Green -
- - -
- Purple -
- - -
-
-
-
- -
-
-
-
-
-
-

Tags

-
-
- - -
-
-
-
-
-
-

Tree

-
-
-
- -
    -
  • Root node 1 -
      -
    • Child node 1
    • -
    • Child node 2 -
        -
      • Child node 1
      • -
      • Child node 2
      • -
      -
    • -
    -
  • -
  • Root node 2
  • -
-
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - \ No newline at end of file diff --git a/home.php b/home.php deleted file mode 100644 index 6687280..0000000 --- a/home.php +++ /dev/null @@ -1,39 +0,0 @@ -updateUser(array( - "name" => $_POST['newName'] - )); -} -?> - - - -

Welcome

-

You have been successfully logged in.

-

- Log Out -

-

- You registered on this website joinedSince(); ?> ago. -

-

- Here is the full data, the database stores on this user : -

-
getUser();
-			print_r($details);
-			?>
-

- Change the name of your account : -

-
- - -
-

- Change Password -

- - diff --git a/img/23.jpg b/img/23.jpg deleted file mode 100644 index 096caf6..0000000 Binary files a/img/23.jpg and /dev/null differ diff --git a/img/32.jpg b/img/32.jpg deleted file mode 100644 index e49380c..0000000 Binary files a/img/32.jpg and /dev/null differ diff --git a/img/67.jpg b/img/67.jpg deleted file mode 100644 index 6beeeb4..0000000 Binary files a/img/67.jpg and /dev/null differ diff --git a/img/78.jpg b/img/78.jpg deleted file mode 100644 index a7eccd3..0000000 Binary files a/img/78.jpg and /dev/null differ diff --git a/img/_notes/dwsync.xml b/img/_notes/dwsync.xml deleted file mode 100644 index 4ef4d1a..0000000 --- a/img/_notes/dwsync.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/img/destroy.png b/img/destroy.png deleted file mode 100644 index 56d7637..0000000 Binary files a/img/destroy.png and /dev/null differ diff --git a/img/logo.png b/img/logo.png deleted file mode 100644 index 3d5f029..0000000 Binary files a/img/logo.png and /dev/null differ diff --git a/import_csv.php b/import_csv.php deleted file mode 100644 index 5b5d968..0000000 --- a/import_csv.php +++ /dev/null @@ -1,56 +0,0 @@ -query($sqlInfo); - if(mysqli_num_rows($result) > 0 ) - { - while($row = $result->fetch_assoc()) - { - echo "--------------existe el advertiser: " .$emapData[0] ." con ID: ".$row["id"]."-----------------
"; - $sqlIncomes = "INSERT INTO incomes(id,advertiser,conversion,revenue,date) VALUES ('','".$row['id']."','$emapData[1]','$emapData[2]','$emapData[3]')"; - $insertIncome = $con->query($sqlIncomes); - echo $sqlIncomes."
"; - - } - } - else - { - echo "-----------------NO existe el advertiser: " .$emapData[0] ."-----------------------
"; - $queryNewAdv = "INSERT INTO advertisers VALUES ('', '$emapData[0]', '')"; - $insertAdv = $con->query($queryNewAdv); - $NewID = $con->insert_id; - echo "Advertiser inserted with the new id: ". $NewID ."
"; - echo "insertando...
"; - $sqlIncomes = "INSERT INTO incomes(id,advertiser,conversion,revenue,date) VALUES ('','$NewID','$emapData[1]','$emapData[2]','$emapData[3]')"; - $insertIncome = $con->query($sqlIncomes); - } - - } - fclose($file); - echo "CSV File has been successfully Imported."; - } - else { - echo "Error: Please Upload only CSV File"; - } - - $con->close(); -} -?> \ No newline at end of file diff --git a/inbox.html b/inbox.html deleted file mode 100644 index 53b5b78..0000000 --- a/inbox.html +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - Inbox - - - - - - - - - -
-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- picture -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-

Inbox

-
-
-
-
-
-
-
-
-
-
-
-
-
- Inbox -
-
- Important -
-
- Sent -
-
- Draft -
-
- Trash -
-
-
-
-
-
-
- Compose Mail - - - - - - - - - - - - - - - - - - - - - - - - - -
Wonderful theme - 10/20/204 - - - Tom -
Wonderful theme - 10/20/204 - - - Jon -
Wonderful theme - 10/20/204 - - - Jon -
Wonderful theme - 10/20/204 - - - Jon -
- -
-
-
-
-
-
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/incomes.php b/incomes.php deleted file mode 100644 index cf1339b..0000000 --- a/incomes.php +++ /dev/null @@ -1,675 +0,0 @@ -updateUser(array( - "name" => $_POST['newName'] - )); -} -?> - - - - - - - INCOMES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- - Hi - getUser(); - echo $details['username'] ,"!"; - ?> -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-

INCOMES

-
-
-
- -
- -
-
-
- -
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AdvertAMclicksConvRevenuePlatf.AdvDiffScrubsHoldValidatedStatusdateNotas(AM)Notas(Finance)
Total:
AdvertAMclicksConvRevenuePlatf.AdvDiffScrubsHoldValidated
-
- -
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/index.php b/index.php deleted file mode 100644 index 6d2aa2d..0000000 --- a/index.php +++ /dev/null @@ -1,244 +0,0 @@ -updateUser(array( - "name" => $_POST['newName'] - )); -} -?> - - - - - - - Welcome to Smarter Admin - - - - - - - - - -
-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- - Hi - getUser(); - echo $details['username'] ,"!"; - ?> -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-

Dashboard

-
-
-
-
-
-
-
-
-
- Sales -

$97334001

-
- -
-
-
-
-
-
- Orders -

400

-
- -
-
-
-
-
-
- Profit -

$600

-
- -
-
-
-
-
-
- Users -

100

-
- -
-
-
-
-
-
-
-
-
-

Charts

-
-
-
-
-
-
-
-
-
-

Charts

-
-
-
-
-
-
-
-
-
-
-
-
-

Quick Message

-
-
- - - - - - - Send -
-
-
-
-
-
-
-

To Do

-
-
- -
    - -
-
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - \ No newline at end of file diff --git a/invoice.html b/invoice.html deleted file mode 100644 index 39d44cf..0000000 --- a/invoice.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - Invoice - - - - - - - - -
-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- picture -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-

John Smith

-
27 Borrow Street
-
New York
-
Bay Street
-
-
-
Invoice #: 90797
-
Due Date: 10/10/2014
-
-
Grand Total
-
$40.00
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Description - Unit Price - Quantity - Price -
-
Name
- sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam -
$8.00 - 1 - $20.00 -
-
Name
- tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam -
$8.00 - 1 - $20.00 -
-
Name
- tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam -
$8.00 - 8 - $20.00 -
-
Name
- tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam -
$8.00 - 1 - $20.00 -
-
-

- Subtotal : $120.00 -

-

- Tax : $20.00 -

-

- Discount : 5% -

-
Grand toal :$400.00
-
- -
-
-
-

Company name

-
-
-
We Accept
- - - -
-
-
-
Please note : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
-
-
-
-
-
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/js/_notes/dwsync.xml b/js/_notes/dwsync.xml deleted file mode 100644 index 7b42b48..0000000 --- a/js/_notes/dwsync.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/js/ckeditor/_notes/dwsync.xml b/js/ckeditor/_notes/dwsync.xml deleted file mode 100644 index a4377e8..0000000 --- a/js/ckeditor/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/js/ckeditor/ckeditor.js b/js/ckeditor/ckeditor.js deleted file mode 100644 index d35e8bc..0000000 --- a/js/ckeditor/ckeditor.js +++ /dev/null @@ -1,1013 +0,0 @@ -/* -Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.md or http://ckeditor.com/license -*/ -(function(){if(window.CKEDITOR&&window.CKEDITOR.dom)return;window.CKEDITOR||(window.CKEDITOR=function(){var a=/(^|.*[\\\/])ckeditor\.js(?:\?.*|;.*)?$/i,e={timestamp:"E8PB",version:"4.4.5",revision:"25cdcad",rnd:Math.floor(900*Math.random())+100,_:{pending:[],basePathSrcPattern:a},status:"unloaded",basePath:function(){var f=window.CKEDITOR_BASEPATH||"";if(!f)for(var d=document.getElementsByTagName("script"),c=0;c=0;t--)if(p[t].priority<=e){p.splice(t+1,0,m);return{removeListener:j}}p.unshift(m)}return{removeListener:j}}, -once:function(){var a=Array.prototype.slice.call(arguments),f=a[1];a[1]=function(a){a.removeListener();return f.apply(this,arguments)};return this.on.apply(this,a)},capture:function(){CKEDITOR.event.useCapture=1;var a=this.on.apply(this,arguments);CKEDITOR.event.useCapture=0;return a},fire:function(){var a=0,f=function(){a=1},d=0,b=function(){d=1};return function(h,m,j){var p=e(this)[h],h=a,u=d;a=d=0;if(p){var t=p.listeners;if(t.length)for(var t=t.slice(0),y,n=0;n=0&&d.listeners.splice(b,1)}},removeAllListeners:function(){var a=e(this),f;for(f in a)delete a[f]},hasListeners:function(a){return(a=e(this)[a])&&a.listeners.length> -0}}}());CKEDITOR.editor||(CKEDITOR.editor=function(){CKEDITOR._.pending.push([this,arguments]);CKEDITOR.event.call(this)},CKEDITOR.editor.prototype.fire=function(a,e){a in{instanceReady:1,loaded:1}&&(this[a]=true);return CKEDITOR.event.prototype.fire.call(this,a,e,this)},CKEDITOR.editor.prototype.fireOnce=function(a,e){a in{instanceReady:1,loaded:1}&&(this[a]=true);return CKEDITOR.event.prototype.fireOnce.call(this,a,e,this)},CKEDITOR.event.implementOn(CKEDITOR.editor.prototype)); -CKEDITOR.env||(CKEDITOR.env=function(){var a=navigator.userAgent.toLowerCase(),e={ie:a.indexOf("trident/")>-1,webkit:a.indexOf(" applewebkit/")>-1,air:a.indexOf(" adobeair/")>-1,mac:a.indexOf("macintosh")>-1,quirks:document.compatMode=="BackCompat"&&(!document.documentMode||document.documentMode<10),mobile:a.indexOf("mobile")>-1,iOS:/(ipad|iphone|ipod)/.test(a),isCustomDomain:function(){if(!this.ie)return false;var a=document.domain,d=window.location.hostname;return a!=d&&a!="["+d+"]"},secure:location.protocol== -"https:"};e.gecko=navigator.product=="Gecko"&&!e.webkit&&!e.ie;if(e.webkit)a.indexOf("chrome")>-1?e.chrome=true:e.safari=true;var b=0;if(e.ie){b=e.quirks||!document.documentMode?parseFloat(a.match(/msie (\d+)/)[1]):document.documentMode;e.ie9Compat=b==9;e.ie8Compat=b==8;e.ie7Compat=b==7;e.ie6Compat=b<7||e.quirks}if(e.gecko){var c=a.match(/rv:([\d\.]+)/);if(c){c=c[1].split(".");b=c[0]*1E4+(c[1]||0)*100+(c[2]||0)*1}}e.air&&(b=parseFloat(a.match(/ adobeair\/(\d+)/)[1]));e.webkit&&(b=parseFloat(a.match(/ applewebkit\/(\d+)/)[1])); -e.version=b;e.isCompatible=e.iOS&&b>=534||!e.mobile&&(e.ie&&b>6||e.gecko&&b>=2E4||e.air&&b>=1||e.webkit&&b>=522||false);e.hidpi=window.devicePixelRatio>=2;e.needsBrFiller=e.gecko||e.webkit||e.ie&&b>10;e.needsNbspFiller=e.ie&&b<11;e.cssClass="cke_browser_"+(e.ie?"ie":e.gecko?"gecko":e.webkit?"webkit":"unknown");if(e.quirks)e.cssClass=e.cssClass+" cke_browser_quirks";if(e.ie)e.cssClass=e.cssClass+(" cke_browser_ie"+(e.quirks?"6 cke_browser_iequirks":e.version));if(e.air)e.cssClass=e.cssClass+" cke_browser_air"; -if(e.iOS)e.cssClass=e.cssClass+" cke_browser_ios";if(e.hidpi)e.cssClass=e.cssClass+" cke_hidpi";return e}()); -"unloaded"==CKEDITOR.status&&function(){CKEDITOR.event.implementOn(CKEDITOR);CKEDITOR.loadFullCore=function(){if(CKEDITOR.status!="basic_ready")CKEDITOR.loadFullCore._load=1;else{delete CKEDITOR.loadFullCore;var a=document.createElement("script");a.type="text/javascript";a.src=CKEDITOR.basePath+"ckeditor.js";document.getElementsByTagName("head")[0].appendChild(a)}};CKEDITOR.loadFullCoreTimeout=0;CKEDITOR.add=function(a){(this._.pending||(this._.pending=[])).push(a)};(function(){CKEDITOR.domReady(function(){var a= -CKEDITOR.loadFullCore,e=CKEDITOR.loadFullCoreTimeout;if(a){CKEDITOR.status="basic_ready";a&&a._load?a():e&&setTimeout(function(){CKEDITOR.loadFullCore&&CKEDITOR.loadFullCore()},e*1E3)}})})();CKEDITOR.status="basic_loaded"}();CKEDITOR.dom={}; -(function(){var a=[],e=CKEDITOR.env.gecko?"-moz-":CKEDITOR.env.webkit?"-webkit-":CKEDITOR.env.ie?"-ms-":"",b=/&/g,c=/>/g,f=/"+f+""):d.push('');return d.join("")}, -htmlEncode:function(a){return(""+a).replace(b,"&").replace(c,">").replace(f,"<")},htmlDecode:function(a){return a.replace(g,"&").replace(h,">").replace(m,"<")},htmlEncodeAttr:function(a){return a.replace(d,""").replace(f,"<").replace(c,">")},htmlDecodeAttr:function(a){return a.replace(j,'"').replace(m,"<").replace(h,">")},getNextNumber:function(){var a=0;return function(){return++a}}(),getNextId:function(){return"cke_"+this.getNextNumber()},override:function(a,f){var d=f(a);d.prototype= -a.prototype;return d},setTimeout:function(a,f,d,b,c){c||(c=window);d||(d=c);return c.setTimeout(function(){b?a.apply(d,[].concat(b)):a.apply(d)},f||0)},trim:function(){var a=/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;return function(f){return f.replace(a,"")}}(),ltrim:function(){var a=/^[ \t\n\r]+/g;return function(f){return f.replace(a,"")}}(),rtrim:function(){var a=/[ \t\n\r]+$/g;return function(f){return f.replace(a,"")}}(),indexOf:function(a,f){if(typeof f=="function")for(var d=0,b=a.length;d=0?a[d]:null},bind:function(a,f){return function(){return a.apply(f,arguments)}},createClass:function(a){var f=a.$,d=a.base,b=a.privates||a._,c=a.proto,a=a.statics;!f&&(f=function(){d&&this.base.apply(this,arguments)});if(b)var e=f,f=function(){var a=this._||(this._={}),f;for(f in b){var d=b[f];a[f]=typeof d=="function"?CKEDITOR.tools.bind(d,this):d}e.apply(this,arguments)};if(d){f.prototype= -this.prototypedCopy(d.prototype);f.prototype.constructor=f;f.base=d;f.baseProto=d.prototype;f.prototype.base=function(){this.base=d.prototype.base;d.apply(this,arguments);this.base=arguments.callee}}c&&this.extend(f.prototype,c,true);a&&this.extend(f,a,true);return f},addFunction:function(f,d){return a.push(function(){return f.apply(d||this,arguments)})-1},removeFunction:function(f){a[f]=null},callFunction:function(f){var d=a[f];return d&&d.apply(window,Array.prototype.slice.call(arguments,1))},cssLength:function(){var a= -/^-?\d+\.?\d*px$/,f;return function(d){f=CKEDITOR.tools.trim(d+"")+"px";return a.test(f)?f:d||""}}(),convertToPx:function(){var a;return function(f){if(!a){a=CKEDITOR.dom.element.createFromHtml('
',CKEDITOR.document);CKEDITOR.document.getBody().append(a)}if(!/%$/.test(f)){a.setStyle("width",f);return a.$.clientWidth}return f}}(),repeat:function(a,f){return Array(f+1).join(a)},tryThese:function(){for(var a, -f=0,d=arguments.length;f]*?>)|^/i,'$&\n - - - - -
-
-
-
- logo - -
- - - - - -
- -

Don't have an account ? - Register -

- - -

Forgot Your Password ? - Reset Password -

- - {$msg[0]}

{$msg[1]}

"; - } - ?> -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/logout.php b/logout.php deleted file mode 100644 index 6f395b0..0000000 --- a/logout.php +++ /dev/null @@ -1,4 +0,0 @@ -logout(); -?> diff --git a/mysql/_notes/dwsync.xml b/mysql/_notes/dwsync.xml deleted file mode 100644 index f52e4a3..0000000 --- a/mysql/_notes/dwsync.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/mysql/advance2.sql b/mysql/advance2.sql deleted file mode 100644 index fb0775e..0000000 --- a/mysql/advance2.sql +++ /dev/null @@ -1,360 +0,0 @@ --- phpMyAdmin SQL Dump --- version 4.5.1 --- http://www.phpmyadmin.net --- --- Servidor: 127.0.0.1 --- Tiempo de generación: 06-03-2017 a las 16:05:03 --- Versión del servidor: 10.1.16-MariaDB --- Versión de PHP: 7.0.9 - -SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; -SET time_zone = "+00:00"; - - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; - --- --- Base de datos: `db672472483` --- - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `accountmanager` --- - -CREATE TABLE `accountmanager` ( - `id` int(11) NOT NULL, - `nombre` varchar(30) NOT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- --- Volcado de datos para la tabla `accountmanager` --- - -INSERT INTO `accountmanager` (`id`, `nombre`) VALUES -(1, 'Julia Carraro'), -(2, 'Kamila Krajnik'), -(3, 'Camilla Rolando'), -(4, 'Amparo Romero'); - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `advertisers` --- - -CREATE TABLE `advertisers` ( - `id` int(2) NOT NULL, - `nombre` varchar(30) NOT NULL, - `id_am` varchar(30) NOT NULL DEFAULT '3' -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- --- Volcado de datos para la tabla `advertisers` --- - -INSERT INTO `advertisers` (`id`, `nombre`, `id_am`) VALUES -(1, 'Neomobile', 'Julia Carraro'), -(2, 'Admitad', 'Kamila Krajnik'), -(3, 'Applift', 'Camilla Rolando'), -(4, 'Digital Galaxy', 'Julia Carraro'), -(5, 'Naranya CPA', 'Julia Carraro'), -(6, 'Wadogo', 'Amparo Romero'), -(7, 'Mobquid LTD', 'Camilla Rolando'), -(8, 'Click2commission CPI', 'Camilla Rolando'), -(9, 'Instal', 'Amparo Romero'), -(10, 'Avazu CPA', 'Julia Carraro'), -(11, 'Digital Virgo', 'Julia Carraro'), -(12, 'Go Wide', 'Camilla Rolando'), -(13, 'Ximobi', 'Julia Carraro'), -(14, '3DM', 'Julia Carraro'), -(15, 'Surikate', 'Julia Carraro'), -(16, 'MobVista CPI', 'Amparo Romero'), -(17, 'Adstract', 'Kamila Krajnik'), -(18, 'Mobipuff', 'Julia Carraro'), -(19, 'Appcoach', 'Amparo Romero'), -(20, 'Image Perfect', 'Julia Carraro'), -(21, 'InlabDigital', 'Camilla Rolando'), -(22, 'Appalgo', 'Kamila Krajnik'), -(23, 'Moblin', 'Camilla Rolando'), -(24, 'AddValue Media CPI', 'Kamila Krajnik'), -(25, 'Layroute', 'Amparo Romero'), -(26, 'AdsFast', 'Camilla Rolando'), -(27, 'Headway CPA', 'Julia Carraro'), -(28, 'MiniMob', 'Amparo Romero'), -(29, 'Naranya CPI', 'Camilla Rolando'), -(30, 'SekoMedia', 'Amparo Romero'), -(31, 'Rekket CPA', 'Julia Carraro'), -(32, 'MappStreet', 'Kamila Krajnik'), -(33, 'Sabia Media', 'Kamila Krajnik'), -(34, 'Stroer', 'Camilla Rolando'), -(35, 'Blackfox', 'Amparo Romero'), -(36, 'SmartLead', 'Amparo Romero'), -(37, 'ClicksMob', 'Kamila Krajnik'), -(38, 'InlabDigital CPA', 'Camilla Rolando'), -(39, 'Eptonic', 'Amparo Romero'), -(40, 'Avazu', 'Amparo Romero'), -(41, 'Digital Turbine', 'Amparo Romero'), -(42, 'Marvel Media', 'Julia Carraro'), -(43, 'Collectcent', 'Julia Carraro'), -(44, 'OnMobile CPA', 'Julia Carraro'), -(45, 'Billy Mobile CPA', 'Julia Carraro'), -(46, 'Mobisummer', 'Camilla Rolando'), -(47, 'Traffikey', 'Camilla Rolando'), -(48, 'KIMIA CPA', 'Julia Carraro'), -(49, 'Adooya Crossrider CPI', 'Camilla Rolando'), -(50, 'Art of Click', 'Amparo Romero'), -(51, 'Matomy Media Group', 'Camilla Rolando'), -(52, 'BravAds', 'Camilla Rolando'), -(53, 'Affle', 'Camilla Rolando'), -(54, 'Rekket', 'Kamila Krajnik'), -(55, 'Mobligo', 'Amparo Romero'), -(56, 'Spyke Media', 'Kamila Krajnik'), -(57, 'Adxperience', 'Kamila Krajnik'), -(58, 'Stingrad', 'Kamila Krajnik'), -(59, 'Grand Oro LP', 'Amparo Romero'), -(60, 'Click2Commission CPA', 'Julia Carraro'), -(61, 'WinClap', 'Amparo Romero'), -(62, 'Televida', 'Julia Carraro'), -(63, 'Crazy4Media CPA', 'Julia Carraro'), -(64, 'Linkadia', 'Kamila Krajnik'), -(65, 'Cliq Digital', 'Julia Carraro'), -(66, 'Headway Digital CPA', 'Julia Carraro'), -(67, 'Mobco Cake', 'Camilla Rolando'), -(68, 'Offerseven', 'Kamila Krajnik'), -(69, 'VIP Response CPA', 'Julia Carraro'), -(70, 'Telecoming', 'Julia Carraro'), -(71, 'Spiroox CPA', 'Julia Carraro'), -(72, 'UC Union', 'Amparo Romero'), -(73, 'Epom Market', 'Amparo Romero'), -(74, 'Rocket10', 'Kamila Krajnik'), -(75, 'Mobrider', 'Amparo Romero'), -(76, 'Aragon Advertising', 'Julia Carraro'), -(77, 'Adxmi', 'Amparo Romero'), -(78, 'Freenet Company', 'Julia Carraro'), -(79, 'Mobidea', 'Julia Carraro'), -(80, 'Gameloft', 'Julia Carraro'), -(81, 'Imagine ADS', 'Julia Carraro'), -(82, 'PSMOVILES', 'Julia Carraro'), -(83, 'Buongiorno (Taplinks)', 'Julia Carraro'), -(84, 'Msales', 'Julia Carraro'), -(85, 'Kimia', 'Julia Carraro'), -(128, 'Ad3traffic', 'Camilla Rolando'), -(129, 'Adacts', 'Julia Carraro'), -(130, 'Addictive Ads', 'Julia Carraro'), -(131, 'Adooya Crossrider CPA', 'Julia Carraro'), -(132, 'Adstacks', 'Julia Carraro'), -(133, 'Adwool', 'Julia Carraro'), -(134, 'Affimobiz', 'Julia Carraro'), -(135, 'Affle CPA', 'Julia Carraro'), -(136, 'AlliTapp', 'Julia Carraro'), -(137, 'App Promoters', 'Julia Carraro'), -(138, 'Appave', 'Julia Carraro'), -(139, 'Basebone', 'Julia Carraro'), -(140, 'CliqDigital', 'Julia Carraro'), -(141, 'CPITraffic', 'Julia Carraro'), -(142, 'Crowmobi', 'Julia Carraro'), -(143, 'DCyphermedia', 'Julia Carraro'), -(144, 'DobleVia Latam', 'Julia Carraro'), -(145, 'Doozymob', 'Julia Carraro'), -(146, 'Glize', 'Julia Carraro'), -(147, 'Gold360', 'Julia Carraro'), -(148, 'Go-Rilla', 'Julia Carraro'), -(149, 'IDB Group', 'Julia Carraro'), -(150, 'JungleTap', 'Julia Carraro'), -(151, 'Kwanko (Swelen)', 'Julia Carraro'), -(152, 'Laddez', 'Julia Carraro'), -(153, 'Leadzinmarketing AFFTRACK', 'Julia Carraro'), -(154, 'Linkadia CPA', 'Julia Carraro'), -(155, 'Maverick CPA', 'Julia Carraro'), -(156, 'Miri Media', 'Julia Carraro'), -(157, 'MiskyAds', 'Julia Carraro'), -(158, 'MobChain', 'Julia Carraro'), -(159, 'Mobilogia', 'Julia Carraro'), -(160, 'Mobipium', 'Julia Carraro'), -(161, 'Mobobeat CPI', 'Julia Carraro'), -(162, 'Mobquid', 'Julia Carraro'), -(163, 'Mobquid CPA', 'Julia Carraro'), -(164, 'MoBrain (Headway)', 'Julia Carraro'), -(165, 'Mobsai', 'Julia Carraro'), -(166, 'Nanalab', 'Julia Carraro'), -(167, 'Persona.ly', 'Julia Carraro'), -(168, 'ReGaming', 'Julia Carraro'), -(169, 'Revenuemob', 'Julia Carraro'), -(170, 'Seko Media', 'Julia Carraro'), -(171, 'Softerik', 'Julia Carraro'), -(172, 'StartApp', 'Julia Carraro'), -(173, 'Superads', 'Julia Carraro'), -(174, 'Tracsion', 'Julia Carraro'), -(175, 'Trooperads', 'Julia Carraro'), -(176, 'Zenna', 'Julia Carraro'); - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `incomes` --- - -CREATE TABLE `incomes` ( - `id` int(4) UNSIGNED ZEROFILL NOT NULL, - `advertiser` varchar(50) DEFAULT '0', - `am` varchar(50) DEFAULT '0', - `clicks` int(12) DEFAULT '0', - `conversion` int(12) DEFAULT '0', - `revenue` decimal(10,2) DEFAULT '0.00', - `platform_revenue` decimal(10,2) DEFAULT '0.00', - `difference` decimal(10,2) DEFAULT '0.00', - `scrubs` decimal(10,2) DEFAULT '0.00', - `hold` decimal(10,2) DEFAULT '0.00', - `validated` decimal(10,2) DEFAULT '0.00', - `status` varchar(50) DEFAULT '0', - `notas_am` varchar(200) DEFAULT 'Notes...', - `notas_finance` varchar(200) DEFAULT 'Notes...', - `date` date DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `status` --- - -CREATE TABLE `status` ( - `id` int(5) NOT NULL, - `name` varchar(50) DEFAULT NULL, - `code` varchar(25) DEFAULT NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- --- Volcado de datos para la tabla `status` --- - -INSERT INTO `status` (`id`, `name`, `code`) VALUES -(1, 'facturas emitidas', 'verde'), -(2, 'pendiente validación', 'amarillo'), -(3, 'faltan datos fiscales', 'naranja'), -(4, 'facturado desde brasil', 'rojo'), -(5, 'emitido sin validar', 'morado'); - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `users` --- - -CREATE TABLE `users` ( - `id` int(11) NOT NULL, - `username` varchar(25) NOT NULL, - `email` tinytext NOT NULL, - `password` varchar(255) NOT NULL, - `name` varchar(30) NOT NULL, - `created` datetime NOT NULL, - `attempt` varchar(15) NOT NULL DEFAULT '0' -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `user_devices` --- - -CREATE TABLE `user_devices` ( - `id` int(11) NOT NULL, - `uid` int(11) NOT NULL COMMENT 'The user''s ID', - `token` varchar(15) NOT NULL COMMENT 'A unique token for the user''s device', - `last_access` varchar(20) NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - --- -------------------------------------------------------- - --- --- Estructura de tabla para la tabla `user_tokens` --- - -CREATE TABLE `user_tokens` ( - `token` varchar(40) NOT NULL COMMENT 'The generated unique token', - `uid` int(11) NOT NULL COMMENT 'The User ID', - `requested` varchar(20) NOT NULL COMMENT 'The date when token was created' -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - --- --- Índices para tablas volcadas --- - --- --- Indices de la tabla `accountmanager` --- -ALTER TABLE `accountmanager` - ADD PRIMARY KEY (`id`); - --- --- Indices de la tabla `advertisers` --- -ALTER TABLE `advertisers` - ADD PRIMARY KEY (`id`); - --- --- Indices de la tabla `incomes` --- -ALTER TABLE `incomes` - ADD PRIMARY KEY (`id`); - --- --- Indices de la tabla `status` --- -ALTER TABLE `status` - ADD PRIMARY KEY (`id`); - --- --- Indices de la tabla `users` --- -ALTER TABLE `users` - ADD PRIMARY KEY (`id`); - --- --- Indices de la tabla `user_devices` --- -ALTER TABLE `user_devices` - ADD PRIMARY KEY (`id`); - --- --- AUTO_INCREMENT de las tablas volcadas --- - --- --- AUTO_INCREMENT de la tabla `accountmanager` --- -ALTER TABLE `accountmanager` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5; --- --- AUTO_INCREMENT de la tabla `advertisers` --- -ALTER TABLE `advertisers` - MODIFY `id` int(2) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=177; --- --- AUTO_INCREMENT de la tabla `incomes` --- -ALTER TABLE `incomes` - MODIFY `id` int(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT; --- --- AUTO_INCREMENT de la tabla `status` --- -ALTER TABLE `status` - MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; --- --- AUTO_INCREMENT de la tabla `users` --- -ALTER TABLE `users` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; --- --- AUTO_INCREMENT de la tabla `user_devices` --- -ALTER TABLE `user_devices` - MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/mysql/mysql-users.sql b/mysql/mysql-users.sql deleted file mode 100644 index 4df4e59..0000000 --- a/mysql/mysql-users.sql +++ /dev/null @@ -1,39 +0,0 @@ -SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; -SET time_zone = "+00:00"; - --- --- Table structure for table `users` --- - -CREATE TABLE IF NOT EXISTS `users` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `username` varchar(10) NOT NULL, - `email` tinytext NOT NULL, - `password` varchar(255) NOT NULL, - `name` varchar(30) NOT NULL, - `created` datetime NOT NULL, - `attempt` varchar(15) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1; - --- --- Table structure for table `user_tokens` --- - -CREATE TABLE IF NOT EXISTS `user_tokens` ( - `token` varchar(40) NOT NULL COMMENT 'The generated unique token', - `uid` int(11) NOT NULL COMMENT 'The User ID', - `requested` varchar(20) NOT NULL COMMENT 'The date when token was created' -) ENGINE=MyISAM DEFAULT CHARSET=latin1; - --- --- Table structure for table `user_devices` --- - -CREATE TABLE IF NOT EXISTS `user_devices` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `uid` int(11) NOT NULL COMMENT 'The user''s ID', - `token` varchar(15) NOT NULL COMMENT 'A unique token for the user''s device', - `last_access` varchar(20) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..ce4e94a --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,7 @@ +/// +/// +import "./.next/types/routes.d.ts"; +import "./.next/types/root-params.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..bd31e98 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,24 @@ +import type { NextConfig } from "next"; + +const SECURITY_HEADERS = [ + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, + { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" }, + { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" }, +]; + +const nextConfig: NextConfig = { + async headers() { + return [{ source: "/(.*)", headers: SECURITY_HEADERS }]; + }, + /* Standalone output is what the Dockerfile copies; it is harmless on Vercel. */ + output: "standalone", + /* Runtime data (the local database, uploads) must never ride along in the + traced server output. */ + outputFileTracingExcludes: { "*": ["./data/**"] }, + /* Next writes AGENTS.md / CLAUDE.md on dev start; the repo keeps its own. */ + agentRules: false, +}; + +export default nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..bf8f461 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "vitrina", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev --port 3000", + "build": "next build", + "start": "node scripts/start-standalone.mjs", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:e2e": "playwright test", + "db:generate": "drizzle-kit generate", + "db:migrate": "node scripts/migrate.mjs", + "db:studio": "drizzle-kit studio", + "credits": "node scripts/credits.mjs", + "mock:platform": "node scripts/mock-platform.mjs", + "brand": "node scripts/build-brand-assets.mjs", + "wipe:local": "node scripts/wipe-local.mjs" + }, + "dependencies": { + "@libsql/client": "0.18.0", + "@tanstack/react-virtual": "^3.14.10", + "@vercel/blob": "^2.8.0", + "better-auth": "1.7.5", + "drizzle-orm": "0.45.2", + "fflate": "0.8.3", + "next": "^16.0.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "stripe": "22.6.2", + "zod": "4.6.5", + "zustand": "^5.0.15" + }, + "devDependencies": { + "@playwright/test": "1.63.0", + "@types/node": "^22.10.2", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "drizzle-kit": "0.31.10", + "eslint": "9.39.5", + "eslint-config-next": "16.3.5", + "tsx": "^4.23.13", + "typescript": "^5.9.2", + "vitest": "5.0.1" + } +} diff --git a/php/Bootstrap.php b/php/Bootstrap.php deleted file mode 100644 index bc020b7..0000000 --- a/php/Bootstrap.php +++ /dev/null @@ -1,62 +0,0 @@ - "Editor PHP libraries required PHP 5.3 or newer. You are ". - "currently using ".PHP_VERSION.". PHP 5.3 and newer have a lot of ". - "great new features that the Editor libraries take advantage of to ". - "present an easy to use and flexible API." - ) ); - exit(0); -} - - -// -// Load the DataTables bootstrap core file and let it register the required -// handlers. -// -include( dirname(__FILE__).'/Bootstrap.php' ); - diff --git a/php/Database/Database.php b/php/Database/Database.php deleted file mode 100644 index bd05032..0000000 --- a/php/Database/Database.php +++ /dev/null @@ -1,432 +0,0 @@ - - * array( - * "user" => "", // User name - * "pass" => "", // Password - * "host" => "", // Host name - * "port" => "", // Port - * "db" => "", // Database name - * "type" => "" // Datable type: "Mysql", "Postgres" or "Sqlite" - * ) - *
- */ - function __construct( $opts ) - { - $types = array( 'Mysql', 'Oracle', 'Postgres', 'Sqlite', 'Sqlserver' ); - - if ( ! in_array( $opts['type'], $types ) ) { - throw new \Exception( - "Unknown database driver type. Must be one of ".implode(', ', $types), - 1 - ); - } - - $this->query_driver = "DataTables\\Database\\Driver".$opts['type'].'Query'; - $this->_dbResource = isset( $opts['pdo'] ) ? - $opts['pdo'] : - call_user_func($this->query_driver.'::connect', $opts ); - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - /** @var resource */ - private $_dbResource = null; - - /** @var boolean */ - private $_debug = false; - - /** @var array */ - private $_debugInfo = array(); - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - /** - * Determine if there is any data in the table that matches the query - * condition - * - * @param string|string[] $table Table name(s) to act upon. - * @param array $where Where condition for what to select - see {@link - * Query::where}. - * @return [boolean] Boolean flag - true if there were rows - */ - public function any( $table, $where=null ) - { - $res = $this->query( 'select' ) - ->table( $table ) - ->get( '*' ) - ->where( $where ) - ->exec(); - - return $res->count() > 0; - } - - - /** - * Commit a database transaction. - * - * Use with {@link transaction} and {@link rollback}. - * @return self - */ - public function commit () - { - call_user_func($this->query_driver.'::commit', $this->_dbResource ); - return $this; - } - - - /** - * Get / set debug mode. - * - * @param boolean $_ Debug mode state. If not given, then used as a getter. - * @return boolean|self Debug mode state if no parameter is given, or - * self if used as a setter. - */ - public function debug ( $set=null ) - { - if ( $set === null ) { - return $this->_debug; - } - - $this->_debug = $set; - - return $this; - } - - - /** - * Get debug query information. Please note that the setter version of this - * method is for _internal use only_. - * - * @return array Information about the queries used. When this method is - * called it will reset the query cache. - */ - public function debugInfo ( $query=null, $bindings=null ) - { - if ( $query === null ) { - // Clear - $debugInfo = $this->_debugInfo; - $this->_debugInfo = array(); - - return $debugInfo; - } - - if ( $this->_debug ) { - $this->_debugInfo[] = array( - "query" => $query, - "bindings" => $bindings - ); - } - - return $this; - } - - - /** - * Perform a delete query on a table. - * - * This is a short cut method that creates an update query and then uses - * the query('delete'), table, where and exec methods of the query. - * @param string|string[] $table Table name(s) to act upon. - * @param array $where Where condition for what to delete - see {@link - * Query::where}. - * @return Result - */ - public function delete ( $table, $where=null ) - { - return $this->query( 'delete' ) - ->table( $table ) - ->where( $where ) - ->exec(); - } - - - /** - * Insert data into a table. - * - * This is a short cut method that creates an update query and then uses - * the query('insert'), table, set and exec methods of the query. - * @param string|string[] $table Table name(s) to act upon. - * @param array $set Field names and values to set - see {@link - * Query::set}. - * @param array $pkey Primary key column names (this is an array for - * forwards compt, although only the first item in the array is actually - * used). This doesn't need to be set, but it must be if you want to use - * the `Result->insertId()` method. - * @return Result - */ - public function insert ( $table, $set, $pkey='' ) - { - return $this->query( 'insert' ) - ->pkey( $pkey ) - ->table( $table ) - ->set( $set ) - ->exec(); - } - - - /** - * Update or Insert data. When doing an insert, the where condition is - * added as a set field - * @param string|string[] $table Table name(s) to act upon. - * @param array $set Field names and values to set - see {@link - * Query::set}. - * @param array $where Where condition for what to update - see {@link - * Query::where}. - * @param array $pkey Primary key column names (this is an array for - * forwards compt, although only the first item in the array is actually - * used). This doesn't need to be set, but it must be if you want to use - * the `Result->insertId()` method. Only used if an insert is performed. - * @return Result - */ - public function push ( $table, $set, $where=null, $pkey='' ) - { - // Update or insert - if ( $this->select( $table, "*", $where )->count() > 0 ) { - return $this->update( $table, $set, $where ); - } - - // Add the where condition to the values to set - foreach ($where as $key => $value) { - if ( ! isset( $set[ $key ] ) ) { - $set[ $key ] = $value; - } - } - - return $this->insert( $table, $set, $pkey ); - } - - - /** - * Create a query object to build a database query. - * @param string $type Query type - select, insert, update or delete. - * @param string|string[] $table Table name(s) to act upon. - * @return Query - */ - public function query ( $type, $table=null ) - { - return new $this->query_driver( $this, $type, $table ); - } - - - /** - * Quote a string for a quote. Note you should generally use a bind! - * @param string $val Value to quote - * @param string $type Value type - * @return string - */ - public function quote ( $val, $type=\PDO::PARAM_STR ) - { - return $this->_dbResource->quote( $val, $type ); - } - - - /** - * Create a `Query` object that will execute a custom SQL query. This is - * similar to the `sql` method, but in this case you must call the `exec()` - * method of the returned `Query` object manually. This can be useful if you - * wish to bind parameters using the query `bind` method to ensure data is - * properly escaped. - * - * @return Result - * - * @example - * Safely escape user input - * - * $db - * ->raw() - * ->bind( ':date', $_POST['date'] ) - * ->exec( 'SELECT * FROM staff where date < :date' ); - * - */ - public function raw () - { - return $this->query( 'raw' ); - } - - - /** - * Get the database resource connector. This is typically a PDO object. - * @return resource PDO connection resource (driver dependent) - */ - public function resource () - { - return $this->_dbResource; - } - - - /** - * Rollback the database state to the start of the transaction. - * - * Use with {@link transaction} and {@link commit}. - * @return self - */ - public function rollback () - { - call_user_func($this->query_driver.'::rollback', $this->_dbResource ); - return $this; - } - - - /** - * Select data from a table. - * - * This is a short cut method that creates an update query and then uses - * the query('select'), table, get, where and exec methods of the query. - * @param string|string[] $table Table name(s) to act upon. - * @param array $field Fields to get from the table(s) - see {@link - * Query::get}. - * @param array $where Where condition for what to select - see {@link - * Query::where}. - * @param array $orderBy Order condition - see {@link - * Query::order}. - * @return Result - */ - public function select ( $table, $field="*", $where=null, $orderBy=null ) - { - return $this->query( 'select' ) - ->table( $table ) - ->get( $field ) - ->where( $where ) - ->order( $orderBy ) - ->exec(); - } - - - /** - * Select distinct data from a table. - * - * This is a short cut method that creates an update query and then uses the - * query('select'), distinct ,table, get, where and exec methods of the - * query. - * @param string|string[] $table Table name(s) to act upon. - * @param array $field Fields to get from the table(s) - see {@link - * Query::get}. - * @param array $where Where condition for what to select - see {@link - * Query::where}. - * @param array $orderBy Order condition - see {@link - * Query::order}. - * @return Result - */ - public function selectDistinct ( $table, $field="*", $where=null, $orderBy=null ) - { - return $this->query( 'select' ) - ->table( $table ) - ->distinct( true ) - ->get( $field ) - ->where( $where ) - ->order( $orderBy ) - ->exec(); - } - - - /** - * Execute an raw SQL query - i.e. give the method your own SQL, rather - * than having the Database classes building it for you. - * - * This method will execute the given SQL immediately. Use the `raw()` - * method if you need the ability to add bound parameters. - * @param string $sql SQL string to execute (only if _type is 'raw'). - * @return Result - * - * @example - * Basic select - * - * $result = $db->sql( 'SELECT * FROM myTable;' ); - * - * - * @example - * Set the character set of the connection - * - * $db->sql("SET character_set_client=utf8"); - * $db->sql("SET character_set_connection=utf8"); - * $db->sql("SET character_set_results=utf8"); - * - */ - public function sql ( $sql ) - { - return $this->query( 'raw' ) - ->exec( $sql ); - } - - - /** - * Start a new database transaction. - * - * Use with {@link commit} and {@link rollback}. - * @return self - */ - public function transaction () - { - call_user_func($this->query_driver.'::transaction', $this->_dbResource ); - return $this; - } - - - /** - * Update data. - * - * This is a short cut method that creates an update query and then uses - * the query('update'), table, set, where and exec methods of the query. - * @param string|string[] $table Table name(s) to act upon. - * @param array $set Field names and values to set - see {@link - * Query::set}. - * @param array $where Where condition for what to update - see {@link - * Query::where}. - * @return Result - */ - public function update ( $table, $set=null, $where=null ) - { - return $this->query( 'update' ) - ->table( $table ) - ->set( $set ) - ->where( $where ) - ->exec(); - } -}; - diff --git a/php/Database/Driver/Mysql/Query.php b/php/Database/Driver/Mysql/Query.php deleted file mode 100644 index caa35b7..0000000 --- a/php/Database/Driver/Mysql/Query.php +++ /dev/null @@ -1,116 +0,0 @@ - PDO::ERRMODE_EXCEPTION - ) - ); - } catch (\PDOException $e) { - // If we can't establish a DB connection then we return a DataTables - // error. - echo json_encode( array( - "sError" => "An error occurred while connecting to the database ". - "'{$db}'. The error reported by the server was: ".$e->getMessage() - ) ); - exit(0); - } - - return $pdo; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Protected methods - */ - - protected function _prepare( $sql ) - { - $resource = $this->database()->resource(); - $this->_stmt = $resource->prepare( $sql ); - - // bind values - for ( $i=0 ; $i_bindings) ; $i++ ) { - $binding = $this->_bindings[$i]; - - $this->_stmt->bindValue( - $binding['name'], - $binding['value'], - $binding['type'] ? $binding['type'] : \PDO::PARAM_STR - ); - } - - $this->database()->debugInfo( $sql, $this->_bindings ); - } - - - protected function _exec() - { - try { - $this->_stmt->execute(); - } - catch (PDOException $e) { - echo "An SQL error occurred: ".$e->getMessage(); - error_log( "An SQL error occurred: ".$e->getMessage() ); - return false; - } - - $resource = $this->database()->resource(); - return new DriverMysqlResult( $resource, $this->_stmt ); - } -} - diff --git a/php/Database/Driver/Mysql/Result.php b/php/Database/Driver/Mysql/Result.php deleted file mode 100644 index e5ba6b0..0000000 --- a/php/Database/Driver/Mysql/Result.php +++ /dev/null @@ -1,73 +0,0 @@ -_dbh = $dbh; - $this->_stmt = $stmt; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - private $_stmt; - private $_dbh; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - public function count () - { - return count($this->fetchAll()); - } - - - public function fetch () - { - return $this->_stmt->fetch( \PDO::FETCH_ASSOC ); - } - - - public function fetchAll () - { - return $this->_stmt->fetchAll( \PDO::FETCH_ASSOC ); - } - - - public function insertId () - { - return $this->_dbh->lastInsertId(); - } -} - diff --git a/php/Database/Driver/Mysql/_notes/dwsync.xml b/php/Database/Driver/Mysql/_notes/dwsync.xml deleted file mode 100644 index 44d8680..0000000 --- a/php/Database/Driver/Mysql/_notes/dwsync.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/php/Database/Driver/Oracle/Query.php b/php/Database/Driver/Oracle/Query.php deleted file mode 100644 index 2d7823d..0000000 --- a/php/Database/Driver/Oracle/Query.php +++ /dev/null @@ -1,183 +0,0 @@ - "oci methods are not available in this PHP install to connect to Oracle" - ) ); - exit(0); - } - - $conn = @oci_connect($user, $pass, $host.$port.'/'.$db); - - if ( ! $conn ) { - // If we can't establish a DB connection then we return a DataTables - // error. - $e = oci_error(); - - echo json_encode( array( - "error" => "An error occurred while connecting to the database ". - "'{$db}'. The error reported by the server was: ".$e['message'] - ) ); - exit(0); - } - - // Use ISO date and time styles - $stmt = oci_parse($conn, "ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'" ); - $res = oci_execute( $stmt ); - - $stmt = oci_parse($conn, "ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" ); - $res = oci_execute( $stmt ); - - return $conn; - } - - - public static function transaction ( $conn ) - { - // no op - } - - public static function commit ( $conn ) - { - oci_commit( $conn ); - } - - public static function rollback ( $conn ) - { - oci_rollback( $conn ); - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Protected methods - */ - - protected function _prepare( $sql ) - { - $resource = $this->database()->resource(); - $pkey = $this->pkey(); - - // If insert, add the pkey column - if ( $this->_type === 'insert' && $pkey ) { - $sql .= ' RETURNING '.$pkey[0].' INTO :editor_pkey_value'; - } - - $this->_stmt = oci_parse( $resource, $sql ); - - // If insert, add a binding for the returned id - if ( $this->_type === 'insert' && $pkey ) { - oci_bind_by_name( - $this->_stmt, - ':editor_pkey_value', - $this->_editor_pkey_value, - 36 - ); - } - - // Bind values - for ( $i=0 ; $i_bindings) ; $i++ ) { - $binding = $this->_bindings[$i]; - - oci_bind_by_name( - $this->_stmt, - $binding['name'], - $binding['value'] - ); - } - - $this->database()->debugInfo( $sql, $this->_bindings ); - } - - - protected function _exec() - { - $res = @oci_execute( $this->_stmt, OCI_NO_AUTO_COMMIT ); - - if ( ! $res ) { - $e = oci_error( $this->_stmt ); - // $e['message'] - - return false; - } - - $resource = $this->database()->resource(); - return new DriverOracleResult( $resource, $this->_stmt, $this->_editor_pkey_value ); - } - - - protected function _build_table() - { - $out = array(); - - for ( $i=0, $ien=count($this->_table) ; $i<$ien ; $i++ ) { - $t = $this->_table[$i]; - - if ( strpos($t, ' as ') ) { - $a = explode( ' as ', $t ); - $out[] = $a[0].' '.$a[1]; - } - else { - $out[] = $t; - } - } - - return ' '.implode(', ', $out).' '; - } -} diff --git a/php/Database/Driver/Oracle/Result.php b/php/Database/Driver/Oracle/Result.php deleted file mode 100644 index f818155..0000000 --- a/php/Database/Driver/Oracle/Result.php +++ /dev/null @@ -1,82 +0,0 @@ -_dbh = $dbh; - $this->_stmt = $stmt; - $this->_pkey_val = $pkey_val; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - private $_stmt; // Result from oci_parse - private $_dbh; // Result from oci_connect - private $_rows = null; - private $_pkey_val; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - public function count () - { - return count($this->fetchAll()); - } - - - public function fetch () - { - return oci_fetch_assoc( $this->_stmt ); - } - - - public function fetchAll () - { - if ( ! $this->_rows ) { - $out = array(); - - oci_fetch_all( $this->_stmt, $out, 0, -1, OCI_FETCHSTATEMENT_BY_ROW + OCI_ASSOC ); - - $this->_rows = $out; - } - - return $this->_rows; - } - - - public function insertId () - { - return $this->_pkey_val; - } -} - diff --git a/php/Database/Driver/Oracle/_notes/dwsync.xml b/php/Database/Driver/Oracle/_notes/dwsync.xml deleted file mode 100644 index 44d8680..0000000 --- a/php/Database/Driver/Oracle/_notes/dwsync.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/php/Database/Driver/Postgres/Query.php b/php/Database/Driver/Postgres/Query.php deleted file mode 100644 index 198c11b..0000000 --- a/php/Database/Driver/Postgres/Query.php +++ /dev/null @@ -1,143 +0,0 @@ - PDO::ERRMODE_EXCEPTION - ) - ); - } catch (\PDOException $e) { - // If we can't establish a DB connection then we return a DataTables - // error. - echo json_encode( array( - "sError" => "An error occurred while connecting to the database ". - "'{$db}'. The error reported by the server was: ".$e->getMessage() - ) ); - exit(0); - } - - return $pdo; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Protected methods - */ - - protected function _prepare( $sql ) - { - $resource = $this->database()->resource(); - - // Add a RETURNING command to postgres insert queries so we can get the - // pkey value from the query reliably - if ( $this->_type === 'insert' ) { - $table = explode( ' as ', $this->_table[0] ); - - // Get the pkey field name - $pkRes = $resource->prepare( - "SELECT - pg_attribute.attname, - format_type(pg_attribute.atttypid, pg_attribute.atttypmod) - FROM pg_index, pg_class, pg_attribute - WHERE - pg_class.oid = '{$table[0]}'::regclass AND - indrelid = pg_class.oid AND - pg_attribute.attrelid = pg_class.oid AND - pg_attribute.attnum = any(pg_index.indkey) - AND indisprimary" - ); - $pkRes->execute(); - $row = $pkRes->fetch(); - - $sql .= ' RETURNING '.$row['attname'].' as dt_pkey'; - } - - $this->_stmt = $resource->prepare( $sql ); - - // bind values - for ( $i=0 ; $i_bindings) ; $i++ ) { - $binding = $this->_bindings[$i]; - - $this->_stmt->bindValue( - $binding['name'], - $binding['value'], - $binding['type'] ? $binding['type'] : \PDO::PARAM_STR - ); - } - - $this->database()->debugInfo( $sql, $this->_bindings ); - } - - - protected function _exec() - { - try { - $this->_stmt->execute(); - } - catch (PDOException $e) { - echo "An SQL error occurred: ".$e->getMessage(); - error_log( "An SQL error occurred: ".$e->getMessage() ); - return false; - } - - $resource = $this->database()->resource(); - return new DriverPostgresResult( $resource, $this->_stmt ); - } -} - diff --git a/php/Database/Driver/Postgres/Result.php b/php/Database/Driver/Postgres/Result.php deleted file mode 100644 index 64ef063..0000000 --- a/php/Database/Driver/Postgres/Result.php +++ /dev/null @@ -1,75 +0,0 @@ -_dbh = $dbh; - $this->_stmt = $stmt; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - private $_stmt; - private $_dbh; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - public function count () - { - return count($this->fetchAll()); - } - - - public function fetch () - { - return $this->_stmt->fetch( \PDO::FETCH_ASSOC ); - } - - - public function fetchAll () - { - return $this->_stmt->fetchAll( \PDO::FETCH_ASSOC ); - } - - - public function insertId () - { - // Only useful after an insert of course... - $rows = $this->_stmt->fetchAll(); - return $rows[0]['dt_pkey']; - } -} - diff --git a/php/Database/Driver/Postgres/_notes/dwsync.xml b/php/Database/Driver/Postgres/_notes/dwsync.xml deleted file mode 100644 index 44d8680..0000000 --- a/php/Database/Driver/Postgres/_notes/dwsync.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/php/Database/Driver/Sqlite/Query.php b/php/Database/Driver/Sqlite/Query.php deleted file mode 100644 index 8ab61ff..0000000 --- a/php/Database/Driver/Sqlite/Query.php +++ /dev/null @@ -1,111 +0,0 @@ - PDO::ERRMODE_EXCEPTION - ) - ); - } catch (\PDOException $e) { - // If we can't establish a DB connection then we return a DataTables - // error. - echo json_encode( array( - "sError" => "An error occurred while connecting to the database ". - "'{$db}'. The error reported by the server was: ".$e->getMessage() - ) ); - exit(0); - } - - return $pdo; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Protected methods - */ - - protected function _prepare( $sql ) - { - $resource = $this->database()->resource(); - $this->_stmt = $resource->prepare( $sql ); - - // bind values - for ( $i=0 ; $i_bindings) ; $i++ ) { - $binding = $this->_bindings[$i]; - - $this->_stmt->bindValue( - $binding['name'], - $binding['value'], - $binding['type'] ? $binding['type'] : \PDO::PARAM_STR - ); - } - - $this->database()->debugInfo( $sql, $this->_bindings ); - } - - - protected function _exec() - { - try { - $this->_stmt->execute(); - } - catch (PDOException $e) { - echo "An SQL error occurred: ".$e->getMessage(); - error_log( "An SQL error occurred: ".$e->getMessage() ); - return false; - } - - $resource = $this->database()->resource(); - return new DriverSqliteResult( $resource, $this->_stmt ); - } -} - diff --git a/php/Database/Driver/Sqlite/Result.php b/php/Database/Driver/Sqlite/Result.php deleted file mode 100644 index e2ac26c..0000000 --- a/php/Database/Driver/Sqlite/Result.php +++ /dev/null @@ -1,73 +0,0 @@ -_dbh = $dbh; - $this->_stmt = $stmt; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - private $_stmt; - private $_dbh; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - public function count () - { - return count($this->fetchAll()); - } - - - public function fetch () - { - return $this->_stmt->fetch( \PDO::FETCH_ASSOC ); - } - - - public function fetchAll () - { - return $this->_stmt->fetchAll( \PDO::FETCH_ASSOC ); - } - - - public function insertId () - { - return $this->_dbh->lastInsertId(); - } -} - diff --git a/php/Database/Driver/Sqlite/_notes/dwsync.xml b/php/Database/Driver/Sqlite/_notes/dwsync.xml deleted file mode 100644 index 44d8680..0000000 --- a/php/Database/Driver/Sqlite/_notes/dwsync.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/php/Database/Driver/Sqlserver/Query.php b/php/Database/Driver/Sqlserver/Query.php deleted file mode 100644 index 5dc660b..0000000 --- a/php/Database/Driver/Sqlserver/Query.php +++ /dev/null @@ -1,135 +0,0 @@ - PDO::ERRMODE_EXCEPTION - ) - ); - } catch (\PDOException $e) { - // If we can't establish a DB connection then we return a DataTables - // error. - echo json_encode( array( - "sError" => "An error occurred while connecting to the database ". - "'{$db}'. The error reported by the server was: ".$e->getMessage() - ) ); - exit(0); - } - - return $pdo; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Protected methods - */ - - protected function _prepare( $sql ) - { - $resource = $this->database()->resource(); - $this->_stmt = $resource->prepare( $sql ); - - // bind values - for ( $i=0 ; $i_bindings) ; $i++ ) { - $binding = $this->_bindings[$i]; - - $this->_stmt->bindValue( - $binding['name'], - $binding['value'], - $binding['type'] ? $binding['type'] : \PDO::PARAM_STR - ); - } - - $this->database()->debugInfo( $sql, $this->_bindings ); - } - - - protected function _exec() - { - try { - $this->_stmt->execute(); - } - catch (PDOException $e) { - echo "An SQL error occurred: ".$e->getMessage(); - error_log( "An SQL error occurred: ".$e->getMessage() ); - return false; - } - - $resource = $this->database()->resource(); - return new DriverSqlserverResult( $resource, $this->_stmt ); - } - - - // SQL Server 2012+ only - protected function _build_limit() - { - $out = ''; - - if ( $this->_offset ) { - $out .= ' OFFSET '.$this->_offset.' ROWS'; - } - - if ( $this->_limit ) { - if ( ! $this->_offset ) { - $out .= ' OFFSET 0 ROWS'; - } - $out .= ' FETCH NEXT '.$this->_limit. ' ROWS ONLY'; - } - - return $out; - } -} - diff --git a/php/Database/Driver/Sqlserver/Result.php b/php/Database/Driver/Sqlserver/Result.php deleted file mode 100644 index 781131c..0000000 --- a/php/Database/Driver/Sqlserver/Result.php +++ /dev/null @@ -1,71 +0,0 @@ -_dbh = $dbh; - $this->_stmt = $stmt; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - private $_stmt; - private $_dbh; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - public function count () - { - return count($this->fetchAll()); - } - - - public function fetch () - { - return $this->_stmt->fetch( \PDO::FETCH_ASSOC ); - } - - - public function fetchAll () - { - return $this->_stmt->fetchAll( \PDO::FETCH_ASSOC ); - } - - - public function insertId () - { - return $this->_dbh->lastInsertId(); - } -} - diff --git a/php/Database/Driver/Sqlserver/_notes/dwsync.xml b/php/Database/Driver/Sqlserver/_notes/dwsync.xml deleted file mode 100644 index 44d8680..0000000 --- a/php/Database/Driver/Sqlserver/_notes/dwsync.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/php/Database/Query.php b/php/Database/Query.php deleted file mode 100644 index 2ee4b86..0000000 --- a/php/Database/Query.php +++ /dev/null @@ -1,1076 +0,0 @@ -_dbHost = $dbHost; - $this->_type = $type; - $this->table( $table ); - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - /** - * @var string Driver to use - * @internal - */ - protected $_type = ""; - - /** - * @var array - * @internal - */ - protected $_table = array(); - - /** - * @var array - * @internal - */ - protected $_field = array(); - - /** - * @var array - * @internal - */ - protected $_bindings = array(); - - /** - * @var array - * @internal - */ - protected $_where = array(); - - /** - * @var array - * @internal - */ - protected $_join = array(); - - /** - * @var array - * @internal - */ - protected $_order = array(); - - /** - * @var array - * @internal - */ - protected $_noBind = array(); - - /** - * @var int - * @internal - */ - protected $_limit = null; - - /** - * @var int - * @internal - */ - protected $_offset = null; - - /** - * @var string - * @internal - */ - protected $_distinct = false; - - /** - * @var string - * @internal - */ - protected $_identifier_limiter = array( '`', '`' ); - - /** - * @var string - * @internal - */ - protected $_field_quote = '\''; - - /** - * @var array - * @internal - */ - protected $_pkey = null; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Static methods - */ - - /** - * Commit a transaction. - * @param \PDO $dbh The Database handle (typically a PDO object, but not always). - */ - public static function commit ( $dbh ) - { - $dbh->commit(); - } - - /** - * Database connection - override by the database driver. - * @param string|array $user User name or all parameters in an array - * @param string $pass Password - * @param string $host Host name - * @param string $db Database name - * @return Query - */ - public static function connect ( $user, $pass='', $host='', $port='', $db='', $dsn='' ) - { - return false; - } - - - /** - * Start a database transaction - * @param \PDO $dbh The Database handle (typically a PDO object, but not always). - */ - public static function transaction ( $dbh ) - { - $dbh->beginTransaction(); - } - - - /** - * Rollback the database state to the start of the transaction. - * @param \PDO $dbh The Database handle (typically a PDO object, but not always). - */ - public static function rollback ( $dbh ) - { - $dbh->rollBack(); - } - - - /** - * Common helper for the drivers to handle a PDO DSN postfix - * @param string $dsn DSN postfix to use - * @return Query - * @internal - */ - static function dsnPostfix ( $dsn ) - { - if ( ! $dsn ) { - return ''; - } - - // Add a DSN field separator if not given - if ( strpos( $dsn, ';' ) !== 0 ) { - return ';'.$dsn; - } - - return $dsn; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - /** - * Safely bind an input value to a parameter. This is evaluated when the - * query is executed. This allows user input to be safely executed without - * risk of an SQL injection attack. - * - * @param [type] $name Parameter name. This should include a leading colon - * @param [type] $value Value to bind - * @param [type] $type Data type. See the PHP PDO documentation: - * http://php.net/manual/en/pdo.constants.php - * @return Query - */ - public function bind ( $name, $value, $type=null ) - { - $this->_bindings[] = array( - "name" => $this->_safe_bind( $name ), - "value" => $value, - "type" => $type - ); - - return $this; - } - - - /** - * Get the Database host for this query instance - * @return DataTable Database class instance - */ - public function database () - { - return $this->_dbHost; - } - - - /** - * Set a distinct flag for a `select` query. Note that this has no effect on - * any of the other query types. - * @param boolean $dis Optional - * @return Query - */ - public function distinct ( $dis ) - { - $this->_distinct = $dis; - return $this; - } - - - /** - * Execute the query. - * @param string $sql SQL string to execute (only if _type is 'raw'). - * @return Result - */ - public function exec ( $sql=null ) - { - $type = strtolower( $this->_type ); - - if ( $type === 'select' ) { - return $this->_select(); - } - else if ( $type === 'insert' ) { - return $this->_insert(); - } - else if ( $type === 'update' ) { - return $this->_update(); - } - else if ( $type === 'delete' ) { - return $this->_delete(); - } - else if ( $type === 'raw' ) { - return $this->_raw( $sql ); - } - - throw new \Exception("Unknown database command or not supported: ".$type, 1); - } - - - /** - * Get fields. - * @param string|string[] $get,... Fields to get - can be specified as - * individual fields, an array of fields, a string of comma separated - * fields or any combination of those. - * @return self - */ - public function get ( $get ) - { - if ( $get === null ) { - return $this; - } - - $args = func_get_args(); - - for ( $i=0 ; $iget( $args[$i][$j] ); - } - } - else { - // String argument so split into pieces and add - // TODO - This limits the ability to use functions. Need to - // parse the string so it will split when not in a function - $fields = explode(",", $args[$i]); - - for ( $j=0 ; $j_field[] = trim( $fields[$j] ); - } - } - } - - return $this; - } - - - /** - * Perform a JOIN operation - * @param string $table Table name to do the JOIN on - * @param string $condition JOIN condition - * @param string $type JOIN type - * @return self - */ - public function join ( $table, $condition, $type='' ) - { - // Tidy and check we know what the join type is - if ($type !== '') { - $type = strtoupper(trim($type)); - - if ( ! in_array($type, array('LEFT', 'RIGHT', 'INNER', 'OUTER', 'LEFT OUTER', 'RIGHT OUTER'))) { - $type = ''; - } - } - - // Protect the identifiers - if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $condition, $match)) - { - $match[1] = $this->_protect_identifiers( $match[1] ); - $match[3] = $this->_protect_identifiers( $match[3] ); - - $condition = $match[1].$match[2].$match[3]; - } - - $this->_join[] = $type .' JOIN '. $this->_protect_identifiers($table) .' ON '. $condition .' '; - - return $this; - } - - - /** - * Limit the result set to a certain size. - * @param int $lim The number of records to limit the result to. - * @return self - */ - public function limit ( $lim ) - { - $this->_limit = $lim; - - return $this; - } - - - /** - * Get / set the primary key column name(s) so they can be easily returned - * after an insert. - * @param string[] $pkey Primary keys - * @return Query|string[] - */ - public function pkey ( $pkey=null ) - { - if ( $pkey === null ) { - return $this->_pkey; - } - - $this->_pkey = $pkey; - - return $this; - } - - - /** - * Set table(s) to perform the query on. - * @param string|string[] $table,... Table(s) to use - can be specified as - * individual names, an array of names, a string of comma separated - * names or any combination of those. - * @return self - */ - public function table ( $table ) - { - if ( $table === null ) { - return $this; - } - - if ( is_array($table) ) { - // Array so loop internally - for ( $i=0 ; $itable( $table[$i] ); - } - } - else { - // String based, explode for multiple tables - $tables = explode(",", $table); - - for ( $i=0 ; $i_table[] = $this->_protect_identifiers( trim($tables[$i]) ); - } - } - - return $this; - } - - - /** - * Offset the return set by a given number of records (useful for paging). - * @param int $off The number of records to offset the result by. - * @return self - */ - public function offset ( $off ) - { - $this->_offset = $off; - - return $this; - } - - - /** - * Order by - * @param string|string[] $order Columns and direction to order by - can - * be specified as individual names, an array of names, a string of comma - * separated names or any combination of those. - * @return self - */ - public function order ( $order ) - { - if ( $order === null ) { - return $this; - } - - if ( !is_array($order) ) { - $order = explode(",", $order); - } - - for ( $i=0 ; $i_order[] = $this->_protect_identifiers( $identifier ).' '.$direction; - } - - return $this; - } - - - /** - * Set fields to a given value. - * - * Can be used in two different ways, as set( field, value ) or as an array of - * fields to set: set( array( 'fieldName' => 'value', ...) ); - * @param string|string[] $set Can be given as a single string, when then $val - * must be set, or as an array of key/value pairs to be set. - * @param string $val When $set is given as a simple string, $set is the field - * name and this is the field's value. - * @param boolean $bind Should the value be bound or not - * @return self - */ - public function set ( $set, $val=null, $bind=true ) - { - if ( $set === null ) { - return $this; - } - - if ( !is_array($set) ) { - $set = array( $set => $val ); - } - - foreach ($set as $key => $value) { - $this->_field[] = $key; - - if ( $bind ) { - $this->bind( ':'.$key, $value ); - } - else { - $this->_noBind[$key] = $value; - } - } - - return $this; - } - - - /** - * Where query - multiple conditions are bound as ANDs. - * - * Can be used in two different ways, as where( field, value ) or as an array of - * conditions to use: where( array('fieldName', ...), array('value', ...) ); - * @param string|string[]|callable $key Single field name, or an array of field names. - * If given as a function (i.e. a closure), the function is called, passing the - * query itself in as the only parameter, so the function can add extra conditions - * with parentheses around the additional parameters. - * @param string|string[] $value Single field value, or an array of - * values. Can be null to search for `IS NULL` or `IS NOT NULL` (depending - * on the value of `$op` which should be `=` or `!=`. - * @param string $op Condition operator: <, >, = etc - * @param boolean $bind Escape the value (true, default) or not (false). - * @return self - * - * @example - * The following will produce - * `'WHERE name='allan' AND ( location='Scotland' OR location='Canada' )`: - * - * - * $query - * ->where( 'name', 'allan' ) - * ->where( function ($q) { - * $q->where( 'location', 'Scotland' ); - * $q->where( 'location', 'Canada' ); - * } ); - * - */ - public function where ( $key, $value=null, $op="=", $bind=true ) - { - if ( $key === null ) { - return $this; - } - else if ( is_callable($key) && is_object($key) ) { // is a closure - $this->_where_group( true, 'AND' ); - $key( $this ); - $this->_where_group( false, 'OR' ); - } - else if ( !is_array($key) && is_array($value) ) { - for ( $i=0 ; $iwhere( $key, $value[$i], $op, $bind ); - } - } - else { - $this->_where( $key, $value, 'AND ', $op, $bind ); - } - - return $this; - } - - - /** - * Add addition where conditions to the query with an AND operator. An alias - * of `where` for naming consistency. - * - * Can be used in two different ways, as where( field, value ) or as an array of - * conditions to use: where( array('fieldName', ...), array('value', ...) ); - * @param string|string[]|callable $key Single field name, or an array of field names. - * If given as a function (i.e. a closure), the function is called, passing the - * query itself in as the only parameter, so the function can add extra conditions - * with parentheses around the additional parameters. - * @param string|string[] $value Single field value, or an array of - * values. Can be null to search for `IS NULL` or `IS NOT NULL` (depending - * on the value of `$op` which should be `=` or `!=`. - * @param string $op Condition operator: <, >, = etc - * @param boolean $bind Escape the value (true, default) or not (false). - * @return self - */ - public function and_where ( $key, $value=null, $op="=", $bind=true ) - { - return $this->where( $key, $value, $op, $bind ); - } - - - /** - * Add addition where conditions to the query with an OR operator. - * - * Can be used in two different ways, as where( field, value ) or as an array of - * conditions to use: where( array('fieldName', ...), array('value', ...) ); - * @param string|string[]|callable $key Single field name, or an array of field names. - * If given as a function (i.e. a closure), the function is called, passing the - * query itself in as the only parameter, so the function can add extra conditions - * with parentheses around the additional parameters. - * @param string|string[] $value Single field value, or an array of - * values. Can be null to search for `IS NULL` or `IS NOT NULL` (depending - * on the value of `$op` which should be `=` or `!=`. - * @param string $op Condition operator: <, >, = etc - * @param boolean $bind Escape the value (true, default) or not (false). - * @return self - */ - public function or_where ( $key, $value=null, $op="=", $bind=true ) - { - if ( $key === null ) { - return $this; - } - else if ( is_callable($key) ) { - $this->_where_group( true, 'OR' ); - $key( $this ); - $this->_where_group( false, 'OR' ); - } - else { - if ( !is_array($key) && is_array($value) ) { - for ( $i=0 ; $ior_where( $key, $value[$i], $op, $bind ); - } - return $this; - } - - $this->_where( $key, $value, 'OR ', $op, $bind ); - } - - return $this; - } - - - /** - * Provide grouping for WHERE conditions. Use it with a callback function to - * automatically group any conditions applied inside the method. - * - * For legacy reasons this method also provides the ability to explicitly - * define if a grouping bracket should be opened or closed in the query. - * This method is not prefer. - * - * @param boolean|callable $inOut If callable it will create the group - * automatically and pass the query into the called function. For - * legacy operations use `true` to open brackets, `false` to close. - * @param string $op Conditional operator to use to join to the - * preceding condition. Default `AND`. - * @return self - * - * @example - * - * $query->where_group( function ($q) { - * $q->where( 'location', 'Edinburgh' ); - * $q->where( 'position', 'Manager' ); - * } ); - * - */ - public function where_group ( $inOut, $op='AND' ) - { - if ( is_callable($inOut) ) { - $this->_where_group( true, $op ); - $inOut( $this ); - $this->_where_group( false, $op ); - } - else { - $this->_where_group( $inOut, $op ); - } - - return $this; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Protected methods - */ - - /** - * Create a comma separated field list - * @param bool $addAlias Flag to add an alias - * @return string - * @internal - */ - protected function _build_field( $addAlias=false ) - { - $a = array(); - - for ( $i=0 ; $i_field) ; $i++ ) { - $field = $this->_field[$i]; - - // Keep the name when referring to a table - if ( $addAlias && strpos($field, ' as ') !== false && $field !== '*' ) { - $split = explode(' as ', $field); - $a[] = $this->_protect_identifiers( $split[0] ).' as '. - $this->_field_quote. $split[1] .$this->_field_quote; - } - else if ( $addAlias && strpos($field, ' as ') === false && $field !== '*' ) { - $a[] = $this->_protect_identifiers( $field ).' as '. - $this->_field_quote. $field .$this->_field_quote; - } - else { - $a[] = $this->_protect_identifiers( $field ); - } - } - - return ' '.implode(', ', $a).' '; - } - - /** - * Create a JOIN statement list - * @return string - * @internal - */ - protected function _build_join() - { - return implode(' ', $this->_join); - } - - /** - * Create the LIMIT / OFFSET string - * - * MySQL and Postgres stylee - anything else can have the driver override - * @return string - * @internal - */ - protected function _build_limit() - { - $out = ''; - - if ( $this->_limit ) { - $out .= ' LIMIT '.$this->_limit; - } - - if ( $this->_offset ) { - $out .= ' OFFSET '.$this->_offset; - } - - return $out; - } - - /** - * Create the ORDER BY string - * @return string - * @internal - */ - protected function _build_order() - { - if ( count( $this->_order ) > 0 ) { - return ' ORDER BY '.implode(', ', $this->_order).' '; - } - return ''; - } - - /** - * Create a set list - * @return string - * @internal - */ - protected function _build_set() - { - $a = array(); - - for ( $i=0 ; $i_field) ; $i++ ) { - $field = $this->_field[$i]; - - if ( isset( $this->_noBind[ $field ] ) ) { - $a[] = $this->_protect_identifiers( $field ) .' = '. $this->_noBind[ $field ]; - } - else { - $a[] = $this->_protect_identifiers( $field ) .' = :'. $this->_safe_bind( $field ); - } - } - - return ' '.implode(', ', $a).' '; - } - - /** - * Create the TABLE list - * @return string - * @internal - */ - protected function _build_table() - { - return ' '.implode(', ', $this->_table).' '; - } - - /** - * Create a bind field value list - * @return string - * @internal - */ - protected function _build_value() - { - $a = array(); - - for ( $i=0, $ien=count($this->_field) ; $i<$ien ; $i++ ) { - $a[] = ' :'.$this->_safe_bind( $this->_field[$i] ); - } - - return ' '.implode(', ', $a).' '; - } - - /** - * Create the WHERE statement - * @return string - * @internal - */ - protected function _build_where() - { - if ( count($this->_where) === 0 ) { - return ""; - } - - $condition = "WHERE "; - - for ( $i=0 ; $i_where) ; $i++ ) { - if ( $i === 0 ) { - // Nothing (simplifies the logic!) - } - else if ( $this->_where[$i]['group'] === ')' ) { - // If a group has been used but no conditions were added inside - // of, we don't want to end up with `()` in the SQL as that is - // invalid, so add a 1. - if ( $this->_where[$i-1]['group'] === '(' ) { - $condition .= '1=1'; - } - // else nothing - } - else if ( $this->_where[$i-1]['group'] === '(' ) { - // Nothing - } - else { - $condition .= $this->_where[$i]['operator']; - } - - if ( $this->_where[$i]['group'] !== null ) { - $condition .= $this->_where[$i]['group']; - } - else { - $condition .= $this->_where[$i]['query'] .' '; - } - } - - return $condition; - } - - /** - * Create a DELETE statement - * @return Result - * @internal - */ - protected function _delete() - { - $this->_prepare( - 'DELETE FROM ' - .$this->_build_table() - .$this->_build_where() - ); - - return $this->_exec(); - } - - /** - * Execute the query. Provided by the driver - * @return Result - * @internal - */ - protected function _exec() - {} - - /** - * Create an INSERT statement - * @return Result - * @internal - */ - protected function _insert() - { - $this->_prepare( - 'INSERT INTO ' - .$this->_build_table().' (' - .$this->_build_field() - .') ' - .'VALUES (' - .$this->_build_value() - .')' - ); - - return $this->_exec(); - } - - /** - * Prepare the SQL query by populating the bound variables. - * Provided by the driver - * @return void - * @internal - */ - protected function _prepare( $sql ) - {} - - /** - * Protect field names - * @param string $identifier String to be protected - * @return string - * @internal - */ - protected function _protect_identifiers( $identifier ) - { - $idl = $this->_identifier_limiter; - - // No escaping character - if ( ! $idl ) { - return $identifier; - } - - $left = $idl[0]; - $right = $idl[1]; - - // Dealing with a function or other expression? Just return immediately - if (strpos($identifier, '(') !== FALSE || strpos($identifier, '*') !== FALSE || strpos($identifier, ' ') !== FALSE) - { - return $identifier; - } - - // Going to be operating on the spaces in strings, to simplify the white-space - $identifier = preg_replace('/[\t ]+/', ' ', $identifier); - - // Find if our identifier has an alias, so we don't escape that - if ( strpos($identifier, ' as ') !== false ) { - $alias = strstr($identifier, ' as '); - $identifier = substr($identifier, 0, - strlen($alias)); - } - else { - $alias = ''; - } - - $a = explode('.', $identifier); - return $left . implode($right.'.'.$left, $a) . $right . $alias; - } - - /** - * Passed in SQL statement - * @return Result - * @internal - */ - protected function _raw( $sql ) - { - $this->_prepare( $sql ); - - return $this->_exec(); - } - - /** - * The characters that can be used for the PDO bindValue name are quite - * limited (`[a-zA-Z0-9_]+`). We need to abstract this out to allow slightly - * more complex expressions including dots for easy aliasing - * @param string $name Field name - * @return string - * @internal - */ - protected function _safe_bind ( $name ) - { - $name = str_replace('.', '_1_', $name); - $name = str_replace('-', '_2_', $name); - - return $name; - } - - /** - * Create a SELECT statement - * @return Result - * @internal - */ - protected function _select() - { - $this->_prepare( - 'SELECT '.($this->_distinct ? 'DISTINCT ' : '') - .$this->_build_field( true ) - .'FROM '.$this->_build_table() - .$this->_build_join() - .$this->_build_where() - .$this->_build_order() - .$this->_build_limit() - ); - - return $this->_exec(); - } - - /** - * Create an UPDATE statement - * @return Result - * @internal - */ - protected function _update() - { - $this->_prepare( - 'UPDATE ' - .$this->_build_table() - .'SET '.$this->_build_set() - .$this->_build_where() - ); - - return $this->_exec(); - } - - /** - * Add an individual where condition to the query. - * @internal - * @param $where - * @param null $value - * @param string $type - * @param string $op - * @param bool $bind - */ - protected function _where ( $where, $value=null, $type='AND ', $op="=", $bind=true ) - { - if ( $where === null ) { - return; - } - else if ( !is_array($where) ) { - $where = array( $where => $value ); - } - - foreach ($where as $key => $value) { - $i = count( $this->_where ); - - if ( $value === null ) { - // Null query - $this->_where[] = array( - 'operator' => $type, - 'group' => null, - 'field' => $this->_protect_identifiers($key), - 'query' => $this->_protect_identifiers($key) .( $op === '=' ? - ' IS NULL' : - ' IS NOT NULL') - ); - } - else if ( $bind ) { - // Binding condition (i.e. escape data) - $this->_where[] = array( - 'operator' => $type, - 'group' => null, - 'field' => $this->_protect_identifiers($key), - 'query' => $this->_protect_identifiers($key) .' '.$op.' '.$this->_safe_bind(':where_'.$i) - ); - $this->bind( ':where_'.$i, $value ); - } - else { - // Non-binding condition - $this->_where[] = array( - 'operator' => $type, - 'group' => null, - 'field' => null, - 'query' => $this->_protect_identifiers($key) .' '. $op .' '. $this->_protect_identifiers($value) - ); - } - } - } - - /** - * Add parentheses to a where condition - * @return string - * @internal - */ - protected function _where_group ( $inOut, $op ) - { - $this->_where[] = array( - "group" => $inOut ? '(' : ')', - "operator" => $op - ); - } -}; - - diff --git a/php/Database/Result.php b/php/Database/Result.php deleted file mode 100644 index af5d55b..0000000 --- a/php/Database/Result.php +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/php/Editor/Editor.php b/php/Editor/Editor.php deleted file mode 100644 index 05e61b9..0000000 --- a/php/Editor/Editor.php +++ /dev/null @@ -1,2000 +0,0 @@ - - * Editor::inst( $db, 'browsers' ) - * ->fields( - * Field::inst( 'first_name' )->validator( 'Validate::required' ), - * Field::inst( 'last_name' )->validator( 'Validate::required' ), - * Field::inst( 'country' ), - * Field::inst( 'details' ) - * ) - * ->process( $_POST ) - * ->json(); - * - */ -class Editor extends Ext { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Statics - */ - - /** Request type - read */ - const ACTION_READ = 'read'; - - /** Request type - create */ - const ACTION_CREATE = 'create'; - - /** Request type - edit */ - const ACTION_EDIT = 'edit'; - - /** Request type - delete */ - const ACTION_DELETE = 'delete'; - - /** Request type - upload */ - const ACTION_UPLOAD = 'upload'; - - - /** - * Determine the request type from an HTTP request. - * - * @param array $http Typically $_POST, but can be any array used to carry - * an Editor payload - * @return string `Editor::ACTION_READ`, `Editor::ACTION_CREATE`, - * `Editor::ACTION_EDIT` or `Editor::ACTION_DELETE` indicating the request - * type. - */ - static public function action ( $http ) - { - if ( ! isset( $http['action'] ) ) { - return self::ACTION_READ; - } - - switch ( $http['action'] ) { - case 'create': - return self::ACTION_CREATE; - - case 'edit': - return self::ACTION_EDIT; - - case 'remove': - return self::ACTION_DELETE; - - case 'upload': - return self::ACTION_UPLOAD; - - default: - throw new \Exception("Unknown Editor action: ".$http['action']); - } - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Constructor - */ - - /** - * Constructor. - * @param Database $db An instance of the DataTables Database class that we can - * use for the DB connection. Can be given here or with the 'db' method. - * - * 456 - * - * @param string|array $table The table name in the database to read and write - * information from and to. Can be given here or with the 'table' method. - * @param string|array $pkey Primary key column name in the table given in - the $table parameter. Can be given here or with the 'pkey' method. - */ - function __construct( $db=null, $table=null, $pkey=null ) - { - // Set constructor parameters using the API - note that the get/set will - // ignore null values if they are used (i.e. not passed in) - $this->db( $db ); - $this->table( $table ); - $this->pkey( $pkey ); - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public properties - */ - - /** @var string */ - public $version = '1.6.1'; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - /** @var DataTables\Database */ - private $_db = null; - - /** @var DataTables\Editor\Field[] */ - private $_fields = array(); - - /** @var array */ - private $_formData; - - /** @var array */ - private $_processData; - - /** @var string */ - private $_idPrefix = 'row_'; - - /** @var DataTables\Editor\Join[] */ - private $_join = array(); - - /** @var array */ - private $_pkey = array('id'); - - /** @var string[] */ - private $_table = array(); - - /** @var boolean */ - private $_transaction = true; - - /** @var array */ - private $_where = array(); - - /** @var array */ - private $_leftJoin = array(); - - /** @var boolean - deprecated */ - private $_whereSet = false; - - /** @var array */ - private $_out = array( - "fieldErrors" => array(), - "error" => "", - "data" => array(), - "ipOpts" => array(), - "cancelled" => array() - ); - - /** @var array */ - private $_events = array(); - - /** @var boolean */ - private $_debug = false; - - /** @var callback */ - private $_validator = null; - - /** @var boolean Enable true / catch when processing */ - private $_tryCatch = true; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - /** - * Get the data constructed in this instance. - * - * This will get the PHP array of data that has been constructed for the - * command that has been processed by this instance. Therefore only useful after - * process has been called. - * @return array Processed data array. - */ - public function data () - { - return $this->_out; - } - - - /** - * Get / set the DB connection instance - * @param Database $_ DataTable's Database class instance to use for database - * connectivity. If not given, then used as a getter. - * @return Database|self The Database connection instance if no parameter - * is given, or self if used as a setter. - */ - public function db ( $_=null ) - { - return $this->_getSet( $this->_db, $_ ); - } - - - /** - * Get / set debug mode. - * - * It can be useful to see the SQL statements that Editor is using. This - * method enables that ability. Information about the queries used is - * automatically added to the output data array / JSON under the property - * name `debugSql`. - * - * @param boolean $_ Debug mode state. If not given, then used as a getter. - * @return boolean|self Debug mode state if no parameter is given, or - * self if used as a setter. - */ - public function debug ( $_=null ) - { - return $this->_getSet( $this->_debug, $_ ); - } - - - /** - * Get / set field instance. - * - * The list of fields designates which columns in the table that Editor will work - * with (both get and set). - * @param Field|string $_... This parameter effects the return value of the - * function: - * - * * `null` - Get an array of all fields assigned to the instance - * * `string` - Get a specific field instance whose 'name' matches the - * field passed in - * * {@link Field} - Add a field to the instance's list of fields. This - * can be as many fields as required (i.e. multiple arguments) - * * `array` - An array of {@link Field} instances to add to the list - * of fields. - * @return Field|Field[]|Editor The selected field, an array of fields, or - * the Editor instance for chaining, depending on the input parameter. - * @throws \Exception Unkown field error - * @see {@link Field} for field documentation. - */ - public function field ( $_=null ) - { - if ( is_string( $_ ) ) { - for ( $i=0, $ien=count($this->_fields) ; $i<$ien ; $i++ ) { - if ( $this->_fields[$i]->name() === $_ ) { - return $this->_fields[$i]; - } - } - - throw new \Exception('Unknown field: '.$_); - } - - if ( $_ !== null && !is_array($_) ) { - $_ = func_get_args(); - } - return $this->_getSet( $this->_fields, $_, true ); - } - - - /** - * Get / set field instances. - * - * An alias of {@link field}, for convenience. - * @param Field $_... Instances of the {@link Field} class, given as a single - * instance of {@link Field}, an array of {@link Field} instances, or multiple - * {@link Field} instance parameters for the function. - * @return Field[]|self Array of fields, or self if used as a setter. - * @see {@link Field} for field documentation. - */ - public function fields ( $_=null ) - { - if ( $_ !== null && !is_array($_) ) { - $_ = func_get_args(); - } - return $this->_getSet( $this->_fields, $_, true ); - } - - - /** - * Get / set the DOM prefix. - * - * Typically primary keys are numeric and this is not a valid ID value in an - * HTML document - is also increases the likelihood of an ID clash if multiple - * tables are used on a single page. As such, a prefix is assigned to the - * primary key value for each row, and this is used as the DOM ID, so Editor - * can track individual rows. - * @param string $_ Primary key's name. If not given, then used as a getter. - * @return string|self Primary key value if no parameter is given, or - * self if used as a setter. - */ - public function idPrefix ( $_=null ) - { - return $this->_getSet( $this->_idPrefix, $_ ); - } - - - /** - * Get the data that is being processed by the Editor instance. This is only - * useful once the `process()` method has been called, and is available for - * use in validation and formatter methods. - * - * @return array Data given to `process()`. - */ - public function inData () - { - return $this->_processData; - } - - - /** - * Get / set join instances. Note that for the majority of use cases you - * will want to use the `leftJoin()` method. It is significantly easier - * to use if you are just doing a simple left join! - * - * The list of Join instances that Editor will join the parent table to - * (i.e. the one that the {@link table} and {@link fields} methods refer to - * in this class instance). - * - * @param Join $_,... Instances of the {@link Join} class, given as a - * single instance of {@link Join}, an array of {@link Join} instances, - * or multiple {@link Join} instance parameters for the function. - * @return Join[]|self Array of joins, or self if used as a setter. - * @see {@link Join} for joining documentation. - */ - public function join ( $_=null ) - { - if ( $_ !== null && !is_array($_) ) { - $_ = func_get_args(); - } - return $this->_getSet( $this->_join, $_, true ); - } - - - /** - * Get the JSON for the data constructed in this instance. - * - * Basically the same as the {@link data} method, but in this case we echo, or - * return the JSON string of the data. - * @param boolean $print Echo the JSON string out (true, default) or return it - * (false). - * @return string|self self if printing the JSON, or JSON representation of - * the processed data if false is given as the first parameter. - */ - public function json ( $print=true ) - { - if ( $print ) { - echo json_encode( $this->_out ); - return $this; - } - return json_encode( $this->_out ); - } - - - /** - * Echo out JSONP for the data constructed and processed in this instance. - * This is basically the same as {@link json} but wraps the return in a - * JSONP callback. - * - * @param string $callback The callback function name to use. If not given - * or `null`, then `$_GET['callback']` is used (the jQuery default). - * @return self Self for chaining. - * @throws \Exception JSONP function name validation - */ - public function jsonp ( $callback=null ) - { - if ( ! $callback ) { - $callback = $_GET['callback']; - } - - if ( preg_match('/[^a-zA-Z0-9_]/', $callback) ) { - throw new \Exception("Invalid JSONP callback function name"); - } - - echo $callback.'('.json_encode( $this->_out ).');'; - return $this; - } - - - /** - * Add a left join condition to the Editor instance, allowing it to operate - * over multiple tables. Multiple `leftJoin()` calls can be made for a - * single Editor instance to join multiple tables. - * - * A left join is the most common type of join that is used with Editor - * so this method is provided to make its use very easy to configure. Its - * parameters are basically the same as writing an SQL left join statement, - * but in this case Editor will handle the create, update and remove - * requirements of the join for you: - * - * * Create - On create Editor will insert the data into the primary table - * and then into the joined tables - selecting the required data for each - * table. - * * Edit - On edit Editor will update the main table, and then either - * update the existing rows in the joined table that match the join and - * edit conditions, or insert a new row into the joined table if required. - * * Remove - On delete Editor will remove the main row and then loop over - * each of the joined tables and remove the joined data matching the join - * link from the main table. - * - * Please note that when using join tables, Editor requires that you fully - * qualify each field with the field's table name. SQL can result table - * names for ambiguous field names, but for Editor to provide its full CRUD - * options, the table name must also be given. For example the field - * `first_name` in the table `users` would be given as `users.first_name`. - * - * @param string $table Table name to do a join onto - * @param string $field1 Field from the parent table to use as the join link - * @param string $operator Join condition (`=`, '<`, etc) - * @param string $field2 Field from the child table to use as the join link - * @return self Self for chaining. - * - * @example - * Simple join: - * - * ->field( - * Field::inst( 'users.first_name as myField' ), - * Field::inst( 'users.last_name' ), - * Field::inst( 'users.dept_id' ), - * Field::inst( 'dept.name' ) - * ) - * ->leftJoin( 'dept', 'users.dept_id', '=', 'dept.id' ) - * ->process($_POST) - * ->json(); - * - * - * This is basically the same as the following SQL statement: - * - * - * SELECT users.first_name, users.last_name, user.dept_id, dept.name - * FROM users - * LEFT JOIN dept ON users.dept_id = dept.id - * - */ - public function leftJoin ( $table, $field1, $operator, $field2 ) - { - $this->_leftJoin[] = array( - "table" => $table, - "field1" => $field1, - "field2" => $field2, - "operator" => $operator - ); - - return $this; - } - - - /** - * Add an event listener. The `Editor` class will trigger an number of - * events that some action can be taken on. - * - * @param [type] $name Event name - * @param [type] $callback Callback function to execute when the event - * occurs - * @return self Self for chaining. - */ - public function on ( $name, $callback ) - { - if ( ! isset( $this->_events[ $name ] ) ) { - $this->_events[ $name ] = array(); - } - - $this->_events[ $name ][] = $callback; - - return $this; - } - - - /** - * Get / set the table name. - * - * The table name designated which DB table Editor will use as its data - * source for working with the database. Table names can be given with an - * alias, which can be used to simplify larger table names. The field - * names would also need to reflect the alias, just like an SQL query. For - * example: `users as a`. - * - * @param string|array $_,... Table names given as a single string, an array of - * strings or multiple string parameters for the function. - * @return string[]|self Array of tables names, or self if used as a setter. - */ - public function table ( $_=null ) - { - if ( $_ !== null && !is_array($_) ) { - $_ = func_get_args(); - } - return $this->_getSet( $this->_table, $_, true ); - } - - - /** - * Get / set transaction support. - * - * When enabled (which it is by default) Editor will use an SQL transaction - * to ensure data integrity while it is performing operations on the table. - * This can be optionally disabled using this method, if required by your - * database configuration. - * - * @param boolean $_ Enable (`true`) or disabled (`false`) transactions. - * If not given, then used as a getter. - * @return boolean|self Transactions enabled flag, or self if used as a - * setter. - */ - public function transaction ( $_=null ) - { - return $this->_getSet( $this->_transaction, $_ ); - } - - - /** - * Get / set the primary key. - * - * The primary key must be known to Editor so it will know which rows are being - * edited / deleted upon those actions. The default value is ['id']. - * - * @param string|array $_ Primary key's name. If not given, then used as a - * getter. An array of column names can be given to allow composite keys to - * be used. - * @return string|self Primary key value if no parameter is given, or - * self if used as a setter. - */ - public function pkey ( $_=null ) - { - if ( is_string( $_ ) ) { - $this->_pkey = array( $_ ); - return $this; - } - return $this->_getSet( $this->_pkey, $_ ); - } - - - /** - * Convert a primary key array of field values to a combined value. - * - * @param string $row The row of data that the primary key value should - * be extracted from. - * @param boolean $flat Flag to indicate if the given array is flat - * (useful for `where` conditions) or nested for join tables. - * @return string The created primary key value. - * @throws \Exception If one of the values that the primary key is made up - * of cannot be found in the data set given, an Exception will be thrown. - */ - public function pkeyToValue ( $row, $flat=false ) - { - $pkey = $this->_pkey; - $id = array(); - - for ( $i=0, $ien=count($pkey) ; $i<$ien ; $i++ ) { - $column = $pkey[ $i ]; - - if ( $flat ) { - $val = isset( $row[ $column ] ) ? - $row[ $column ] : - null; - } - else { - $val = $this->_readProp( $column, $row ); - } - - if ( $val === null ) { - throw new \Exception("Primary key element is not available in data set.", 1); - } - - $id[] = $val; - } - - return implode( $this->_pkey_separator(), $id ); - } - - - /** - * Convert a primary key combined value to an array of field values. - * - * @param string $value The id that should be split apart - * @param boolean $flat Flag to indicate if the returned array should be - * flat (useful for `where` conditions) or nested for join tables. - * @return array Array of field values that the id was made up of. - * @throws \Exception If the primary key value does not match the expected - * length based on the primary key configuration, an exception will be - * thrown. - */ - public function pkeyToArray ( $value, $flat=false ) - { - $arr = array(); - $value = str_replace( $this->idPrefix(), '', $value ); - $idParts = explode( $this->_pkey_separator(), $value ); - - if ( count($this->_pkey) !== count($idParts) ) { - throw new \Exception("Primary key data doesn't match submitted data", 1); - } - - for ( $i=0, $ien=count($idParts) ; $i<$ien ; $i++ ) { - if ( $flat ) { - $arr[ $this->_pkey[$i] ] = $idParts[$i]; - } - else { - $this->_writeProp( $arr, $this->_pkey[$i], $idParts[$i] ); - } - } - - return $arr; - } - - - /** - * Process a request from the Editor client-side to get / set data. - * - * @param array $data Typically $_POST or $_GET as required by what is sent - by Editor - * @return self - */ - public function process ( $data ) - { - if ( $this->_debug ) { - $debugVal = $this->_db->debug(); - $this->_db->debug( true ); - } - - if ( $this->_tryCatch ) { - try { - $this->_process( $data ); - } - catch (\Exception $e) { - // Error feedback - $this->_out['error'] = $e->getMessage(); - - if ( $this->_transaction ) { - $this->_db->rollback(); - } - } - } - else { - $this->_process( $data ); - } - - if ( $this->_debug ) { - $this->_out['debugSql'] = $this->_db->debugInfo(); - $this->_db->debug( $debugVal ); - } - - return $this; - } - - - /** - * Enable / try catch when `process()` is called. Disabling this can be - * useful for debugging, but is not recommended for production. - * - * @param boolean $_ `true` to enable (default), otherwise false to disable - * @return boolean|Editor Value if used as a getter, otherwise `$this` when - * used as a setter. - */ - public function tryCatch ( $_=null ) - { - return $this->_getSet( $this->_tryCatch, $_ ); - } - - - /** - * Perform validation on a data set. - * - * Note that validation is performed on data only when the action is - * `create` or `edit`. Additionally, validation is performed on the _wire - * data_ - i.e. that which is submitted from the client, without formatting. - * Any formatting required by `setFormatter` is performed after the data - * from the client has been validated. - * - * @param &array $errors Output array to which field error information will - * be written. Each element in the array represents a field in an error - * condition. These elements are themselves arrays with two properties - * set; `name` and `status`. - * @param array $data The format data to check - * @return boolean `true` if the data is valid, `false` if not. - */ - public function validate ( &$errors, $data ) - { - // Validation is only performed on create and edit - if ( $data['action'] != "create" && $data['action'] != "edit" ) { - return true; - } - - foreach( $data['data'] as $id => $values ) { - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - $validation = $field->validate( $values, $this, - str_replace( $this->idPrefix(), '', $id ) - ); - - if ( $validation !== true ) { - $errors[] = array( - "name" => $field->name(), - "status" => $validation - ); - } - } - - // MJoin validation - for ( $i=0 ; $i_join) ; $i++ ) { - $this->_join[$i]->validate( $errors, $this, $values ); - } - } - - return count( $errors ) > 0 ? false : true; - } - - - /** - * Get / set a global validator that will be triggered for the create, edit - * and remove actions performed from the client-side. - * - * @param [type] $_ Function to execute when validating the input data. - * It is passed three parameters: 1. The editor instance, 2. The action - * and 3. The values. - * @return [Editor|callback] Editor instance if called as a setter, or the - * validator function if not. - */ - public function validator ( $_=null ) - { - return $this->_getSet( $this->_validator, $_ ); - } - - - /** - * Where condition to add to the query used to get data from the database. - * - * Can be used in two different ways: - * - * * Simple case: `where( field, value, operator )` - * * Complex: `where( fn )` - * - * The simple case is fairly self explanatory, a condition is applied to the - * data that looks like `field operator value` (e.g. `name = 'Allan'`). The - * complex case allows full control over the query conditions by providing a - * closure function that has access to the database Query that Editor is - * using, so you can use the `where()`, `or_where()`, `and_where()` and - * `where_group()` methods as you require. - * - * Please be very careful when using this method! If an edit made by a user - * using Editor removes the row from the where condition, the result is - * undefined (since Editor expects the row to still be available, but the - * condition removes it from the result set). - * - * @param string|callable $key Single field name or a closure function - * @param string $value Single field value. - * @param string $op Condition operator: <, >, = etc - * @return string[]|self Where condition array, or self if used as a setter. - */ - public function where ( $key=null, $value=null, $op='=' ) - { - if ( $key === null ) { - return $this->_where; - } - - if ( is_callable($key) && is_object($key) ) { - $this->_where[] = $key; - } - else { - $this->_where[] = array( - "key" => $key, - "value" => $value, - "op" => $op - ); - } - - return $this; - } - - - /** - * Get / set if the WHERE conditions should be included in the create and - * edit actions. - * - * @param boolean $_ Include (`true`), or not (`false`) - * @return boolean Current value - * @deprecated Note that `whereSet` is now deprecated and replaced with the - * ability to set values for columns on create and edit. The C# libraries - * do not support this option at all. - */ - public function whereSet ( $_=null ) - { - return $this->_getSet( $this->_whereSet, $_ ); - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private methods - */ - - /** - * Process a request from the Editor client-side to get / set data. - * - * @param array $data Data to process - * @private - */ - private function _process( $data ) - { - $this->_processData = $data; - $this->_formData = isset($data['data']) ? $data['data'] : null; - $validator = $this->_validator; - - if ( $this->_transaction ) { - $this->_db->transaction(); - } - - $this->_prepJoin(); - - if ( $validator ) { - $ret = $validator( $this, !isset($data['action']) ? self::ACTION_READ : $data['action'], $data ); - - if ( $ret ) { - $this->_out['error'] = $ret; - } - } - - if ( ! $this->_out['error'] ) { - if ( ! isset($data['action']) ) { - /* Get data */ - $this->_out = array_merge( $this->_out, $this->_get( null, $data ) ); - } - else if ( $data['action'] == "upload" ) { - /* File upload */ - $this->_upload( $data ); - } - else if ( $data['action'] == "remove" ) { - /* Remove rows */ - $this->_remove( $data ); - $this->_fileClean(); - } - else { - /* Create or edit row */ - // Pre events so they can occur before the validation - foreach ($data['data'] as $idSrc => $values) { - $cancel = null; - - if ( $data['action'] == 'create' ) { - $cancel = $this->_trigger( 'preCreate', $values ); - } - else { - $id = str_replace( $this->_idPrefix, '', $idSrc ); - $cancel = $this->_trigger( 'preEdit', $id, $values ); - } - - // One of the event handlers returned false - don't continue - if ( $cancel === false ) { - // Remove the data from the data set so it won't be processed - unset( $data['data'][$idSrc] ); - - // Tell the client-side we aren't updating this row - $this->_out['cancelled'][] = $idSrc; - } - } - - // Field validation - $valid = $this->validate( $this->_out['fieldErrors'], $data ); - - if ( $valid ) { - foreach ($data['data'] as $id => $values) { - $d = $data['action'] == "create" ? - $this->_insert( $values ) : - $this->_update( $id, $values ); - - if ( $d !== null ) { - $this->_out['data'][] = $d; - } - } - } - - $this->_fileClean(); - } - } - - if ( $this->_transaction ) { - $this->_db->commit(); - } - - // Tidy up the reply - if ( count( $this->_out['fieldErrors'] ) === 0 ) { - unset( $this->_out['fieldErrors'] ); - } - - if ( $this->_out['error'] === '' ) { - unset( $this->_out['error'] ); - } - - if ( count( $this->_out['ipOpts'] ) === 0 ) { - unset( $this->_out['ipOpts'] ); - } - - if ( count( $this->_out['cancelled'] ) === 0 ) { - unset( $this->_out['cancelled'] ); - } - } - - - /** - * Get an array of objects from the database to be given to DataTables as a - * result of an sAjaxSource request, such that DataTables can display the information - * from the DB in the table. - * - * @param integer|string $id Primary key value to get an individual row - (after create or update operations). Gets the full set if not given. - If a compound key is being used, this should be the string - representation of it (i.e. joined together) rather than an array form. - * @param array $http HTTP parameters from GET or POST request (so we can service - * server-side processing requests from DataTables). - * @return array DataTables get information - * @throws \Exception Error on SQL execution - * @private - */ - private function _get( $id=null, $http=null ) - { - - $cancel = $this->_trigger( 'preGet', $id ); - if ( $cancel === false ) { - return array(); - } - - $query = $this->_db - ->query('select') - ->table( $this->_table ) - ->get( $this->_pkey ); - - // Add all fields that we need to get from the database - foreach ($this->_fields as $field) { - // Don't reselect a pkey column if it was already added - if ( in_array( $field->dbField(), $this->_pkey ) ) { - continue; - } - - if ( $field->apply('get') && $field->getValue() === null ) { - $query->get( $field->dbField() ); - } - } - - $this->_get_where( $query ); - $this->_perform_left_join( $query ); - $ssp = $this->_ssp_query( $query, $http ); - - if ( $id !== null ) { - $query->where( $this->pkeyToArray( $id, true ) ); - } - - $res = $query->exec(); - if ( ! $res ) { - throw new \Exception('Error executing SQL for data get. Enable SQL debug using `->debug(true)`'); - } - - $out = array(); - while ( $row=$res->fetch() ) { - $inner = array(); - $inner['DT_RowId'] = $this->_idPrefix . $this->pkeyToValue( $row, true ); - - foreach ($this->_fields as $field) { - if ( $field->apply('get') ) { - $field->write( $inner, $row ); - } - } - - $out[] = $inner; - } - - // Field options - $options = array(); - - foreach ($this->_fields as $field) { - $opts = $field->optionsExec( $this->_db ); - - if ( $opts !== false ) { - $options[ $field->name() ] = $opts; - } - } - - // Row based "joins" - for ( $i=0 ; $i_join) ; $i++ ) { - $this->_join[$i]->data( $this, $out, $options ); - } - - $this->_trigger( 'postGet', $out, $id ); - - return array_merge( - array( - 'data' => $out, - 'options' => $options, - 'files' => $this->_fileData() - ), - $ssp - ); - } - - - /** - * Insert a new row in the database - * @private - */ - private function _insert( $values ) - { - // Only allow a composite insert if the values for the key are - // submitted. This is required because there is no reliable way in MySQL - // to return the newly inserted row, so we can't know any newly - // generated values. - $this->_pkey_validate_insert( $values ); - - // Insert the new row - $id = $this->_insert_or_update( null, $values ); - - // Was the primary key altered as part of the edit, if so use the - // submitted values - $id = count( $this->_pkey ) > 1 ? - $this->pkeyToValue( $values ) : - $this->_pkey_submit_merge( $id, $values ); - - // Join tables - for ( $i=0 ; $i_join) ; $i++ ) { - $this->_join[$i]->create( $this, $id, $values ); - } - - // Full data set for the created row - $row = $this->_get( $id ); - $row = count( $row['data'] ) > 0 ? - $row['data'][0] : - null; - - $this->_trigger( 'postCreate', $id, $values, $row ); - - return $row; - } - - - /** - * Update a row in the database - * @param string $id The DOM ID for the row that is being edited. - * @return array Row's data - * @private - */ - private function _update( $id, $values ) - { - $id = str_replace( $this->_idPrefix, '', $id ); - - // Update or insert the rows for the parent table and the left joined - // tables - $this->_insert_or_update( $id, $values ); - - // And the join tables - for ( $i=0 ; $i_join) ; $i++ ) { - $this->_join[$i]->update( $this, $id, $values ); - } - - // Was the primary key altered as part of the edit, if so use the - // submitted values - $getId = $this->_pkey_submit_merge( $id, $values ); - - // Full data set for the modified row - $row = $this->_get( $getId ); - $row = count( $row['data'] ) > 0 ? - $row['data'][0] : - null; - - $this->_trigger( 'postEdit', $id, $values, $row ); - - return $row; - } - - - /** - * Delete one or more rows from the database - * @private - */ - private function _remove( $data ) - { - $ids = array(); - - // Get the ids to delete from the data source - foreach ($data['data'] as $idSrc => $rowData) { - // Strip the ID prefix that the client-side sends back - $id = str_replace( $this->_idPrefix, "", $idSrc ); - - $res = $this->_trigger( 'preRemove', $id, $rowData ); - - // Allow the event to be cancelled and inform the client-side - if ( $res === false ) { - $this->_out['cancelled'][] = $idSrc; - } - else { - $ids[] = $id; - } - } - - if ( count( $ids ) === 0 ) { - return; - } - - // Row based joins - remove first as the host row will be removed which - // is a dependency - for ( $i=0 ; $i_join) ; $i++ ) { - $this->_join[$i]->remove( $this, $ids ); - } - - // Remove from the left join tables - for ( $i=0, $ien=count($this->_leftJoin) ; $i<$ien ; $i++ ) { - $join = $this->_leftJoin[$i]; - $table = $this->_alias( $join['table'], 'orig' ); - - // which side of the join refers to the parent table? - if ( strpos( $join['field1'], $join['table'] ) === 0 ) { - $parentLink = $join['field2']; - $childLink = $join['field1']; - } - else { - $parentLink = $join['field1']; - $childLink = $join['field2']; - } - - // Only delete on the primary key, since that is what the ids refer - // to - otherwise we'd be deleting random data! Note that this - // won't work with compound keys since the parent link would be - // over multiple fields. - if ( $parentLink === $this->_pkey[0] && count($this->_pkey) === 1 ) { - $this->_remove_table( $join['table'], $ids, array($childLink) ); - } - } - - // Remove from the primary tables - for ( $i=0, $ien=count($this->_table) ; $i<$ien ; $i++ ) { - $this->_remove_table( $this->_table[$i], $ids ); - } - - foreach ($data['data'] as $idSrc => $rowData) { - $id = str_replace( $this->_idPrefix, "", $idSrc ); - - $this->_trigger( 'postRemove', $id, $rowData ); - } - } - - - /** - * File upload - * @param array $data Upload data - * @throws \Exception File upload name error - * @private - */ - private function _upload( $data ) - { - // Search for upload field in local fields - $field = $this->_find_field( $data['uploadField'], 'name' ); - $fieldName = ''; - - if ( ! $field ) { - // Perhaps it is in a join instance - for ( $i=0 ; $i_join) ; $i++ ) { - $join = $this->_join[$i]; - $fields = $join->fields(); - - for ( $j=0, $jen=count($fields) ; $j<$jen ; $j++ ) { - $joinField = $fields[ $j ]; - $name = $join->name().'[].'.$joinField->name(); - - if ( $name === $data['uploadField'] ) { - $field = $joinField; - $fieldName = $name; - } - } - } - } - else { - $fieldName = $field->name(); - } - - if ( ! $field ) { - throw new \Exception("Unknown upload field name submitted"); - } - - $upload = $field->upload(); - if ( ! $upload ) { - throw new \Exception("File uploaded to a field that does not have upload options configured"); - } - - $res = $upload->exec( $this ); - - if ( $res === false ) { - $this->_out['fieldErrors'][] = array( - "name" => $fieldName, // field name can be just the field's - "status" => $upload->error() // name or a join combination - ); - } - else { - $files = $this->_fileData( $upload->table(), $res ); - - $this->_out['files'] = $files; - $this->_out['upload']['id'] = $res; - } - } - - - /** - * Get information about the files that are detailed in the database for - * the fields which have an upload method defined on them. - * - * @param string [$limitTable=null] Limit the data gathering to a single - * table only - * @param number [$id=null] Limit to a specific id - * @return array File information - * @private - */ - private function _fileData ( $limitTable=null, $id=null ) - { - $files = array(); - - // The fields in this instance - $this->_fileDataFields( $files, $this->_fields, $limitTable, $id ); - - // From joined tables - for ( $i=0 ; $i_join) ; $i++ ) { - $this->_fileDataFields( $files, $this->_join[$i]->fields(), $limitTable, $id ); - } - - return $files; - } - - - /** - * Common file get method for any array of fields - * @param array &$files File output array - * @param Field[] $fields Fields to get file information about - * @param string $limitTable Limit the data gathering to a single table - * only - * @private - */ - private function _fileDataFields ( &$files, $fields, $limitTable, $id=null ) - { - foreach ($fields as $field) { - $upload = $field->upload(); - - if ( $upload ) { - $table = $upload->table(); - - if ( ! $table ) { - continue; - } - - if ( $limitTable !== null && $table !== $limitTable ) { - continue; - } - - if ( isset( $files[ $table ] ) ) { - continue; - } - - $fileData = $upload->data( $this->_db, $id ); - - if ( $fileData !== null ) { - $files[ $table ] = $fileData; - } - } - } - } - - /** - * Run the file clean up - * - * @private - */ - private function _fileClean () - { - foreach ( $this->_fields as $field ) { - $upload = $field->upload(); - - if ( $upload ) { - $upload->dbCleanExec( $this, $field ); - } - } - - for ( $i=0 ; $i_join) ; $i++ ) { - foreach ( $this->_join[$i]->fields() as $field ) { - $upload = $field->upload(); - - if ( $upload ) { - $upload->dbCleanExec( $this, $field ); - } - } - } - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Server-side processing methods - */ - - /** - * When server-side processing is being used, modify the query with // the - * required extra conditions - * - * @param \DataTables\Database\Query $query Query instance to apply the SSP commands to - * @param array $http Parameters from HTTP request - * @return array Server-side processing information array - * @private - */ - private function _ssp_query ( $query, $http ) - { - if ( ! isset( $http['draw'] ) ) { - return array(); - } - - // Add the server-side processing conditions - $this->_ssp_limit( $query, $http ); - $this->_ssp_sort( $query, $http ); - $this->_ssp_filter( $query, $http ); - - // Get the number of rows in the result set - $ssp_set_count = $this->_db - ->query('select') - ->table( $this->_table ) - ->get( 'COUNT('.$this->_pkey[0].') as cnt' ); - $this->_get_where( $ssp_set_count ); - $this->_ssp_filter( $ssp_set_count, $http ); - $this->_perform_left_join( $ssp_set_count ); - $ssp_set_count = $ssp_set_count->exec()->fetch(); - - // Get the number of rows in the full set - $ssp_full_count = $this->_db - ->query('select') - ->table( $this->_table ) - ->get( 'COUNT('.$this->_pkey[0].') as cnt' ); - $this->_get_where( $ssp_full_count ); - if ( count( $this->_where ) ) { // only needed if there is a where condition - $this->_perform_left_join( $ssp_full_count ); - } - $ssp_full_count = $ssp_full_count->exec()->fetch(); - - return array( - "draw" => intval( $http['draw'] ), - "recordsTotal" => $ssp_full_count['cnt'], - "recordsFiltered" => $ssp_set_count['cnt'] - ); - } - - - /** - * Convert a column index to a database field name - used for server-side - * processing requests. - * @param array $http HTTP variables (i.e. GET or POST) - * @param int $index Index in the DataTables' submitted data - * @returns string DB field name - * @throws \Exception Unknown fields - * @private - */ - private function _ssp_field( $http, $index ) - { - $name = $http['columns'][$index]['data']; - $field = $this->_find_field( $name, 'name' ); - - if ( ! $field ) { - // Is it the primary key? - if ( $name === 'DT_RowId' ) { - return $this->_pkey[0]; - } - - throw new \Exception('Unknown field: '.$name .' (index '.$index.')'); - } - - return $field->dbField(); - } - - - /** - * Sorting requirements to a server-side processing query. - * @param \DataTables\Database\Query $query Query instance to apply sorting to - * @param array $http HTTP variables (i.e. GET or POST) - * @private - */ - private function _ssp_sort ( $query, $http ) - { - for ( $i=0 ; $iorder( - $this->_ssp_field( $http, $order['column'] ) .' '. - ($order['dir']==='asc' ? 'asc' : 'desc') - ); - } - } - - - /** - * Add DataTables' 'where' condition to a server-side processing query. This - * works for both global and individual column filtering. - * @param \DataTables\Database\Query $query Query instance to apply the WHERE conditions to - * @param array $http HTTP variables (i.e. GET or POST) - * @private - */ - private function _ssp_filter ( $query, $http ) - { - $that = $this; - - // Global filter - $fields = $this->_fields; - - // Global search, add a ( ... or ... ) set of filters for each column - // in the table (not the fields, just the columns submitted) - if ( $http['search']['value'] ) { - $query->where( function ($q) use (&$that, &$fields, $http) { - for ( $i=0 ; $i_ssp_field( $http, $i ); - - if ( $field ) { - $q->or_where( $field, '%'.$http['search']['value'].'%', 'like' ); - } - } - } - } ); - } - - // if ( $http['search']['value'] ) { - // $words = explode(" ", $http['search']['value']); - - // $query->where( function ($q) use (&$that, &$fields, $http, $words) { - // for ( $j=0, $jen=count($words) ; $j<$jen ; $j++ ) { - // if ( $words[$j] ) { - // $q->where_group( true ); - - // for ( $i=0, $ien=count($http['columns']) ; $i<$ien ; $i++ ) { - // if ( $http['columns'][$i]['searchable'] == 'true' ) { - // $field = $that->_ssp_field( $http, $i ); - - // $q->or_where( $field, $words[$j].'%', 'like' ); - // $q->or_where( $field, '% '.$words[$j].'%', 'like' ); - // } - // } - - // $q->where_group( false ); - // } - // } - // } ); - // } - - // Column filters - for ( $i=0, $ien=count($http['columns']) ; $i<$ien ; $i++ ) { - $column = $http['columns'][$i]; - $search = $column['search']['value']; - - if ( $search !== '' && $column['searchable'] == 'true' ) { - $query->where( $this->_ssp_field( $http, $i ), '%'.$search.'%', 'like' ); - } - } - } - - - /** - * Add a limit / offset to a server-side processing query - * @param \DataTables\Database\Query $query Query instance to apply the offset / limit to - * @param array $http HTTP variables (i.e. GET or POST) - * @private - */ - private function _ssp_limit ( $query, $http ) - { - if ( $http['length'] != -1 ) { // -1 is 'show all' in DataTables - $query - ->offset( $http['start'] ) - ->limit( $http['length'] ); - } - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Internal helper methods - */ - - /** - * Add left join commands for the instance to a query. - * - * @param \DataTables\Database\Query $query Query instance to apply the joins to - * @private - */ - private function _perform_left_join ( $query ) - { - if ( count($this->_leftJoin) ) { - for ( $i=0, $ien=count($this->_leftJoin) ; $i<$ien ; $i++ ) { - $join = $this->_leftJoin[$i]; - - $query->join( $join['table'], $join['field1'].' '.$join['operator'].' '.$join['field2'], 'LEFT' ); - } - } - } - - - /** - * Add local WHERE condition to query - * @param \DataTables\Database\Query $query Query instance to apply the WHERE conditions to - * @private - */ - private function _get_where ( $query ) - { - for ( $i=0 ; $i_where) ; $i++ ) { - if ( is_callable( $this->_where[$i] ) ) { - $this->_where[$i]( $query ); - } - else { - $query->where( - $this->_where[$i]['key'], - $this->_where[$i]['value'], - $this->_where[$i]['op'] - ); - } - } - } - - - /** - * Get a field instance from a known field name - * - * @param string $name Field name - * @param string $type Matching name type - * @return Field Field instance - * @private - */ - private function _find_field ( $name, $type ) - { - for ( $i=0, $ien=count($this->_fields) ; $i<$ien ; $i++ ) { - $field = $this->_fields[ $i ]; - - if ( $type === 'name' && $field->name() === $name ) { - return $field; - } - else if ( $type === 'db' && $field->dbField() === $name ) { - return $field; - } - } - - return null; - } - - - /** - * Insert or update a row for all main tables and left joined tables. - * - * @param int|string $id ID to use to condition the update. If null is - * given, the first query performed is an insert and the inserted id - * used as the value should there be any subsequent tables to operate - * on. Mote that for compound keys, this should be the "joined" value - * (i.e. a single string rather than an array). - * @return \DataTables\Database\Result Result from the query or null if no - * query performed. - * @private - */ - private function _insert_or_update ( $id, $values ) - { - // Loop over all tables in _table, doing the insert or update as needed - for ( $i=0, $ien=count( $this->_table ) ; $i<$ien ; $i++ ) { - $res = $this->_insert_or_update_table( - $this->_table[$i], - $values, - $id !== null ? - $this->pkeyToArray( $id, true ) : - null - ); - - // If we don't have an id yet, then the first insert will return - // the id we want - if ( $id === null ) { - $id = $res->insertId(); - } - } - - // And for the left join tables as well - for ( $i=0, $ien=count( $this->_leftJoin ) ; $i<$ien ; $i++ ) { - $join = $this->_leftJoin[$i]; - - // which side of the join refers to the parent table? - $joinTable = $this->_alias( $join['table'], 'alias' ); - $tablePart = $this->_part( $join['field1'] ); - - if ( $this->_part( $join['field1'], 'db' ) ) { - $tablePart = $this->_part( $join['field1'], 'db' ).'.'.$tablePart; - } - - if ( $tablePart === $joinTable ) { - $parentLink = $join['field2']; - $childLink = $join['field1']; - } - else { - $parentLink = $join['field1']; - $childLink = $join['field2']; - } - - if ( $parentLink === $this->_pkey[0] && count($this->_pkey) === 1 ) { - $whereVal = $id; - } - else { - // We need submitted information about the joined data to be - // submitted as well as the new value. We first check if the - // host field was submitted - $field = $this->_find_field( $parentLink, 'db' ); - - if ( ! $field || ! $field->apply( 'set', $values ) ) { - // If not, then check if the child id was submitted - $field = $this->_find_field( $childLink, 'db' ); - - // No data available, so we can't do anything - if ( ! $field || ! $field->apply( 'set', $values ) ) { - continue; - } - } - - $whereVal = $field->val('set', $values); - } - - $whereName = $this->_part( $childLink, 'field' ); - - $this->_insert_or_update_table( - $join['table'], - $values, - array( $whereName => $whereVal ) - ); - } - - return $id; - } - - - /** - * Insert or update a row in a single database table, based on the data - * given and the fields configured for the instance. - * - * The function will find the fields which are required for this specific - * table, based on the names of fields and use only the appropriate data for - * this table. Therefore the full submitted data set can be passed in. - * - * @param string $table Database table name to use (can include an alias) - * @param array $where Update condition - * @return \DataTables\Database\Result Result from the query or null if no query - * performed. - * @throws \Exception Where set error - * @private - */ - private function _insert_or_update_table ( $table, $values, $where=null ) - { - $set = array(); - $action = ($where === null) ? 'create' : 'edit'; - $tableAlias = $this->_alias( $table, 'alias' ); - - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - $tablePart = $this->_part( $field->dbField() ); - - if ( $this->_part( $field->dbField(), 'db' ) ) { - $tablePart = $this->_part( $field->dbField(), 'db' ).'.'.$tablePart; - } - - // Does this field apply to this table (only check when a join is - // being used) - if ( count($this->_leftJoin) && $tablePart !== $tableAlias ) { - continue; - } - - // Check if this field should be set, based on options and - // submitted data - if ( ! $field->apply( $action, $values ) ) { - continue; - } - - // Some db's (specifically postgres) don't like having the table - // name prefixing the column name. Todo: it might be nicer to have - // the db layer abstract this out? - $fieldPart = $this->_part( $field->dbField(), 'field' ); - $set[ $fieldPart ] = $field->val( 'set', $values ); - } - - // Add where fields if setting where values and required for this - // table - // Note that `whereSet` is now deprecated - if ( $this->_whereSet ) { - for ( $j=0, $jen=count($this->_where) ; $j<$jen ; $j++ ) { - $cond = $this->_where[$j]; - - if ( ! is_callable( $cond ) ) { - // Make sure the value wasn't in the submitted data set, - // otherwise we would be overwriting it - if ( ! isset( $set[ $cond['key'] ] ) ) - { - $whereTablePart = $this->_part( $cond['key'], 'table' ); - - // No table part on the where condition to match against - // or table operating on matches table part from cond. - if ( ! $whereTablePart || $tableAlias == $whereTablePart ) { - $set[ $cond['key'] ] = $cond['value']; - } - } - else { - throw new \Exception( 'Where condition used as a setter, '. - 'but value submitted for field: '.$cond['key'] - ); - } - } - } - } - - // If nothing to do, then do nothing! - if ( ! count( $set ) ) { - return null; - } - - // Insert or update - if ( $action === 'create' ) { - return $this->_db->insert( $table, $set, $this->_pkey ); - } - else { - return $this->_db->push( $table, $set, $where, $this->_pkey ); - } - } - - - /** - * Delete one or more rows from the database for an individual table - * - * @param string $table Database table name to use - * @param array $ids Array of ids to remove - * @param string $pkey Database column name to match the ids on for the - * delete condition. If not given the instance's base primary key is - * used. - * @private - */ - private function _remove_table ( $table, $ids, $pkey=null ) - { - if ( $pkey === null ) { - $pkey = $this->_pkey; - } - - // Check there is a field which has a set option for this table - $count = 0; - - foreach ($this->_fields as $field) { - if ( strpos( $field->dbField(), '.') === false || ( - $this->_part( $field->dbField(), 'table' ) === $table && - $field->set() !== Field::SET_NONE - ) - ) { - $count++; - } - } - - if ( $count > 0 ) { - $q = $this->_db - ->query( 'delete' ) - ->table( $table ); - - for ( $i=0, $ien=count($ids) ; $i<$ien ; $i++ ) { - $cond = $this->pkeyToArray( $ids[$i], true ); - - $q->or_where( function ($q2) use ($cond) { - $q2->where( $cond ); - } ); - } - - $q->exec(); - } - } - - - /** - * Check the validity of the set options if we are doing a join, since - * there are some conditions for this state. Will throw an error if not - * valid. - * - * @private - */ - private function _prepJoin () - { - if ( count( $this->_leftJoin ) === 0 ) { - return; - } - - // Check if the primary key has a table identifier - if not - add one - for ( $i=0, $ien=count($this->_pkey) ; $i<$ien ; $i++ ) { - $val = $this->_pkey[$i]; - - if ( strpos( $val, '.' ) === false ) { - $this->_pkey[$i] = $this->_alias( $this->_table[0], 'alias' ).'.'.$val; - } - } - - // Check that all fields have a table selector, otherwise, we'd need to - // know the structure of the tables, to know which fields belong in - // which. This extra requirement on the fields removes that - for ( $i=0, $ien=count($this->_fields) ; $i<$ien ; $i++ ) { - $field = $this->_fields[$i]; - $name = $field->dbField(); - - if ( strpos( $name, '.' ) === false ) { - throw new \Exception( 'Table part of the field "'.$name.'" was not found. '. - 'In Editor instances that use a join, all fields must have the '. - 'database table set explicitly.' - ); - } - } - } - - - /** - * Get one side or the other of an aliased SQL field name. - * - * @param string $name SQL field - * @param string $type Which part to get: `alias` (default) or `orig`. - * @returns string Alias - * @private - */ - private function _alias ( $name, $type='alias' ) - { - if ( stripos( $name, ' as ' ) !== false ) { - $a = preg_split( '/ as /i', $name ); - return $type === 'alias' ? - $a[1] : - $a[0]; - } - - return $name; - } - - - /** - * Get part of an SQL field definition regardless of how deeply defined it - * is - * - * @param string $name SQL field - * @param string $type Which part to get: `table` (default) or `db` or - * `column` - * @return string Part name - * @private - */ - private function _part ( $name, $type='table' ) - { - $db = null; - $table = null; - $column = null; - - if ( strpos( $name, '.' ) !== false ) { - $a = explode( '.', $name ); - - if ( count($a) === 3 ) { - $db = $a[0]; - $table = $a[1]; - $column = $a[2]; - } - else if ( count($a) === 2 ) { - $table = $a[0]; - $column = $a[1]; - } - } - else { - $column = $name; - } - - if ( $type === 'db' ) { - return $db; - } - else if ( $type === 'table' ) { - return $table; - } - return $column; - } - - - /** - * Trigger an event - * - * @private - */ - private function _trigger () - { - $out = null; - $args = func_get_args(); - $eventName = array_shift( $args ); - array_unshift( $args, $this ); - - if ( ! isset( $this->_events[ $eventName ] ) ) { - return; - } - - $events = $this->_events[ $eventName ]; - - for ( $i=0, $ien=count($events) ; $i<$ien ; $i++ ) { - $res = call_user_func_array( $events[$i], $args ); - - if ( $res !== null ) { - $out = $res; - } - } - - return $out; - } - - - /** - * Merge a primary key value with an updated data source. - * - * @param string $pkeyVal Old primary key value to merge into - * @param array $row Data source for update - * @return string Merged value - */ - private function _pkey_submit_merge ( $pkeyVal, $row ) - { - $pkey = $this->_pkey; - $arr = $this->pkeyToArray( $pkeyVal, true ); - - for ( $i=0, $ien=count($pkey) ; $i<$ien ; $i++ ) { - $column = $pkey[ $i ]; - $field = $this->_find_field( $column, 'db' ); - - if ( $field && $field->apply( 'edit', $row ) ) { - $arr[ $column ] = $field->val( 'set', $row ); - } - } - - return $this->pkeyToValue( $arr, true ); - } - - - /** - * Validate that all primary key fields have values for create. - * - * @param array $row Row's data - * @return boolean `true` if valid, `false` otherwise - */ - private function _pkey_validate_insert ( $row ) - { - $pkey = $this->_pkey; - - if ( count( $pkey ) === 1 ) { - return true; - } - - for ( $i=0, $ien=count($pkey) ; $i<$ien ; $i++ ) { - $column = $pkey[ $i ]; - $field = $this->_find_field( $column, 'db' ); - - if ( ! $field || ! $field->apply("set", $row) ) { - throw new Exception( "When inserting into a compound key table, ". - "all fields that are part of the compound key must be ". - "submitted with a specific value.", 1 - ); - } - } - - return true; - } - - - /** - * Create the separator value for a compound primary key. - * - * @return string Calculated separator - */ - private function _pkey_separator () - { - $str = implode(',', $this->_pkey); - - return hash( 'crc32', $str ); - } -} - diff --git a/php/Editor/Field.php b/php/Editor/Field.php deleted file mode 100644 index e3578d3..0000000 --- a/php/Editor/Field.php +++ /dev/null @@ -1,813 +0,0 @@ - - * Field::inst( 'city' ) - * - * - * @example - * Get a column with the name "first_name" - when edited a value must - * be given due to the "required" validation from the {@link Validate} class. - * - * Field::inst( 'first_name' )->validator( 'Validate::required' ) - * - * - * @example - * Working with a date field, which is validated, and also has *get* and - * *set* formatters. - * - * Field::inst( 'registered_date' ) - * ->validator( 'Validate::dateFormat', 'D, d M y' ) - * ->getFormatter( 'Format::date_sql_to_format', 'D, d M y' ) - * ->setFormatter( 'Format::date_format_to_sql', 'D, d M y' ) - * - * - * @example - * Using an alias in the first parameter - * - * Field::inst( 'name.first as first_name' ) - * - */ -class Field extends DataTables\Ext { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Statics - */ - - /** Set option flag (`set()`) - do not set data */ - const SET_NONE = 'none'; - - /** Set option flag (`set()`) - write to database on both create and edit */ - const SET_BOTH = 'both'; - - /** Set option flag (`set()`) - write to database only on create */ - const SET_CREATE = 'create'; - - /** Set option flag (`set()`) - write to database only on edit */ - const SET_EDIT = 'edit'; - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Constructor - */ - - /** - * Field instance constructor. - * @param string $dbField Name of the database column - * @param string $name Name to use in the JSON output from Editor and the - * HTTP submit from the client-side when editing. If not given then the - * $dbField name is used. - */ - function __construct( $dbField=null, $name=null ) - { - if ( $dbField !== null && $name === null ) { - // Allow just a single parameter to be passed - each can be - // overridden if needed later using the API. - $this->name( $dbField ); - $this->dbField( $dbField ); - } - else { - $this->name( $name ); - $this->dbField( $dbField ); - } - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private parameters - */ - - /** @var string */ - private $_dbField = null; - - /** @var boolean */ - private $_get = true; - - /** @var mixed */ - private $_getFormatter = null; - - /** @var mixed */ - private $_getFormatterOpts = null; - - /** @var mixed */ - private $_getValue = null; - - /** @var Options */ - private $_opts = null; - - /** @var callable */ - private $_optsFn = null; - - /** @var string */ - private $_name = null; - - /** @var string */ - private $_set = Field::SET_BOTH; - - /** @var mixed */ - private $_setFormatter = null; - - /** @var mixed */ - private $_setFormatterOpts = null; - - /** @var mixed */ - private $_setValue = null; - - /** @var mixed */ - private $_validator = array(); - - /** @var Upload */ - private $_upload = null; - - /** @var callable */ - private $_xss = null; - - /** @var boolean */ - private $_xssFormat = true; - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - - /** - * Get / set the DB field name. - * - * Note that when used as a setter, an alias can be given for the field - * using the SQL `as` keyword - for example: `firstName as name`. In this - * situation the dbField is set to the field name before the `as`, and the - * field's name (`name()`) is set to the name after the ` as `. - * - * As a result of this, the following constructs have identical - * functionality: - * - * Field::inst( 'firstName as name' ); - * Field::inst( 'firstName', 'name' ); - * - * @param string $_ Value to set if using as a setter. - * @return string|self The name of the db field if no parameter is given, - * or self if used as a setter. - */ - public function dbField ( $_=null ) - { - if ( $_ === null ) { - return $this->_dbField; - } - - if ( stripos( $_, ' as ' ) ) { - $a = preg_split( '/ as /i', $_ ); - $this->_dbField = trim( $a[0] ); - $this->_name = trim( $a[1] ); - } - else { - $this->_dbField = $_; - } - - return $this; - } - - - /** - * Get / set the 'get' property of the field. - * - * A field can be marked as write only when setting the get property to false - * here. - * @param boolean $_ Value to set if using as a setter. - * @return boolean|self The get property if no parameter is given, or self - * if used as a setter. - */ - public function get ( $_=null ) - { - return $this->_getSet( $this->_get, $_ ); - } - - - /** - * Get formatter for the field's data. - * - * When the data has been retrieved from the server, it can be passed through - * a formatter here, which will manipulate (format) the data as required. This - * can be useful when, for example, working with dates and a particular format - * is required on the client-side. - * - * Editor has a number of formatters available with the {@link Format} class - * which can be used directly with this method. - * @param callable|string $_ Value to set if using as a setter. Can be given as - * a closure function or a string with a reference to a function that will - * be called with call_user_func(). - * @param mixed $opts Variable that is passed through to the get formatting - * function - can be useful for passing through extra information such as - * date formatting string, or a required flag. The actual options available - * depend upon the formatter used. - * @return callable|string|self The get formatter if no parameter is given, or - * self if used as a setter. - */ - public function getFormatter ( $_=null, $opts=null ) - { - if ( $opts !== null ) { - $this->_getFormatterOpts = $opts; - } - return $this->_getSet( $this->_getFormatter, $_ ); - } - - - /** - * Get / set a get value. If given, then this value is used to send to the - * client-side, regardless of what value is held by the database. - * - * @param callable|string|number $_ Value to set, or no value to use as a - * getter - * @return callable|string|self Value if used as a getter, or self if used - * as a setter. - */ - public function getValue ( $_=null ) - { - return $this->_getSet( $this->_getValue, $_ ); - } - - - /** - * Get / set the 'name' property of the field. - * - * The name is typically the same as the dbField name, since it makes things - * less confusing(!), but it is possible to set a different name for the data - * which is used in the JSON returned to DataTables in a 'get' operation and - * the field name used in a 'set' operation. - * @param string $_ Value to set if using as a setter. - * @return string|self The name property if no parameter is given, or self - * if used as a setter. - */ - public function name ( $_=null ) - { - return $this->_getSet( $this->_name, $_ ); - } - - - /** - * Get a list of values that can be used for the options list in radio, - * select and checkbox inputs from the database for this field. - * - * Note that this is for simple 'label / value' pairs only. For more complex - * data, including pairs that require joins and where conditions, use a - * closure to provide a query - * - * @param string|callable $table Database table name to use to get the - * paired data from, or a closure function if providing a method - * @param string $value Table column name that contains the pair's - * value. Not used if the first parameter is given as a closure - * @param string $label Table column name that contains the pair's - * label. Not used if the first parameter is given as a closure - * @param callable $condition Function that will add `where` - * conditions to the query - * @param callable $format Function will render each label - * @param string $order SQL ordering - * @return Field Self for chaining - */ - public function options ( $table=null, $value=null, $label=null, $condition=null, $format=null, $order=null ) - { - if ( $table === null ) { - return $this->_opts; - } - - // Overloads for backwards compatibility - if ( is_a( $table, '\DataTables\Editor\Options' ) ) { - // Options class - $this->_optsFn = null; - $this->_opts = $table; - } - else if ( is_callable($table) && is_object($table) ) { - // Function - $this->_opts = null; - $this->_optsFn = $table; - } - else { - $this->_optsFn = null; - $this->_opts = Options::inst() - ->table( $table ) - ->value( $value ) - ->label( $label ); - - if ( $condition ) { - $this->_opts->where( $condition ); - } - - if ( $format ) { - $this->_opts->render( $format ); - } - - if ( $order ) { - $this->_opts->order( $order ); - } - } - - return $this; - } - - - /** - * Get / set the 'set' property of the field. - * - * A field can be marked as read only using this option, to be set only - * during an create or edit action or to be set during both actions. This - * provides the ability to have fields that are only set when a new row is - * created (for example a "created" time stamp). - * @param string|boolean $_ Value to set when the method is being used as a - * setter (leave as undefined to use as a getter). This can take the - * value of: - * - * * `true` - Same as `Field::SET_BOTH` - * * `false` - Same as `Field::SET_NONE` - * * `Field::SET_BOTH` - Set the database value on both create and edit commands - * * `Field::SET_NONE` - Never set the database value - * * `Field::SET_CREATE` - Set the database value only on create - * * `Field::SET_EDIT` - Set the database value only on edit - * @return string|self The set property if no parameter is given, or self - * if used as a setter. - */ - public function set ( $_=null ) - { - if ( $_ === true ) { - $_ = Field::SET_BOTH; - } - else if ( $_ === false ) { - $_ = Field::SET_NONE; - } - - return $this->_getSet( $this->_set, $_ ); - } - - - /** - * Set formatter for the field's data. - * - * When the data has been retrieved from the server, it can be passed through - * a formatter here, which will manipulate (format) the data as required. This - * can be useful when, for example, working with dates and a particular format - * is required on the client-side. - * - * Editor has a number of formatters available with the {@link Format} class - * which can be used directly with this method. - * @param callable|string $_ Value to set if using as a setter. Can be given as - * a closure function or a string with a reference to a function that will - * be called with call_user_func(). - * @param mixed $opts Variable that is passed through to the get formatting - * function - can be useful for passing through extra information such as - * date formatting string, or a required flag. The actual options available - * depend upon the formatter used. - * @return callable|string|self The set formatter if no parameter is given, or - * self if used as a setter. - */ - public function setFormatter ( $_=null, $opts=null ) - { - if ( $opts !== null ) { - $this->_setFormatterOpts = $opts; - } - return $this->_getSet( $this->_setFormatter, $_ ); - } - - - /** - * Get / set a set value. If given, then this value is used to write to the - * database regardless of what data is sent from the client-side. - * - * @param callable|string|number $_ Value to set, or no value to use as a - * getter - * @return callable|string|self Value if used as a getter, or self if used - * as a setter. - */ - public function setValue ( $_=null ) - { - return $this->_getSet( $this->_setValue, $_ ); - } - - - /** - * Get / set the upload class for this field. - * @param Upload $_ Upload class if used as a setter - * @return Upload|self Value if used as a getter, or self if used - * as a setter. - */ - public function upload ( $_=null ) - { - return $this->_getSet( $this->_upload, $_ ); - } - - - /** - * Get / set the 'validator' of the field. - * - * The validator can be used to check if any abstract piece of data is valid - * or not according to the given rules of the validation function used. - * - * Multiple validation options can be applied to a field instance by calling - * this method multiple times. For example, it would be possible to have a - * 'required' validation and a 'maxLength' validation with multiple calls. - * - * Editor has a number of validation available with the {@link Validate} class - * which can be used directly with this method. - * @param callable|string $_ Value to set if using as the validation method. - * Can be given as a closure function or a string with a reference to a - * function that will be called with call_user_func(). - * @param mixed $opts Variable that is passed through to the validation - * function - can be useful for passing through extra information such as - * date formatting string, or a required flag. The actual options available - * depend upon the validation function used. - * @return callable|string|self The validation method if no parameter is given, - * or self if used as a setter. - */ - public function validator ( $_=null, $opts=null ) - { - if ( $_ === null ) { - return $this->_validator; - } - else { - $this->_validator[] = array( - "func" => $_, - "opts" => $opts - ); - } - - return $this; - } - - - /** - * Set a formatting method that will be used for XSS checking / removal. - * This should be a function that takes a single argument (the value to be - * cleaned) and returns the cleaned value. - * - * Editor will use HtmLawed by default for this operation, which is built - * into the software and no additional configuration is required, but a - * custom function can be used if you wish to use a different formatter such - * as HTMLPurifier. - * - * If you wish to disable this option (which you would only do if you are - * absolutely confident that your validation will pick up on any XSS inputs) - * simply provide a closure function that returns the value given to the - * function. This is _not_ recommended. - * - * @param callable|false $xssFormatter XSS cleaner function, use `false` or - * `null` to disable XSS cleaning. - * @return Field Self for chaining. - */ - public function xss ( $xssFormatter ) - { - if ( $xssFormatter === true || $xssFormatter === false || $xssFormatter === null ) { - $this->_xssFormat = $xssFormatter; - } - else { - $this->_xss = $xssFormatter; - } - - return $this; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Internal methods - * Used by the Editor class and not generally for public use - */ - - /** - * Check to see if a field should be used for a particular action (get or set). - * - * Called by the Editor / Join class instances - not expected for general - * consumption - internal. - * @param string $action Direction that the data is travelling - 'get' is - * reading DB data, `create` and `edit` for writing to the DB - * @param array $data Data submitted from the client-side when setting. - * @return boolean true if the field should be used in the get / set. - * @internal - */ - public function apply ( $action, $data=null ) - { - if ( $action === 'get' ) { - // Get action - can we get this field - return $this->_get; - } - else { - // Note that validation must be done on input data before we get here - - // Create or edit action, are we configured to use this field - if ( $action === 'create' && - ($this->_set === Field::SET_NONE || $this->_set === Field::SET_EDIT) - ) { - return false; - } - else if ( $action === 'edit' && - ($this->_set === Field::SET_NONE || $this->_set === Field::SET_CREATE) - ) { - return false; - } - - // Check it was in the submitted data. If not, then not required - // (validation would have failed if it was) and therefore we don't - // set it. Check for a value as well, as it can format data from - // some other source - if ( $this->_setValue === null && ! $this->_inData( $this->name(), $data ) ) { - return false; - } - - // In the data set, so use it - return true; - } - } - - - /** - * Execute the ipOpts to get the list of options to return to the client- - * side - * - * @param \DataTables\Database $db Database instance - * @return Array Array of value / label options for the list - * @internal - */ - public function optionsExec ( $db ) - { - if ( $this->_optsFn ) { - $fn = $this->_optsFn; - return $fn(); - } - else if ( $this->_opts ) { - return $this->_opts->exec( $db ); - } - - return false; - } - - - /** - * Get the value of the field, taking into account if it is coming from the - * DB or from a POST. If formatting has been specified for this field, it - * will be applied here. - * - * Called by the Editor / Join class instances - not expected for general - * consumption - internal. - * @param string $direction Direction that the data is travelling - 'get' is - * reading data, and 'set' is writing it to the DB. - * @param array $data Data submitted from the client-side when setting or the - * data for the row when getting data from the DB. - * @return string Value for the field - * @internal - */ - public function val ( $direction, $data ) - { - if ( $direction === 'get' ) { - if ( $this->_getValue !== null ) { - $val = $this->_getAssignedValue( $this->_getValue ); - } - else { - // Getting data, so the db field name - $val = isset( $data[ $this->_dbField ] ) ? - $data[ $this->_dbField ] : - null; - } - - return $this->_format( - $val, $data, $this->_getFormatter, $this->_getFormatterOpts - ); - } - else { - // Setting data, so using from the payload (POST usually) and thus - // use the 'name' - $val = $this->_setValue !== null ? - $this->_getAssignedValue( $this->_setValue ) : - $this->_readProp( $this->name(), $data ); - - // XSS removal / checker - if ( $this->_xssFormat ) { - $val = $this->xssSafety( $val ); - } - - return $this->_format( - $val, $data, $this->_setFormatter, $this->_setFormatterOpts - ); - } - } - - - /** - * Check the validity of the field based on the data submitted. Note that - * this validation is performed on the wire data - i.e. that which is - * submitted, before any setFormatter is run - * - * Called by the Editor / Join class instances - not expected for general - * consumption - internal. - * - * @param array $data Data submitted from the client-side - * @param Editor $editor Editor instance - * @param * $id Row id that is being validated - * @return boolean|string `true` if valid, string with error message if not - * @internal - */ - public function validate ( $data, $editor, $id=null ) - { - // Three cases for the validator - closure, string or null - if ( ! count( $this->_validator ) ) { - return true; - } - - $val = $this->_readProp( $this->name(), $data ); - - for ( $i=0, $ien=count( $this->_validator ) ; $i<$ien ; $i++ ) { - $validator = $this->_validator[$i]; - $processData = $editor->inData(); - $instances = array( - 'action' => $processData['action'], - 'id' => $id, - 'field' => $this, - 'editor' => $editor, - 'db' => $editor->db() - ); - - if ( is_string( $validator['func'] ) ) { - // Don't require the Editor namespace if DataTables validator is given as a string - if ( strpos($validator['func'], "Validate::") === 0 ) { - $res = call_user_func( "\\DataTables\\Editor\\".$validator['func'], $val, $data, $validator['opts'], $instances ); - } - else { - $res = call_user_func( $validator['func'], $val, $data, $validator['opts'], $instances ); - } - } - else { - $func = $validator['func']; - $res = $func( $val, $data, $this, $instances ); - } - - // Check if there was a validation error and if so, return it - if ( $res !== true ) { - return $res; - } - } - - // Validation methods all run, must be valid - return true; - } - - - /** - * Write the value for this field to the output array for a read operation - * - * @param array $out Row output data (to the JSON) - * @param mixed $srcData Row input data (raw, from the database) - * @internal - */ - public function write( &$out, $srcData ) - { - $this->_writeProp( $out, $this->name(), $this->val('get', $srcData) ); - } - - - /** - * Perform XSS prevention on an input. - * - * @param * $val Value to be escaped - * @return string Safe value - */ - public function xssSafety ( $val ) { - $xss = $this->_xss; - - if ( is_array( $val ) ) { - $res = array(); - - foreach ( $val as $individual ) { - $res[] = $xss ? - $xss( $individual ) : - DataTables\Vendor\Htmlaw::filter( $individual ); - } - - return $res; - } - - return $xss ? - $xss( $val ) : - DataTables\Vendor\Htmlaw::filter( $val ); - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private methods - */ - - /** - * Apply a formatter to data. The caller will decide what formatter to apply - * (get or set) - * - * @param mixed $val Value to be formatted - * @param mixed $data Full row data - * @param callable $formatter Formatting function to be called - * @param array $opts Array of options to be passed to the formatter - * @return mixed Formatted value - */ - private function _format( $val, $data, $formatter, $opts ) - { - // Three cases for the formatter - closure, string or null - if ( $formatter ) { - if ( is_string( $formatter ) ) { - // Don't require the Editor namespace if DataTables validator is given as a string - if ( strpos($formatter, "Format::") === 0 ) { - // Editor formatter - return call_user_func( - "\\DataTables\\Editor\\".$formatter, - $val, - $data, - $opts - ); - } - - // User function (string identifier) - return call_user_func( $formatter, $val, $data, $opts ); - } - - // Closure - return $formatter( $val, $data, $opts ); - } - return $val; - } - - /** - * Get the value from `_[gs]etValue` - taking into account if it is callable - * function or not - * - * @param mixed $val Value to be evaluated - * @return mixed Value assigned, or returned from the function - */ - private function _getAssignedValue ( $val ) - { - return is_callable($val) && is_object($val) ? - $val() : - $val; - } - - /** - * Check is a parameter is in the submitted data set. This is functionally - * the same as the `_readProp()` method, but in this case a binary value - * is required to indicate if the value is present or not. - * - * @param string $name Javascript dotted object name to write to - * @param array $data Data source array to read from - * @return boolean `true` if present, `false` otherwise - * @private - */ - private function _inData ( $name, $data ) - { - if ( strpos($name, '.') === false ) { - return isset( $data[ $name ] ) ? - true : - false; - } - - $names = explode( '.', $name ); - $inner = $data; - - for ( $i=0 ; $i - * Join::inst( 'access', 'array' ) - * ->link( 'users.id', 'user_access.user_id' ) - * ->link( 'access.id', 'user_access.access_id' ) - * ->field( - * Field::inst( 'id' )->validator( 'Validate::required' ), - * Field::inst( 'name' ) - * ) - * - * - * @example - * Single row join - here we join the parent table with a single row in - * the child table, without an intermediate link table. In this case the - * child table is called *extra* and the two fields give the columns that - * Editor will read from that table. - * - * Join::inst( 'extra', 'object' ) - * ->link( 'user.id', 'extra.user_id' ) - * ->field( - * Field::inst( 'comments' ), - * Field::inst( 'review' ) - * ) - * - */ -class Join extends DataTables\Ext { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Constructor - */ - - /** - * Join instance constructor. - * @param string $table Table name to get the joined data from. - * @param string $type Work with a single result ('object') or an array of - * results ('array') for the join. - */ - function __construct( $table=null, $type='object' ) - { - $this->table( $table ); - $this->type( $type ); - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private properties - */ - - /** @var DataTables\Editor\Field[] */ - private $_fields = array(); - - /** @var array */ - private $_join = array( - "parent" => null, - "child" => null, - "table" => null - ); - - /** @var string */ - private $_table = null; - - /** @var string */ - private $_type = null; - - /** @var string */ - private $_name = null; - - /** @var boolean */ - private $_get = true; - - /** @var boolean */ - private $_set = true; - - /** @var string */ - private $_aliasParentTable = null; - - /** @var array */ - private $_where = array(); - - /** @var boolean */ - private $_whereSet = false; - - /** @var array */ - private $_links = array(); - - /** @var string */ - private $_customOrder = null; - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - /** - * Get / set parent table alias. - * - * When working with a self referencing table (i.e. a column in the table contains - * a primary key value from its own table) it can be useful to set an alias on the - * parent table's name, allowing a self referencing Join. For example: - * - * SELECT p2.publisher - * FROM publisher as p2 - * JOIN publisher on (publisher.idPublisher = p2.idPublisher) - * - * Where, in this case, `publisher` is the table that is used by the Editor instance, - * and you want to use `Join` to link back to the table (resolving a name for example). - * This method allows that alias to be set. Fields can then use standard SQL notation - * to select a field, for example `p2.publisher` or `publisher.publisher`. - * @param string $_ Table alias to use - * @return string|self Table alias set (which is null by default), or self if used as - * a setter. - */ - public function aliasParentTable ( $_=null ) - { - return $this->_getSet( $this->_aliasParentTable, $_ ); - } - - - /** - * Get / set field instances. - * - * The list of fields designates which columns in the table that will be read - * from the joined table. - * @param Field $_... Instances of the {@link Field} class, given as a single - * instance of {@link Field}, an array of {@link Field} instances, or multiple - * {@link Field} instance parameters for the function. - * @return Field[]|self Array of fields, or self if used as a setter. - * @see {@link Field} for field documentation. - */ - public function field ( $_=null ) - { - if ( $_ !== null && !is_array($_) ) { - $_ = func_get_args(); - } - return $this->_getSet( $this->_fields, $_, true ); - } - - - /** - * Get / set field instances. - * - * An alias of {@link field}, for convenience. - * @param Field $_... Instances of the {@link Field} class, given as a single - * instance of {@link Field}, an array of {@link Field} instances, or multiple - * {@link Field} instance parameters for the function. - * @return Field[]|self Array of fields, or self if used as a setter. - * @see {@link Field} for field documentation. - */ - public function fields ( $_=null ) - { - if ( $_ !== null && !is_array($_) ) { - $_ = func_get_args(); - } - return $this->_getSet( $this->_fields, $_, true ); - } - - - /** - * Get / set get attribute. - * - * When set to false no read operations will occur on the join tables. - * @param boolean $_ Value - * @return boolean|self Name - */ - public function get ( $_=null ) - { - return $this->_getSet( $this->_get, $_ ); - } - - - /** - * Get / set join properties. - * - * Define how the SQL will be performed, on what columns. There are - * basically two types of join that are supported by Editor here, a direct - * foreign key reference in the join table to the parent table's primary - * key, or a link table that contains just primary keys for the parent and - * child tables (this approach is usually used with a {@link type} of - * 'array' since you can often have multiple links between the two tables, - * while a direct foreign key reference will typically use a type of - * 'object' (i.e. a single entry). - * - * @param string|string[] $parent Parent table's primary key names. If used - * with a link table (i.e. third parameter to this method is given, then - * an array should be used, with the first element being the pkey's name - * in the parent table, and the second element being the key's name in - * the link table. - * @param string|string[] $child Child table's primary key names. If used - * with a link table (i.e. third parameter to this method is given, then - * an array should be used, with the first element being the pkey's name - * in the child table, and the second element being the key's name in the - * link table. - * @param string $table Join table name, if using a link table - * @returns Join This for chaining - * @deprecated 1.5 Please use the {@link link} method rather than this - * method now. - */ - public function join ( $parent=null, $child=null, $table=null ) - { - if ( $parent === null && $child === null ) { - return $this->_join; - } - - $this->_join['parent'] = $parent; - $this->_join['child'] = $child; - $this->_join['table'] = $table; - return $this; - } - - - /** - * Create a join link between two tables. The order of the fields does not - * matter, but each field must contain the table name as well as the field - * name. - * - * This method can be called a maximum of two times for an Mjoin instance: - * - * * First time, creates a link between the Editor host table and a join - * table - * * Second time creates the links required for a link table. - * - * Please refer to the Editor Mjoin documentation for further details: - * https://editor.datatables.net/manual/php - * - * @param string $field1 Table and field name - * @param string $field2 Table and field name - * @return Join Self for chaining - * @throws \Exception Link limitations - */ - public function link ( $field1, $field2 ) - { - if ( strpos($field1, '.') === false || strpos($field2, '.') === false ) { - throw new \Exception("Link fields must contain both the table name and the column name"); - } - - if ( count( $this->_links ) >= 4 ) { - throw new \Exception("Link method cannot be called more than twice for a single instance"); - } - - $this->_links[] = $field1; - $this->_links[] = $field2; - - return $this; - } - - - /** - * Specify the property that the data will be sorted by. - * - * @param string $order SQL column name to order the data by - * @return Join Self for chaining - */ - public function order ( $_=null ) - { - // How this works is by setting the SQL order by clause, and since the - // join that is done in PHP is always done sequentially, the order is - // retained. - return $this->_getSet( $this->_customOrder, $_ ); - } - - - /** - * Get / set name. - * - * The `name` of the Join is the JSON property key that is used when - * 'getting' the data, and the HTTP property key (in a JSON style) when - * 'setting' data. Typically the name of the db table will be used here, - * but this method allows that to be overridden. - * @param string $_ Field name - * @return String|self Name - */ - public function name ( $_=null ) - { - return $this->_getSet( $this->_name, $_ ); - } - - - /** - * Get / set set attribute. - * - * When set to false no write operations will occur on the join tables. - * This can be useful when you want to display information which is joined, - * but want to only perform write operations on the parent table. - * @param boolean $_ Value - * @return boolean|self Name - */ - public function set ( $_=null ) - { - return $this->_getSet( $this->_set, $_ ); - } - - - /** - * Get / set join table name. - * - * Please note that this will also set the {@link name} used by the Join - * as well. This is for convenience as the JSON output / HTTP input will - * typically use the same name as the database name. If you want to set a - * custom name, the {@link name} method must be called ***after*** this one. - * @param string $_ Name of the table to read the join data from - * @return String|self Name of the join table, or self if used as a setter. - */ - public function table ( $_=null ) - { - if ( $_ !== null ) { - $this->_name = $_; - } - return $this->_getSet( $this->_table, $_ ); - } - - - /** - * Get / set the join type. - * - * The join type allows the data that is returned from the join to be given - * as an array (i.e. working with multiple possibly results for each record from - * the parent table), or as an object (i.e. working which one and only one result - * for each record form the parent table). - * @param string $_ Join type ('object') or an array of - * results ('array') for the join. - * @return String|self Join type, or self if used as a setter. - */ - public function type ( $_=null ) - { - return $this->_getSet( $this->_type, $_ ); - } - - - /** - * Where condition to add to the query used to get data from the database. - * Note that this is applied to the child table. - * - * Can be used in two different ways: - * - * * Simple case: `where( field, value, operator )` - * * Complex: `where( fn )` - * - * @param string|callable $key Single field name or a closure function - * @param string|string[] $value Single field value, or an array of values. - * @param string $op Condition operator: <, >, = etc - * @return string[]|self Where condition array, or self if used as a setter. - */ - public function where ( $key=null, $value=null, $op='=' ) - { - if ( $key === null ) { - return $this->_where; - } - - if ( is_callable($key) && is_object($key) ) { - $this->_where[] = $key; - } - else { - $this->_where[] = array( - "key" => $key, - "value" => $value, - "op" => $op - ); - } - - return $this; - } - - - /** - * Get / set if the WHERE conditions should be included in the create and - * edit actions. - * - * This means that the fields which have been used as part of the 'get' - * WHERE condition (using the `where()` method) will be set as the values - * given. - * - * This is default false (i.e. they are not included). - * - * @param boolean $_ Include (`true`), or not (`false`) - * @return boolean Current value - */ - public function whereSet ( $_=null ) - { - return $this->_getSet( $this->_whereSet, $_ ); - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Internal methods - */ - - /** - * Get data - * @param Editor $editor Host Editor instance - * @param string[] $data Data from the parent table's get and were we need - * to add out output. - * @param array $options options array for fields - * @internal - */ - public function data( $editor, &$data, &$options ) - { - if ( ! $this->_get ) { - return; - } - - $this->_prep( $editor ); - $db = $editor->db(); - $dteTable = $editor->table(); - $pkey = $editor->pkey(); - $idPrefix = $editor->idPrefix(); - - $dteTable = $dteTable[0]; - $dteTableLocal = $this->_aliasParentTable ? // Can be aliased to allow a self join - $this->_aliasParentTable : - $dteTable; - $joinField = isset($this->_join['table']) ? - $this->_join['parent'][0] : - $this->_join['parent']; - - // This is something that will likely come in a future version, but it - // is a relatively low use feature. Please get in touch if this is - // something you require. - if ( count( $pkey ) > 1 ) { - throw new \Exception("MJoin is not currently supported with a compound primary key for the main table", 1); - } - - $pkey = $pkey[0]; - $pkeyIsJoin = $pkey === $joinField || $pkey === $dteTable.'.'.$joinField; - - // Sanity check that table selector fields are read only, and have an name without - // a dot (for DataTables mData to be able to read it) - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - - if ( strpos( $field->dbField() , "." ) !== false ) { - if ( $field->set() !== Field::SET_NONE && $this->_set ) { - echo json_encode( array( - "sError" => "Table selected fields (i.e. '{table}.{column}') in `Join` ". - "must be read only. Use `set(false)` for the field to disable writing." - ) ); - exit(0); - } - - if ( strpos( $field->name() , "." ) !== false ) { - echo json_encode( array( - "sError" => "Table selected fields (i.e. '{table}.{column}') in `Join` ". - "must have a name alias which does not contain a period ('.'). Use ". - "name('---') to set a name for the field" - ) ); - exit(0); - } - } - } - - // Set up the JOIN query - $stmt = $db - ->query( 'select' ) - ->distinct( true ) - ->get( $dteTableLocal.'.'.$joinField.' as dteditor_pkey' ) - ->get( $this->_fields('get') ) - ->table( $dteTable .' as '. $dteTableLocal ); - - if ( $this->order() ) { - $stmt->order( $this->order() ); - } - - $this->_apply_where( $stmt ); - - if ( isset($this->_join['table']) ) { - // Working with a link table - $stmt - ->join( - $this->_join['table'], - $dteTableLocal.'.'.$this->_join['parent'][0] .' = '. $this->_join['table'].'.'.$this->_join['parent'][1] - ) - ->join( - $this->_table, - $this->_table.'.'.$this->_join['child'][0] .' = '. $this->_join['table'].'.'.$this->_join['child'][1] - ); - } - else { - // No link table in the middle - $stmt - ->join( - $this->_table, - $this->_table.'.'.$this->_join['child'] .' = '. $dteTableLocal.'.'.$this->_join['parent'] - ); - } - - $res = $stmt->exec(); - if ( ! $res ) { - return; - } - - // Map to primary key for fast lookup - $join = array(); - while ( $row=$res->fetch() ) { - $inner = array(); - - for ( $j=0 ; $j_fields) ; $j++ ) { - $field = $this->_fields[$j]; - if ( $field->apply('get') ) { - $inner[ $field->name() ] = $field->val('get', $row); - } - } - - if ( $this->_type === 'object' ) { - $join[ $row['dteditor_pkey'] ] = $inner; - } - else { - if ( !isset( $join[ $row['dteditor_pkey'] ] ) ) { - $join[ $row['dteditor_pkey'] ] = array(); - } - $join[ $row['dteditor_pkey'] ][] = $inner; - } - } - - if ( count($data) > 0 ) { - // Check that the joining field is available. The joining key can - // come from the Editor instance's primary key, or any other field, - // including a nested value (from a left join). If the instance's - // pkey, then we've got that in the DT_RowId parameter, so we can - // use that. Otherwise, the key must be in the field list. - if ( $this->_propExists( $dteTable.'.'.$joinField, $data[0] ) ) { - $readField = $dteTable.'.'.$joinField; - } - else if ( $this->_propExists( $joinField, $data[0] ) ) { - $readField = $joinField; - } - else if ( ! $pkeyIsJoin ) { - echo json_encode( array( - "sError" => "Join was performed on the field '{$joinField}' which was not " - ."included in the Editor field list. The join field must be included " - ."as a regular field in the Editor instance." - ) ); - exit(0); - } - - // Loop over the data and do a join based on the data available - for ( $i=0 ; $i_readProp( $readField, $data[$i] ); - - if ( isset( $join[$rowPKey] ) ) { - $data[$i][ $this->_name ] = $join[$rowPKey]; - } - else { - $data[$i][ $this->_name ] = ($this->_type === 'object') ? - (object)array() : array(); - } - } - } - - // Field options - foreach ($this->_fields as $field) { - $opts = $field->optionsExec( $db ); - - if ( $opts !== false ) { - $name = $this->name(). - ($this->_type === 'object' ? '.' : '[].'). - $field->name(); - $options[ $name ] = $opts; - } - } - } - - - /** - * Create a row. - * @param Editor $editor Host Editor instance - * @param int $parentId Parent row's primary key value - * @param string[] $data Data to be set for the join - * @internal - */ - public function create ( $editor, $parentId, $data ) - { - // If not settable, or the many count for the join was not submitted - // there we do nothing - if ( - ! $this->_set || - ! isset($data[$this->_name]) || - ! isset($data[$this->_name.'-many-count']) - ) { - return; - } - - $this->_prep( $editor ); - $db = $editor->db(); - - if ( $this->_type === 'object' ) { - $this->_insert( $db, $parentId, $data[$this->_name] ); - } - else { - for ( $i=0 ; $i_name]) ; $i++ ) { - $this->_insert( $db, $parentId, $data[$this->_name][$i] ); - } - } - } - - - /** - * Update a row. - * @param Editor $editor Host Editor instance - * @param int $parentId Parent row's primary key value - * @param string[] $data Data to be set for the join - * @internal - */ - public function update ( $editor, $parentId, $data ) - { - // If not settable, or the many count for the join was not submitted - // there we do nothing - if ( ! $this->_set || ! isset($data[$this->_name.'-many-count']) ) { - return; - } - - $this->_prep( $editor ); - $db = $editor->db(); - - if ( $this->_type === 'object' ) { - // update or insert - $this->_update_row( $db, $parentId, $data[$this->_name] ); - } - else { - // WARNING - this will remove rows and then readd them. Any - // data not in the field list WILL BE LOST - // todo - is there a better way of doing this? - $this->remove( $editor, array($parentId) ); - $this->create( $editor, $parentId, $data ); - } - } - - - /** - * Delete rows - * @param Editor $editor Host Editor instance - * @param int[] $ids Parent row IDs to delete - * @internal - */ - public function remove ( $editor, $ids ) - { - if ( ! $this->_set ) { - return; - } - - $that = $this; - $this->_prep( $editor ); - $db = $editor->db(); - - if ( isset($this->_join['table']) ) { - $stmt = $db - ->query( 'delete' ) - ->table( $this->_join['table'] ) - ->or_where( $this->_join['parent'][1], $ids ) - ->exec(); - } - else { - $stmt = $db - ->query( 'delete' ) - ->table( $this->_table ) - ->where_group( function ( $q ) use ( $that ) { - $q->or_where( $that->_join['child'], $ids ); - } ); - - $this->_apply_where( $stmt ); - - $stmt->exec(); - } - } - - - /** - * Validate input data - * - * @param &array $errors Errors array - * @param Editor $editor Editor instance - * @param string[] $data Data to validate - * @internal - */ - public function validate ( &$errors, $editor, $data ) - { - if ( ! $this->_set || ! isset($data[$this->_name]) ) { - return; - } - - $this->_prep( $editor ); - - $joinData = $data[$this->_name]; - - if ( $this->_type === 'object' ) { - $this->_validateFields( $errors, $editor, $joinData, $this->_name.'.' ); - } - else { - for ( $i=0 ; $i_validateFields( $errors, $editor, $joinData[$i], $this->_name.'[].' ); - } - } - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private methods - */ - - /** - * Add local WHERE condition to query - * @param \DataTables\Database\Query $query Query instance to apply the WHERE conditions to - * @private - */ - private function _apply_where ( $query ) - { - for ( $i=0 ; $i_where) ; $i++ ) { - if ( is_callable( $this->_where[$i] ) ) { - $this->_where[$i]( $query ); - } - else { - $query->where( - $this->_where[$i]['key'], - $this->_where[$i]['value'], - $this->_where[$i]['op'] - ); - } - } - } - - - /** - * Create a row. - * @param \DataTables\Database $db Database reference to use - * @param int $parentId Parent row's primary key value - * @param string[] $data Data to be set for the join - * @private - */ - private function _insert( $db, $parentId, $data ) - { - if ( isset($this->_join['table']) ) { - // Insert keys into the join table - $stmt = $db - ->query('insert') - ->table( $this->_join['table'] ) - ->set( $this->_join['parent'][1], $parentId ) - ->set( $this->_join['child'][1], $data[$this->_join['child'][0]] ) - ->exec(); - } - else { - // Insert values into the target table - $stmt = $db - ->query('insert') - ->table( $this->_table ) - ->set( $this->_join['child'], $parentId ); - - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - - if ( $field->apply( 'set', $data ) ) { - $stmt->set( $field->dbField(), $field->val('set', $data) ); - } - } - - // If the where condition variables should also be added to the database - // Note that `whereSet` is now deprecated - if ( $this->_whereSet ) { - for ( $i=0, $ien=count($this->_where) ; $i<$ien ; $i++ ) { - if ( ! is_callable( $this->_where[$i] ) ) { - $stmt->set( $this->_where[$i]['key'], $this->_where[$i]['value'] ); - } - } - } - - $stmt->exec(); - } - } - - - /** - * Prepare the instance to be run. - * - * @param Editor $editor Editor instance - * @private - */ - private function _prep ( $editor ) - { - $links = $this->_links; - - // Were links used to configure this instance - if so, we need to - // back them onto the join array - if ( $this->_join['parent'] === null && count($links) ) { - $editorTable = $editor->table(); - $editorTable = $editorTable[0]; - $joinTable = $this->table(); - - if ( $this->_aliasParentTable ) { - $editorTable = $this->_aliasParentTable; - } - - if ( count( $links ) === 2 ) { - // No link table - $f1 = explode( '.', $links[0] ); - $f2 = explode( '.', $links[1] ); - - if ( $f1[0] === $editorTable ) { - $this->_join['parent'] = $f1[1]; - $this->_join['child'] = $f2[1]; - } - else { - $this->_join['parent'] = $f2[1]; - $this->_join['child'] = $f1[1]; - } - } - else { - // Link table - $f1 = explode( '.', $links[0] ); - $f2 = explode( '.', $links[1] ); - $f3 = explode( '.', $links[2] ); - $f4 = explode( '.', $links[3] ); - - // Discover the name of the link table - if ( $f1[0] !== $editorTable && $f1[0] !== $joinTable ) { - $this->_join['table'] = $f1[0]; - } - else if ( $f2[0] !== $editorTable && $f2[0] !== $joinTable ) { - $this->_join['table'] = $f2[0]; - } - else if ( $f3[0] !== $editorTable && $f3[0] !== $joinTable ) { - $this->_join['table'] = $f3[0]; - } - else { - $this->_join['table'] = $f2[0]; - } - - $this->_join['parent'] = array( $f1[1], $f2[1] ); - $this->_join['child'] = array( $f3[1], $f4[1] ); - } - } - } - - - /** - * Update a row. - * @param \DataTables\Database $db Database reference to use - * @param int $parentId Parent row's primary key value - * @param string[] $data Data to be set for the join - * @private - */ - private function _update_row ( $db, $parentId, $data ) - { - if ( isset($this->_join['table']) ) { - // Got a link table, just insert the pkey references - $db->push( - $this->_join['table'], - array( - $this->_join['parent'][1] => $parentId, - $this->_join['child'][1] => $data[$this->_join['child'][0]] - ), - array( - $this->_join['parent'][1] => $parentId - ) - ); - } - else { - // No link table, just a direct reference - $set = array( - $this->_join['child'] => $parentId - ); - - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - - if ( $field->apply( 'set', $data ) ) { - $set[ $field->dbField() ] = $field->val('set', $data); - } - } - - // Add WHERE conditions - $where = array($this->_join['child'] => $parentId); - for ( $i=0, $ien=count($this->_where) ; $i<$ien ; $i++ ) { - $where[ $this->_where[$i]['key'] ] = $this->_where[$i]['value']; - - // Is there any point in this? Is there any harm? - // Note that `whereSet` is now deprecated - if ( $this->_whereSet ) { - if ( ! is_callable( $this->_where[$i] ) ) { - $set[ $this->_where[$i]['key'] ] = $this->_where[$i]['value']; - } - } - } - - $db->push( $this->_table, $set, $where ); - } - } - - - /** - * Create an SQL string from the fields that this instance knows about for - * using in queries - * @param string $direction Direction: 'get' or 'set'. - * @returns array Fields to include - * @private - */ - private function _fields ( $direction ) - { - $fields = array(); - - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - - if ( $field->apply( $direction, null ) ) { - if ( strpos( $field->dbField() , "." ) === false ) { - $fields[] = $this->_table.'.'.$field->dbField() ." as ".$field->dbField();; - } - else { - $fields[] = $field->dbField();// ." as ".$field->dbField(); - } - } - } - - return $fields; - } - - - /** - * Validate input data - * - * @param &array $errors Errors array - * @param Editor $editor Editor instance - * @param string[] $data Data to validate - * @param string $prefix Field error prefix for client-side to show the - * error message on the appropriate field - * @internal - */ - private function _validateFields ( &$errors, $editor, $data, $prefix ) - { - for ( $i=0 ; $i_fields) ; $i++ ) { - $field = $this->_fields[$i]; - $validation = $field->validate( $data, $editor ); - - if ( $validation !== true ) { - $errors[] = array( - "name" => $prefix.$field->name(), - "status" => $validation - ); - } - } - } -} - diff --git a/php/Editor/Mjoin.php b/php/Editor/Mjoin.php deleted file mode 100644 index 3f7b34d..0000000 --- a/php/Editor/Mjoin.php +++ /dev/null @@ -1,33 +0,0 @@ -leftJoin()` method. The API API is otherwise - * identical. - * - * This class is recommended over the `Join` class. - */ -class MJoin extends Join -{ - function __construct( $table=null ) - { - parent::__construct( $table, 'array' ); - } -} diff --git a/php/Editor/Options.php b/php/Editor/Options.php deleted file mode 100644 index c5c3a24..0000000 --- a/php/Editor/Options.php +++ /dev/null @@ -1,269 +0,0 @@ - - * Field::inst( 'users.site' ) - * ->options( Options::inst() - * ->table( 'sites' ) - * ->value( 'id' ) - * ->label( 'name' ) - * ) - * - * - * @example - * Get a list of options with custom ordering - * - * Field::inst( 'users.site' ) - * ->options( Options::inst() - * ->table( 'sites' ) - * ->value( 'id' ) - * ->label( 'name' ) - * ->order( 'name DESC' ) - * ) - * - * - * @example - * Get a list of options showing the id and name in the label - * - * Field::inst( 'users.site' ) - * ->options( Options::inst() - * ->table( 'sites' ) - * ->value( 'id' ) - * ->label( [ 'name', 'id' ] ) - * ->render( function ( $row ) { - * return $row['name'].' ('.$row['id'].')'; - * } ) - * ) - * - */ -class Options extends DataTables\Ext { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Private parameters - */ - - /** @var string Table to get the information from */ - private $_table = null; - - /** @var string Column name containing the value */ - private $_value = null; - - /** @var string[] Column names for the label(s) */ - private $_label = array(); - - /** @var integer Row limit */ - private $_limit = null; - - /** @var callable Callback function to do rendering of labels */ - private $_renderer = null; - - /** @var callback Callback function to add where conditions */ - private $_where = null; - - /** @var string ORDERY BY clause */ - private $_order = null; - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - /** - * Get / set the column(s) to use as the label value of the options - * - * @param null|string|string[] $_ null to get the current value, string or - * array to get. - * @return Options|string[] Self if setting for chaining, array of values if - * getting. - */ - public function label ( $_=null ) - { - if ( $_ === null ) { - return $this; - } - else if ( is_string($_) ) { - $this->_label = array( $_ ); - } - else { - $this->_label = $_; - } - - return $this; - } - - /** - * Get / set the LIMIT clause to limit the number of records returned. - * - * @param [type] $_ null|number Number of rows to limit the result to - * @return Options|string[] Self if setting for chaining, limit if getting. - */ - public function limit ( $_=null ) - { - return $this->_getSet( $this->_limit, $_ ); - } - - /** - * Get / set the ORDER BY clause to use in the SQL. If this option is not - * provided the ordering will be based on the rendered output, either - * numerically or alphabetically based on the data returned by the renderer. - * - * @param null|string $_ String to set, null to get current value - * @return Options|string Self if setting for chaining, string if getting. - */ - public function order ( $_=null ) - { - return $this->_getSet( $this->_order, $_ ); - } - - /** - * Get / set the label renderer. The renderer can be used to combine - * multiple database columns into a single string that is shown as the label - * to the end user in the list of options. - * - * @param null|callable $_ Function to set, null to get current value - * @return Options|callable Self if setting for chaining, callable if - * getting. - */ - public function render ( $_=null ) - { - return $this->_getSet( $this->_renderer, $_ ); - } - - /** - * Get / set the database table from which to gather the options for the - * list. - * - * @param null|string $_ String to set, null to get current value - * @return Options|string Self if setting for chaining, string if getting. - */ - public function table ( $_=null ) - { - return $this->_getSet( $this->_table, $_ ); - } - - /** - * Get / set the column name to use for the value in the options list. This - * would normally be the primary key for the table. - * - * @param null|string $_ String to set, null to get current value - * @return Options|string Self if setting for chaining, string if getting. - */ - public function value ( $_=null ) - { - return $this->_getSet( $this->_value, $_ ); - } - - /** - * Get / set the method to use for a WHERE condition if it is to be - * applied to the query to get the options. - * - * @param null|callable $_ Function to set, null to get current value - * @return Options|callable Self if setting for chaining, callable if - * getting. - */ - public function where ( $_=null ) - { - return $this->_getSet( $this->_where, $_ ); - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Internal methods - */ - - /** - * Execute the options (i.e. get them) - * - * @param Database $db Database connection - * @return array List of options - * @internal - */ - public function exec ( $db ) - { - $label = $this->_label; - $value = $this->_value; - $formatter = $this->_renderer; - - // Create a list of the fields that we need to get from the db - $fields = array(); - $fields[] = $value; - $fields = array_merge( $fields, $label ); - - // We need a default formatter if one isn't provided - if ( ! $formatter ) { - $formatter = function ( $row ) use ( $label ) { - $a = array(); - - for ( $i=0, $ien=count($label) ; $i<$ien ; $i++ ) { - $a[] = $row[ $label[$i] ]; - } - - return implode(' ', $a); - }; - } - - // Get the data - $q = $db - ->query('select') - ->table( $this->_table ) - ->distinct( true ) - ->get( $fields ) - ->where( $this->_where ) - ->order( $this->_order ); - - if ( $this->_limit !== null ) { - $q->limit( $this->_limit ); - } - - $rows = $q - ->exec() - ->fetchAll(); - - // Create the output array - $out = array(); - - for ( $i=0, $ien=count($rows) ; $i<$ien ; $i++ ) { - $out[] = array( - "label" => $formatter( $rows[$i] ), - "value" => $rows[$i][$value] - ); - } - - // Only sort if there was no SQL order field - if ( ! $this->_order ) { - usort( $out, function ( $a, $b ) { - return is_numeric($a['label']) && is_numeric($b['label']) ? - ($a['label']*1) - ($b['label']*1) : - strcmp( $a['label'], $b['label'] ); - } ); - } - - return $out; - } -} - \ No newline at end of file diff --git a/php/Editor/Upload.php b/php/Editor/Upload.php deleted file mode 100644 index 23ca79b..0000000 --- a/php/Editor/Upload.php +++ /dev/null @@ -1,664 +0,0 @@ - - * Field::inst( 'imageId' ) - * ->upload( - * Upload::inst( $_SERVER['DOCUMENT_ROOT'].'/uploads/__ID__.__EXTN__' ) - * ->db( 'files', 'id', array( - * 'webPath' => Upload::DB_WEB_PATH, - * 'fileName' => Upload::DB_FILE_NAME, - * 'fileSize' => Upload::DB_FILE_SIZE, - * 'systemPath' => Upload::DB_SYSTEM_PATH - * ) ) - * ->allowedExtensions( [ 'png', 'jpg' ], "Please upload an image file" ) - * ) - * - * - * @example - * As above, but with PHP 5.4 (which allows chaining from new instances of a - * class) - * - * newField( 'imageId' ) - * ->upload( - * new Upload( $_SERVER['DOCUMENT_ROOT'].'/uploads/__ID__.__EXTN__' ) - * ->db( 'files', 'id', array( - * 'webPath' => Upload::DB_WEB_PATH, - * 'fileName' => Upload::DB_FILE_NAME, - * 'fileSize' => Upload::DB_FILE_SIZE, - * 'systemPath' => Upload::DB_SYSTEM_PATH - * ) ) - * ->allowedExtensions( [ 'png', 'jpg' ], "Please upload an image file" ) - * ) - * - */ -class Upload extends DataTables\Ext { - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Constants - */ - - /** Database value option (`Db()`) - File content. This should be written to - * a blob. Typically this should be avoided and the file saved on the file - * system, but there are cases where it can be useful to store the file in - * the database. - */ - const DB_CONTENT = 'editor-content'; - - /** Database value option (`Db()`) - Content type */ - const DB_CONTENT_TYPE = 'editor-contentType'; - - /** Database value option (`Db()`) - File extension */ - const DB_EXTN = 'editor-extn'; - - /** Database value option (`Db()`) - File name (with extension) */ - const DB_FILE_NAME = 'editor-fileName'; - - /** Database value option (`Db()`) - File size (bytes) */ - const DB_FILE_SIZE = 'editor-fileSize'; - - /** Database value option (`Db()`) - MIME type */ - const DB_MIME_TYPE = 'editor-mimeType'; - - /** Database value option (`Db()`) - Full system path to the file */ - const DB_SYSTEM_PATH = 'editor-systemPath'; - - /** Database value option (`Db()`) - HTTP path to the file. This is derived - * from the system path by removing `$_SERVER['DOCUMENT_ROOT']`. If your - * images live outside of the document root a custom value would be to be - * used. - */ - const DB_WEB_PATH = 'editor-webPath'; - - - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Private parameters - */ - - private $_action = null; - private $_dbCleanCallback = null; - private $_dbCleanTableField = null; - private $_dbTable = null; - private $_dbPKey = null; - private $_dbFields = null; - private $_extns = null; - private $_extnError = null; - private $_error = null; - private $_validators = array(); - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Constructor - */ - - /** - * Upload instance constructor - * @param string|callable $action Action to take on upload - this is applied - * directly to {@link action}. - */ - function __construct( $action=null ) - { - if ( $action ) { - $this->action( $action ); - } - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Public methods - */ - - /** - * Set the action to take when a file is uploaded. This can be either of: - * - * * A string - the value given is the full system path to where the - * uploaded file is written to. The value given can include three "macros" - * which are replaced by the script dependent on the uploaded file: - * * `__EXTN__` - the file extension - * * `__NAME__` - the uploaded file's name (including the extension) - * * `__ID__` - Database primary key value if the {@link db} method is - * used. - * * A closure - if a function is given the responsibility of what to do - * with the uploaded file is transferred to this function. That will - * typically involve writing it to the file system so it can be used - * later. - * - * @param string|callable $action Action to take - see description above. - * @return self Current instance, used for chaining - */ - public function action ( $action ) - { - $this->_action = $action; - - return $this; - } - - - /** - * An array of valid file extensions that can be uploaded. This is for - * simple validation that the file is of the expected type - for example you - * might use `[ 'png', 'jpg', 'jpeg', 'gif' ]` for images. The check is - * case-insensitive. If no extensions are given, no validation is performed - * on the file extension. - * - * @param string[] $extn List of file extensions that are allowable for - * the upload - * @param string $error Error message if a file is uploaded that doesn't - * match the valid list of extensions. - * @return self Current instance, used for chaining - */ - public function allowedExtensions ( $extn, $error="This file type cannot be uploaded" ) - { - $this->_extns = $extn; - $this->_extnError = $error; - - return $this; - } - - - /** - * Database configuration method. When used, this method will tell Editor - * what information you want written to a database on file upload, should - * you wish to store relational information about your file on the database - * (this is generally recommended). - * - * @param string $table The name of the table where the file information - * should be stored - * @param string $pkey Primary key column name. The `Upload` class - * requires that the database table have a single primary key so each - * row can be uniquely identified. - * @param array $fields A list of the fields to be written to on upload. - * The property names are the database columns and the values can be - * defined by the constants of this class. The value can also be a - * string or a closure function if you wish to send custom information - * to the database. - * @return self Current instance, used for chaining - */ - public function db ( $table, $pkey, $fields ) - { - $this->_dbTable = $table; - $this->_dbPKey = $pkey; - $this->_dbFields = $fields; - - return $this; - } - - - /** - * Set a callback function that is used to remove files which no longer have - * a reference in a source table. - * - * @param callable $callback Function that will be executed on clean. It is - * given an array of information from the database about the orphaned - * rows, and can return true to indicate that the rows should be - * removed from the database. Any other return value (including none) - * will result in the records being retained. - * @return self Current instance, used for chaining - */ - public function dbClean( $tableField, $callback=null ) - { - // Argument swapping - if ( $callback === null ) { - $callback = $tableField; - $tableField = null; - } - - $this->_dbCleanCallback = $callback; - $this->_dbCleanTableField = $tableField; - - return $this; - } - - - /** - * Add a validation method to check file uploads. Multiple validators can be - * added by calling this method multiple times - they will be executed in - * sequence when a file has been uploaded. - * - * @param callable $fn Validation function. A PHP `$_FILES` parameter is - * passed in for the uploaded file and the return is either a string - * (validation failed and error message), or `null` (validation passed). - * @return self Current instance, used for chaining - */ - public function validator ( $fn ) - { - $this->_validators[] = $fn; - - return $this; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Internal methods - */ - - /** - * Get database information data from the table - * - * @param \DataTables\Database $db Database - * @param number [$id=null] Limit to a specific id - * @return array Database information - * @internal - */ - public function data ( $db, $id=null ) - { - if ( ! $this->_dbTable ) { - return null; - } - - // Select the details requested, for the columns requested - $q = $db - ->query( 'select' ) - ->table( $this->_dbTable ) - ->get( $this->_dbPKey ); - - foreach ( $this->_dbFields as $column => $prop ) { - if ( $prop !== self::DB_CONTENT ) { - $q->get( $column ); - } - } - - if ( $id !== null ) { - $q->where( $this->_dbPKey, $id ); - } - - $result = $q->exec()->fetchAll(); - $out = array(); - - for ( $i=0, $ien=count($result) ; $i<$ien ; $i++ ) { - $out[ $result[$i][ $this->_dbPKey ] ] = $result[$i]; - } - - return $out; - } - - - /** - * Clean the database - * @param \DataTables\Editor $editor Calling Editor instance - * @param Field $field Host field - * @internal - */ - public function dbCleanExec ( $editor, $field ) - { - // Database and file system clean up BEFORE adding the new file to - // the db, otherwise it will be removed immediately - $tables = $editor->table(); - $this->_dbClean( $editor->db(), $tables[0], $field->dbField() ); - } - - - /** - * Get the set error message - * - * @return string Class error - * @internal - */ - public function error () - { - return $this->_error; - } - - - /** - * Execute an upload - * - * @param \DataTables\Editor $editor Calling Editor instance - * @return int Primary key value - * @internal - */ - public function exec ( $editor ) - { - $id = null; - $upload = $_FILES['upload']; - - // Validation - PHP standard validation - if ( $upload['error'] !== UPLOAD_ERR_OK ) { - if ( $upload['error'] === UPLOAD_ERR_INI_SIZE ) { - $this->_error = "File exceeds maximum file upload size"; - } - else { - $this->_error = "There was an error uploading the file (".$upload['error'].")"; - } - return false; - } - - // Validation - acceptable file extensions - if ( is_array( $this->_extns ) ) { - $extn = pathinfo($upload['name'], PATHINFO_EXTENSION); - - if ( in_array( strtolower($extn), array_map( 'strtolower', $this->_extns ) ) === false ) { - $this->_error = $this->_extnError; - return false; - } - } - - // Validation - custom callback - for ( $i=0, $ien=count($this->_validators) ; $i<$ien ; $i++ ) { - $res = $this->_validators[$i]( $upload ); - - if ( is_string( $res ) ) { - $this->_error = $res; - return false; - } - } - - // Database - if ( $this->_dbTable ) { - foreach ( $this->_dbFields as $column => $prop ) { - // We can't know what the path is, if it has moved into place - // by an external function - throw an error if this does happen - if ( ! is_string( $this->_action ) && - ($prop === self::DB_SYSTEM_PATH || $prop === self::DB_WEB_PATH ) - ) { - $this->_error = "Cannot set path information in database ". - "if a custom method is used to save the file."; - - return false; - } - } - - // Commit to the database - $id = $this->_dbExec( $editor->db() ); - } - - // Perform file system actions - return $this->_actionExec( $id ); - } - - - /** - * Get the primary key column for the table - * - * @return string Primary key column name - * @internal - */ - public function pkey () - { - return $this->_dbPKey; - } - - - /** - * Get the db table name - * - * @return string DB table name - * @internal - */ - public function table () - { - return $this->_dbTable; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * - * Private methods - */ - - /** - * Execute the configured action for the upload - * - * @param int $id Primary key value - * @return int File identifier - typically the primary key - */ - private function _actionExec ( $id ) - { - $upload = $_FILES['upload']; - - if ( ! is_string( $this->_action ) ) { - // Custom function - $action = $this->_action; - return $action( $upload, $id ); - } - - // Default action - move the file to the location specified by the - // action string - $to = $this->_path( $upload['name'], $id ); - $res = move_uploaded_file( $upload['tmp_name'], $to ); - - if ( $res === false ) { - $this->_error = "An error occurred while moving the uploaded file."; - return false; - } - - return $id !== null ? - $id : - $to; - } - - /** - * Perform the database clean by first getting the information about the - * orphaned rows and then calling the callback function. The callback can - * then instruct the rows to be removed through the return value. - * - * @param \DataTables\Database $db Database instance - * @param string $editorTable Editor Editor instance table name - * @param string $fieldName Host field's name - */ - private function _dbClean ( $db, $editorTable, $fieldName ) - { - $callback = $this->_dbCleanCallback; - - if ( ! $this->_dbTable || ! $callback ) { - return; - } - - // If there is a table / field that we should use to check if the value - // is in use, then use that. Otherwise we'll try to use the information - // from the Editor / Field instance. - if ( $this->_dbCleanTableField ) { - $fieldName = $this->_dbCleanTableField; - } - - $a = explode('.', $fieldName); - if ( count($a) === 1 ) { - $table = $editorTable; - $field = $a[0]; - } - else if ( count($a) === 2 ) { - $table = $a[0]; - $field = $a[1]; - } - else { - $table = $a[1]; - $field = $a[2]; - } - - // Select the details requested, for the columns requested - $q = $db - ->query( 'select' ) - ->table( $this->_dbTable ) - ->get( $this->_dbPKey ); - - foreach ( $this->_dbFields as $column => $prop ) { - if ( $prop !== self::DB_CONTENT ) { - $q->get( $column ); - } - } - - $q->where( $this->_dbPKey, '(SELECT '.$field.' FROM '.$table.' WHERE '.$field.' IS NOT NULL)', 'NOT IN', false ); - - $data = $q->exec()->fetchAll(); - - if ( count( $data ) === 0 ) { - return; - } - - $result = $callback( $data ); - - // Delete the selected rows, iff the developer says to do so with the - // returned value (i.e. acknowledge that the files have be removed from - // the file system) - if ( $result === true ) { - $qDelete = $db - ->query( 'delete' ) - ->table( $this->_dbTable ); - - for ( $i=0, $ien=count( $data ) ; $i<$ien ; $i++ ) { - $qDelete->or_where( $this->_dbPKey, $data[$i][ $this->_dbPKey ] ); - } - - $qDelete->exec(); - } - } - - /** - * Add a record to the database for a newly uploaded file - * - * @param \DataTables\Database $db Database instance - * @return int Primary key value for the newly uploaded file - */ - private function _dbExec ( $db ) - { - $upload = $_FILES['upload']; - $pathFields = array(); - - // Insert the details requested, for the columns requested - $q = $db - ->query( 'insert' ) - ->table( $this->_dbTable ); - - foreach ( $this->_dbFields as $column => $prop ) { - switch ( $prop ) { - case self::DB_CONTENT: - $q->set( $column, file_get_contents($upload['tmp_name']) ); - break; - - case self::DB_CONTENT_TYPE: - case self::DB_MIME_TYPE: - $finfo = finfo_open(FILEINFO_MIME); - $mime = finfo_file($finfo, $upload['tmp_name']); - finfo_close($finfo); - - $q->set( $column, $mime ); - break; - - case self::DB_EXTN: - $extn = pathinfo($upload['name'], PATHINFO_EXTENSION); - $q->set( $column, $extn ); - break; - - case self::DB_FILE_NAME: - $q->set( $column, $upload['name'] ); - break; - - case self::DB_FILE_SIZE: - $q->set( $column, $upload['size'] ); - break; - - case self::DB_SYSTEM_PATH: - $pathFields[ $column ] = self::DB_SYSTEM_PATH; - $q->set( $column, '-' ); // Use a temporary value to avoid cases - break; // where the db will reject empty values - - case self::DB_WEB_PATH: - $pathFields[ $column ] = self::DB_WEB_PATH; - $q->set( $column, '-' ); // Use a temporary value (as above) - break; - - default: - if ( is_callable($prop) && is_object($prop) ) { // is a closure - $q->set( $column, $prop( $db, $upload ) ); - } - else { - $q->set( $column, $prop ); - } - - break; - } - } - - $res = $q->exec(); - $id = $res->insertId(); - - // Update the newly inserted row with the path information. We have to - // use a second statement here as we don't know in advance what the - // database schema is and don't want to prescribe that certain triggers - // etc be created. It makes it a bit less efficient but much more - // compatible - if ( count( $pathFields ) ) { - // For this to operate the action must be a string, which is - // validated in the `exec` method - $path = $this->_path( $upload['name'], $id ); - $webPath = str_replace($_SERVER['DOCUMENT_ROOT'], '', $path); - $q = $db - ->query( 'update' ) - ->table( $this->_dbTable ) - ->where( $this->_dbPKey, $id ); - - foreach ( $pathFields as $column => $type ) { - $q->set( $column, $type === self::DB_WEB_PATH ? $webPath : $path ); - } - - $q->exec(); - } - - return $id; - } - - - /** - * Apply macros to a user specified path - * - * @param string $name File path - * @param int $id Primary key value for the file - * @return string Resolved path - */ - private function _path ( $name, $id ) - { - $extn = pathinfo( $name, PATHINFO_EXTENSION ); - - $to = $this->_action; - $to = str_replace( "__NAME__", $name, $to ); - $to = str_replace( "__ID__", $id, $to ); - $to = str_replace( "__EXTN__", $extn, $to ); - - return $to; - } -} - diff --git a/php/Editor/Validate.php b/php/Editor/Validate.php deleted file mode 100644 index 9cea44c..0000000 --- a/php/Editor/Validate.php +++ /dev/null @@ -1,961 +0,0 @@ - - * // Ensure that a non-empty value is given for a field - * Field::inst( 'engine' )->validator( 'Validate::required' ) - * - * - * @example - * - * // Don't require a field to be submitted, but if it is submitted, it - * // must be non-empty - * Field::inst( 'reg_date' )->validator( 'Validate::notEmpty' ) - * - * // or (this is identical in functionality to the line above): - * Field::inst( 'reg_date' )->validator( 'Validate::basic', array('empty'=>true) ) - * - * - * @example - * - * // Date validation - * Field::inst( 'reg_date' )->validator( 'Validate::date_format', 'D, d M y' ) - * - * - * @example - * - * // Date validation with a custom error message - * Field::inst( 'reg_date' )->validator( 'Validate::date_format', array( - * 'format' => 'D, d M y', - * 'message' => "Invalid date" - * ) ) - * - * - * @example - * - * // Require a non-empty e-mail address - * Field::inst( 'reg_date' )->validator( 'Validate::email', array('required'=>true) ) - * - * // or (this is identical in functionality to the line above): - * Field::inst( 'reg_date' )->validator( 'Validate::email', array( - * 'empty' => false, - * 'optional' => false - * ) ) - * - * - * @example - * - * // Custom validation - closure - * Field::inst( 'engine' )->validator( function($val, $data, $opts) { - * if ( ! preg_match( '/^1/', $val ) ) { - * return "Value must start with a 1"; - * } - * return true; - * } ) - * - */ -class Validate { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Internal functions - */ - - /** - * "Magic" method to automatically apply the required check to any of the - * static methods simply by adding '_required' or '_empty' to the end of the - * method's name, depending on if you need the field to be submitted or not. - * - * This is retained for backwards compatibility, but the validation options - * are now the recommended way of indicating that a field is required. - * - * @internal - * @param string $name Function name - * @param array $arguments Function arguments - * @return mixed|string - */ - public static function __callStatic( $name, $arguments ) { - if ( preg_match( '/_required$/', $name ) ) { - if ( $arguments[0] === null || $arguments[0] === '' ) { - return 'This field is required'; - } - - return call_user_func_array( - __NAMESPACE__.'\Validate::'.str_replace( '_required', '', $name ), - $arguments - ); - } - } - - - /** - * Extend the options from the user function and the validation function - * with core defaults. - * - * @internal - */ - public static function _extend( $userOpts, $prop, $fnOpts ) { - $cfg = array( - 'message' => 'Input not valid', - 'required' => false, - 'empty' => true, - 'optional' => true - ); - - if ( ! is_array( $userOpts ) ) { - if ( $prop ) { - $cfg[ $prop ] = $userOpts; - } - - // Not an array, but the non-array case has been handled above, so - // just an empty array - $userOpts = array(); - } - - // Merge into a single array - first array gets priority if there is a - // key clash, so user first, then function commands and finally the - // global options - $cfg = $userOpts + $fnOpts + $cfg; - - return $cfg; - } - - - /** - * Perform common validation using the configuration parameters - * - * @internal - */ - public static function _common( $val, $cfg ) { - // `required` is a shortcut for optional==false && empty==false - $optional = $cfg['required'] ? false : $cfg['optional']; - $empty = $cfg['required'] ? false : $cfg['empty']; - - // Error state tests - if ( ! $optional && $val === null ) { - // Value must be given - return $cfg['message']; - } - else if ( $empty === false && $val === '' ) { - // Value must not be empty - return $cfg['message']; - } - - // Validation passed states - if ( $optional && $val === null ) { - return true; - } - else if ( $empty === true && $val === '' ) { - return true; - } - - // Have the specific validation function perform its tests - return null; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Basic validation - */ - - /** - * No validation - all inputs are valid. - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @return true - */ - public static function none( $val, $data, $opts, $host ) { - return true; - } - - - /** - * Basic validation - this is used to perform the validation provided by the - * validation options only. If the validation options pass (e.g. `required`, - * `empty` and `optional`) then the validation will pass regardless of the - * actual value. - * - * Note that there are two helper short-cut methods that provide the same - * function as this method, but are slightly shorter: - * - * ``` - * // Required: - * Validate::required() - * - * // is the same as - * Validate::basic( $val, $data, array( - * "required" => true - * ); - * ``` - * - * ``` - * // Optional, but not empty if given: - * Validate::notEmpty() - * - * // is the same as - * Validate::basic( $val, $data, array( - * "empty" => false - * ); - * ``` - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - static function basic( $val, $data, $opts, $host ) { - // Common validation with user override option - $cfg = Validate::_extend( $opts, null, array() ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return true; - } - - - /** - * Required field - there must be a value and it must be a non-empty value - * - * This is a helper short-cut method which is the same as: - * - * ``` - * Validate::basic( $val, $data, array( - * "required" => true - * ); - * ``` - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - static function required( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => "This field is required", - 'required' => true - ) ); - - $common = Validate::_common( $val, $cfg ); - return $common !== null ? - $common : - true; - } - - - /** - * Optional field, but if given there must be a non-empty value - * - * This is a helper short-cut method which is the same as: - * - * ``` - * Validate::basic( $val, $data, array( - * "empty" => false - * ); - * ``` - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - static function notEmpty( $val, $data, $opts, $host ) { - // Supplying this field is optional, but if given, then it must be - // non-empty (user can override by passing in `empty=true` in opts - // at which point there is basically no validation - $cfg = Validate::_extend( $opts, null, array( - 'message' => "This field is required", - 'empty' => false - ) ); - - $common = Validate::_common( $val, $cfg ); - return $common !== null ? - $common : - true; - } - - - /** - * Validate an input as a boolean value. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function boolean( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => "Please enter true or false" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - if ( filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === false ) { - return $cfg['message']; - } - return true; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Number validation methods - */ - - /** - * Check that any input is numeric. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. Additional options: - * * `decimal`: is available to indicate what character should be used - * as the decimal - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function numeric ( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => "This input must be given as a number", - 'decimal' => '.' - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - if ( $cfg['decimal'] !== '.' ) { - $val = str_replace( $cfg['decimal'], '.', $val ); - } - - return ! is_numeric( $val ) ? - $cfg['message'] : - true; - } - - /** - * Check for a numeric input and that it is greater than a given value. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. Additional options: - * * `min`: indicate the minimum value. If only the default validation - * options are required, this parameter can be given as an integer - * value, which will be used as the minimum value. - * * `decimal`: is available to indicate what character should be used - * as the decimal - * separator (default '.'). - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function minNum ( $val, $data, $opts, $host ) { - // `numeric` will do the common validation for us - $numeric = Validate::numeric( $val, $data, $opts, $host ); - if ( $numeric !== true ) { - return $numeric; - } - - $min = is_array($opts) ? $opts['min'] : $opts; - $cfg = Validate::_extend( $opts, 'min', array( - 'message' => "Number is too small, must be ".$min." or larger" - ) ); - - if ( $cfg['decimal'] !== '.' ) { - $val = str_replace( $cfg['decimal'], '.', $val ); - } - - return $val < $min ? - $cfg['message'] : - true; - } - - /** - * Check for a numeric input and that it is less than a given value. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. - * * `max`: indicate the maximum value. If only the default validation - * options are required, this parameter can be given as an integer - * value, which will be used as the maximum value. - * * `decimal`: is available to indicate what character should be used - * as the decimal - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function maxNum ( $val, $data, $opts, $host ) { - // `numeric` will do the common validation for us - $numeric = Validate::numeric( $val, $data, $opts, $host ); - if ( $numeric !== true ) { - return $numeric; - } - - $max = is_array($opts) ? $opts['max'] : $opts; - $cfg = Validate::_extend( $opts, 'min', array( - 'message' => "Number is too large, must be ".$max." or smaller" - ) ); - - if ( $cfg['decimal'] !== '.' ) { - $val = str_replace( $cfg['decimal'], '.', $val ); - } - - return $val > $max ? - $cfg['message'] : - true; - } - - - /** - * Check for a numeric input and that it is both greater and smaller than - * given numbers. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. Additional options: - * * `min`: indicate the minimum value. - * * `max`: indicate the maximum value. - * * `decimal`: is available to indicate what character should be used - * as the decimal - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function minMaxNum ( $val, $data, $opts, $host ) { - $numeric = Validate::numeric( $val, $data, $opts, $host ); - if ( $numeric !== true ) { - return $numeric; - } - - $min = Validate::minNum( $val, $data, $opts, $host ); - if ( $min !== true ) { - return $min; - } - - return Validate::maxNum( $val, $data, $opts, $host ); - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * String validation methods - */ - - /** - * Validate an input as an e-mail address. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function email( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => "Please enter a valid e-mail address" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return filter_var($val, FILTER_VALIDATE_EMAIL) !== false ? - true : - $cfg['message']; - } - - - /** - * Validate a string has a minimum length. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional option of - * `min` is available for this method to indicate the minimum string - * length. If only the default validation options are required, this - * parameter can be given as an integer value, which will be used as the - * minimum string length. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function minLen( $val, $data, $opts, $host ) { - $min = is_array($opts) ? $opts['min'] : $opts; - $cfg = Validate::_extend( $opts, null, array( - 'message' => "The input is too short. ".$min." characters required (".($min-strlen($val))." more required)" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return strlen( $val ) < $min ? - $cfg['message'] : - true; - } - - - /** - * Validate a string does not exceed a maximum length. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional option of - * `max` is available for this method to indicate the maximum string - * length. If only the default validation options are required, this - * parameter can be given as an integer value, which will be used as the - * maximum string length. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function maxLen( $val, $data, $opts, $host ) { - $max = is_array($opts) ? $opts['max'] : $opts; - $cfg = Validate::_extend( $opts, null, array( - 'message' => "The input is ".(strlen($val)-$max)." characters too long" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return strlen( $val ) > $max ? - $cfg['message'] : - true; - } - - /** - * Require a string with a certain minimum or maximum number of characters. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional options of - * `min` and `max` are available for this method to indicate the minimum - * and maximum string lengths, respectively. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function minMaxLen( $val, $data, $opts, $host ) { - $min = Validate::minLen( $val, $data, $opts, $host ); - if ( $min !== true ) { - return $min; - } - - return Validate::maxLen( $val, $data, $opts, $host ); - } - - - /** - * Validate as an IP address. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function ip( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => "Please enter a valid IP address" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return filter_var($val, FILTER_VALIDATE_IP) === false ? - $cfg['message'] : - true; - } - - - /** - * Validate as an URL address. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array $opts Validation options. No additional options are - * available or required for this validation method. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function url( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => "Please enter a valid URL" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return filter_var($val, FILTER_VALIDATE_URL) === false ? - $cfg['message'] : - true; - } - - - /** - * Check if string could contain an XSS attack string - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional options of - * `db` - database connection object, `table` - database table to use and - * `column` - the column to check this value against as value, are also - * available. These options are not required and if not given are - * automatically derived from the Editor and Field instances. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function xss ( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => 'This field contains potentially unsafe data', - 'db' => null, - 'table' => null, - 'field' => null - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - $editor = $host['editor']; - $field = $host['field']; - - return $field->xssSafety( $val ) != $val ? - $cfg['message'] : - true; - } - - - /** - * Confirm that the value submitted is in a list of allowable values - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional options of - * `db` - database connection object, `table` - database table to use and - * `column` - the column to check this value against as value, are also - * available. These options are not required and if not given are - * automatically derived from the Editor and Field instances. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function values( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'empty' => null, - 'message' => 'This value is not valid', - 'db' => null, - 'table' => null, - 'field' => null, - 'valid' => array() - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - // Allow local values to be defined - for example null - return in_array($val, $cfg['valid']) ? - true : - $cfg['message']; - } - - - /** - * Check if there are any tags in the submitted value - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional options of - * `db` - database connection object, `table` - database table to use and - * `column` - the column to check this value against as value, are also - * available. These options are not required and if not given are - * automatically derived from the Editor and Field instances. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function noTags ( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => 'This field may not contain HTML', - 'db' => null, - 'table' => null, - 'field' => null - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - return strip_tags( $val ) != $val ? - $cfg['message'] : - true; - } - - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Date validation methods - */ - - /** - * Check that a valid date input is given - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param array|string $opts If given as a string, then $opts is the date - * format to check the validity of. If given as an array, then the - * date format is in the 'format' parameter, and the return error - * message in the 'message' parameter. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function dateFormat( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, 'format', array( - 'message' => "Date is not in the expected format" - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - $date = \DateTime::createFromFormat( $cfg['format'], $val) ; - - return $date && $date->format( $cfg['format'] ) == $val ? - true : - $cfg['message']; - } - - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Database validation methods - */ - - /** - * Check that the given value is unique in the database - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional options of - * `db` - database connection object, `table` - database table to use and - * `column` - the column to check this value against as value, are also - * available. These options are not required and if not given are - * automatically derived from the Editor and Field instances. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function unique( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'message' => 'This field must have a unique value', - 'db' => null, - 'table' => null, - 'field' => null - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - $editor = $host['editor']; - $field = $host['field']; - - $db = $cfg['db'] ? - $cfg['db'] : - $host['db']; - - $table = $cfg['table'] ? - $cfg['table'] : - $editor->table(); // Returns an array, but `select` will take an array - - $column = $cfg['field'] ? - $cfg['field'] : - $field->dbField(); - - $query = $db - ->query( 'select', $table ) - ->get( $column ) - ->where( $column, $val ); - - // If doing an edit, then we need to also discount the current row, - // since it is of course already validly unique - if ( $host['action'] === 'edit' ) { - $cond = $editor->pkeyToArray( $host['id'], true ); - $query->where( $cond, null, '!=' ); - } - - $res = $query->exec(); - - return $res->count() === 0 ? - true : - $cfg['message']; - } - - /** - * Check that the given value is a value that is available in a database - - * i.e. a join primary key. This will attempt to automatically use the table - * name and value column from the field's `options` method (under the - * assumption that it will typically be used with a joined field), but the - * table and field can also be specified via the options. - * - * @param string $val The value to check for validity - * @param string[] $data The full data set submitted - * @param int|array $opts Validation options. The additional options of - * `db` - database connection object, `table` - database table to use and - * `column` - the column to check this value against as value, are also - * available. These options are not required and if not given are - * automatically derived from the Editor and Field instances. - * @param array $host Host information - * @return string|true true if the value is valid, a string with an error - * message otherwise. - */ - public static function dbValues( $val, $data, $opts, $host ) { - $cfg = Validate::_extend( $opts, null, array( - 'empty' => null, - 'message' => 'This value is not valid', - 'db' => null, - 'table' => null, - 'field' => null, - 'valid' => array() - ) ); - - $common = Validate::_common( $val, $cfg ); - if ( $common !== null ) { - return $common; - } - - // Allow local values to be defined - for example null - if ( in_array($val, $cfg['valid']) ) { - return true; - } - - $editor = $host['editor']; - $field = $host['field']; - $options = $field->options(); - - $db = $cfg['db'] ? $cfg['db'] : $host['db']; - $table = $cfg['table'] ? $cfg['table'] : $options->table(); - $column = $cfg['field'] ? $cfg['field'] : $options->value(); - - if ( ! $table ) { - throw new \Exception('Table for database value check is not defined for field '.$field->name()); - } - - if ( ! $column ) { - throw new \Exception('Value column for database value check is not defined for field '.$field->name()); - } - - // Try / catch in case the submitted value can't be represented as the - // database type (e.g. an empty string as an integer) - try { - $count = $db - ->query( 'select', $table ) - ->get( $column ) - ->where( $column, $val ) - ->exec() - ->count(); - - return $count === 0 ? - $cfg['message'] : - true; - } - catch (\Exception $e) { - return $cfg['message']; - } - } -} - diff --git a/php/Editor/_notes/dwsync.xml b/php/Editor/_notes/dwsync.xml deleted file mode 100644 index 383b0a8..0000000 --- a/php/Editor/_notes/dwsync.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/php/Ext/Ext.php b/php/Ext/Ext.php deleted file mode 100644 index 256e8be..0000000 --- a/php/Ext/Ext.php +++ /dev/null @@ -1,214 +0,0 @@ -newInstanceArgs( func_get_args() ); - } - - /** - * Static method to instantiate a new instance of a class (shorthand of - * 'instantiate'). - * - * This method performs exactly the same actions as the 'instantiate' - * static method, but is simply shorter and easier to type! - * @return \DataTables\Editor|\DataTables\Editor\Field|\DataTables\Editor\Join|\DataTables\Editor\Upload class - * @static - */ - public static function inst () - { - $rc = new \ReflectionClass( get_called_class() ); - return $rc->newInstanceArgs( func_get_args() ); - } - - /** - * Common getter / setter function for DataTables classes. - * - * This getter / setter method makes building getter / setting methods - * easier, by abstracting everything to a single function call. - * @param mixed &$prop The property to set - * @param mixed $val The value to set - if given as null, then we assume - * that the function is being used as a getter. - * @param boolean $array Treat the target property as an array or not - * (default false). If used as an array, then values passed in are added - * to the $prop array. - * @return self|* Class instance if setting (allowing chaining), or - * the value requested if getting. - */ - protected function _getSet( &$prop, $val, $array=false ) - { - // Get - if ( $val === null ) { - return $prop; - } - - // Set - if ( $array ) { - // Property is an array, merge or add to array - is_array( $val ) ? - $prop = array_merge( $prop, $val ) : - $prop[] = $val; - } - else { - // Property is just a value - $prop = $val; - } - - return $this; - } - - /** - * Determine if a property is available in a data set (allowing `null` to be - * a valid value) - * @param string $name Javascript dotted object name to write to - * @param array $data Data source array to read from - * @return boolean true if present, false otherwise - * @private - */ - protected function _propExists ( $name, $data ) - { - if ( strpos($name, '.') === false ) { - return isset( $data[ $name ] ); - } - - $names = explode( '.', $name ); - $inner = $data; - - for ( $i=0 ; $i - - - \ No newline at end of file diff --git a/php/Vendor/Htmlaw.php b/php/Vendor/Htmlaw.php deleted file mode 100644 index e1cc74e..0000000 --- a/php/Vendor/Htmlaw.php +++ /dev/null @@ -1,114 +0,0 @@ -xss()` method. The Vanilla forums wrapper is used - * to provide sensible defaults and a clean interface for HtmLawed. - * - * Changes: - * - * * Add `DataTables/Vendor` namespace to this and htmLawed - this is to ensure - * that if htmLawed is included by any other aspect of the site it will not - * result in a conflict. - * * Use the OOP version of htmLawed (required a single updated to call it) to - * make the namespacing relatively easy. - * * Change the name of the Vanilla class so it don't conflict with the - * htmLawed OOP class - * * Update all `htmLawed::` references to `\DataTables\Vendor\htmLawed::` in - * the htmLawed file (to allow callbacks to operate correctly) - * * Updated Vanilla wrapper to operate on PHP 5.3 - * - * HtmLawed: - * http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/ - * Copyright: Santosh Patnaik - * License: Dual licensed with LGPL 3 and GPL 2+ - * - * Vanilla wrapper for HtmLawed: - * https://github.com/vanilla/htmlawed/ - * Author: Todd Burry - * Copyright: 2009-2014 Vanilla Forums Inc. - * License: LGPL-3.0 - */ - -namespace DataTables\Vendor; - -/** - * A class wrapper for the htmLawed library. - */ -class Htmlaw { - /// Methods /// - - public static $defaultConfig = array( - 'anti_link_spam' => array('`.`', ''), - 'comment' => 1, - 'cdata' => 3, - 'css_expression' => 1, - 'deny_attribute' => 'on*', - 'unique_ids' => 0, - 'elements' => '*-applet-form-input-textarea-iframe-script-style-embed-object', - 'keep_bad' => 1, - 'schemes' => 'classid:clsid; href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: nil; *:file, http, https', // clsid allowed in class - 'valid_xhtml' => 0, - 'direct_list_nest' => 1, - 'balance' => 1 - ); - - public static $defaultSpec = array( - 'object=-classid-type, -codebase', - 'embed=type(oneof=application/x-shockwave-flash)' - ); - - /** - * Filters a string of html with the htmLawed library. - * - * @param string $html The text to filter. - * @param array|null $config Config settings for the array. - * @param string|array|null $spec A specification to further limit the allowed attribute values in the html. - * @return string Returns the filtered html. - * @see http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm - */ - public static function filter($html, array $config = null, $spec = null) { - require_once __DIR__.'/htmLawed/htmLawed.php'; - - if ($config === null) { - $config = self::$defaultConfig; - } - - if (isset($config['spec']) && !$spec) { - $spec = $config['spec']; - } - - if ($spec === null) { - $spec = static::$defaultSpec; - } - - return htmLawed::hl($html, $config, $spec); - } - - - /** - * Filter a string of html so that it can be put into an rss feed. - * - * @param $html The html text to fitlter. - * @return string Returns the filtered html. - * @see Htmlawed::filter(). - */ - public static function filterRSS($html) { - $config = array( - 'anti_link_spam' => array('`.`', ''), - 'comment' => 1, - 'cdata' => 3, - 'css_expression' => 1, - 'deny_attribute' => 'on*,style,class', - 'elements' => '*-applet-form-input-textarea-iframe-script-style-object-embed-comment-link-listing-meta-noscript-plaintext-xmp', - 'keep_bad' => 0, - 'schemes' => 'classid:clsid; href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: nil; *:file, http, https', // clsid allowed in class - 'valid_xml' => 2, - 'balance' => 1 - ); - $spec = static::$defaultSpec; - - $result = static::filter($html, $config, $spec); - - return $result; - } -} diff --git a/php/Vendor/_notes/dwsync.xml b/php/Vendor/_notes/dwsync.xml deleted file mode 100644 index 6e2528c..0000000 --- a/php/Vendor/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/php/Vendor/htmLawed/_notes/dwsync.xml b/php/Vendor/htmLawed/_notes/dwsync.xml deleted file mode 100644 index 16e715f..0000000 --- a/php/Vendor/htmLawed/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/php/Vendor/htmLawed/htmLawed.php b/php/Vendor/htmLawed/htmLawed.php deleted file mode 100644 index 0fd57ed..0000000 --- a/php/Vendor/htmLawed/htmLawed.php +++ /dev/null @@ -1,737 +0,0 @@ -1, 'abbr'=>1, 'acronym'=>1, 'address'=>1, 'applet'=>1, 'area'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'blockquote'=>1, 'br'=>1, 'button'=>1, 'caption'=>1, 'center'=>1, 'cite'=>1, 'code'=>1, 'col'=>1, 'colgroup'=>1, 'dd'=>1, 'del'=>1, 'dfn'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'dt'=>1, 'em'=>1, 'embed'=>1, 'fieldset'=>1, 'font'=>1, 'form'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'i'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'ins'=>1, 'isindex'=>1, 'kbd'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'object'=>1, 'ol'=>1, 'optgroup'=>1, 'option'=>1, 'p'=>1, 'param'=>1, 'pre'=>1, 'q'=>1, 'rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1, 'ruby'=>1, 's'=>1, 'samp'=>1, 'script'=>1, 'select'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'table'=>1, 'tbody'=>1, 'td'=>1, 'textarea'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1, 'tt'=>1, 'u'=>1, 'ul'=>1, 'var'=>1); // 86/deprecated+embed+ruby -if(!empty($C['safe'])){ - unset($e['applet'], $e['embed'], $e['iframe'], $e['object'], $e['script']); -} -$x = !empty($C['elements']) ? str_replace(array("\n", "\r", "\t", ' '), '', $C['elements']) : '*'; -if($x == '-*'){$e = array();} -elseif(strpos($x, '*') === false){$e = array_flip(explode(',', $x));} -else{ - if(isset($x[1])){ - preg_match_all('`(?:^|-|\+)[^\-+]+?(?=-|\+|$)`', $x, $m, PREG_SET_ORDER); - for($i=count($m); --$i>=0;){$m[$i] = $m[$i][0];} - foreach($m as $v){ - if($v[0] == '+'){$e[substr($v, 1)] = 1;} - if($v[0] == '-' && isset($e[($v = substr($v, 1))]) && !in_array('+'. $v, $m)){unset($e[$v]);} - } - } -} -$C['elements'] =& $e; -// config attrs -$x = !empty($C['deny_attribute']) ? str_replace(array("\n", "\r", "\t", ' '), '', $C['deny_attribute']) : ''; -$x = array_flip((isset($x[0]) && $x[0] == '*') ? explode('-', $x) : explode(',', $x. (!empty($C['safe']) ? ',on*' : ''))); -if(isset($x['on*'])){ - unset($x['on*']); - $x += array('onblur'=>1, 'onchange'=>1, 'onclick'=>1, 'ondblclick'=>1, 'onfocus'=>1, 'onkeydown'=>1, 'onkeypress'=>1, 'onkeyup'=>1, 'onmousedown'=>1, 'onmousemove'=>1, 'onmouseout'=>1, 'onmouseover'=>1, 'onmouseup'=>1, 'onreset'=>1, 'onselect'=>1, 'onsubmit'=>1); -} -$C['deny_attribute'] = $x; -// config URL -$x = (isset($C['schemes'][2]) && strpos($C['schemes'], ':')) ? strtolower($C['schemes']) : 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https'; -$C['schemes'] = array(); -foreach(explode(';', str_replace(array(' ', "\t", "\r", "\n"), '', $x)) as $v){ - $x = $x2 = null; list($x, $x2) = explode(':', $v, 2); - if($x2){$C['schemes'][$x] = array_flip(explode(',', $x2));} -} -if(!isset($C['schemes']['*'])){$C['schemes']['*'] = array('file'=>1, 'http'=>1, 'https'=>1,);} -if(!empty($C['safe']) && empty($C['schemes']['style'])){$C['schemes']['style'] = array('!'=>1);} -$C['abs_url'] = isset($C['abs_url']) ? $C['abs_url'] : 0; -if(!isset($C['base_url']) or !preg_match('`^[a-zA-Z\d.+\-]+://[^/]+/(.+?/)?$`', $C['base_url'])){ - $C['base_url'] = $C['abs_url'] = 0; -} -// config rest -$C['and_mark'] = empty($C['and_mark']) ? 0 : 1; -$C['anti_link_spam'] = (isset($C['anti_link_spam']) && is_array($C['anti_link_spam']) && count($C['anti_link_spam']) == 2 && (empty($C['anti_link_spam'][0]) or \DataTables\Vendor\htmLawed::hl_regex($C['anti_link_spam'][0])) && (empty($C['anti_link_spam'][1]) or \DataTables\Vendor\htmLawed::hl_regex($C['anti_link_spam'][1]))) ? $C['anti_link_spam'] : 0; -$C['anti_mail_spam'] = isset($C['anti_mail_spam']) ? $C['anti_mail_spam'] : 0; -$C['balance'] = isset($C['balance']) ? (bool)$C['balance'] : 1; -$C['cdata'] = isset($C['cdata']) ? $C['cdata'] : (empty($C['safe']) ? 3 : 0); -$C['clean_ms_char'] = empty($C['clean_ms_char']) ? 0 : $C['clean_ms_char']; -$C['comment'] = isset($C['comment']) ? $C['comment'] : (empty($C['safe']) ? 3 : 0); -$C['css_expression'] = empty($C['css_expression']) ? 0 : 1; -$C['direct_list_nest'] = empty($C['direct_list_nest']) ? 0 : 1; -$C['hexdec_entity'] = isset($C['hexdec_entity']) ? $C['hexdec_entity'] : 1; -$C['hook'] = (!empty($C['hook']) && function_exists($C['hook'])) ? $C['hook'] : 0; -$C['hook_tag'] = (!empty($C['hook_tag']) && function_exists($C['hook_tag'])) ? $C['hook_tag'] : 0; -$C['keep_bad'] = isset($C['keep_bad']) ? $C['keep_bad'] : 6; -$C['lc_std_val'] = isset($C['lc_std_val']) ? (bool)$C['lc_std_val'] : 1; -$C['make_tag_strict'] = isset($C['make_tag_strict']) ? $C['make_tag_strict'] : 1; -$C['named_entity'] = isset($C['named_entity']) ? (bool)$C['named_entity'] : 1; -$C['no_deprecated_attr'] = isset($C['no_deprecated_attr']) ? $C['no_deprecated_attr'] : 1; -$C['parent'] = isset($C['parent'][0]) ? strtolower($C['parent']) : 'body'; -$C['show_setting'] = !empty($C['show_setting']) ? $C['show_setting'] : 0; -$C['style_pass'] = empty($C['style_pass']) ? 0 : 1; -$C['tidy'] = empty($C['tidy']) ? 0 : $C['tidy']; -$C['unique_ids'] = isset($C['unique_ids']) ? $C['unique_ids'] : 1; -$C['xml:lang'] = isset($C['xml:lang']) ? $C['xml:lang'] : 0; - -if(isset($GLOBALS['C'])){$reC = $GLOBALS['C'];} -$GLOBALS['C'] = $C; -$S = is_array($S) ? $S : \DataTables\Vendor\htmLawed::hl_spec($S); -if(isset($GLOBALS['S'])){$reS = $GLOBALS['S'];} -$GLOBALS['S'] = $S; - -$t = preg_replace('`[\x00-\x08\x0b-\x0c\x0e-\x1f]`', '', $t); -if($C['clean_ms_char']){ - $x = array("\x7f"=>'', "\x80"=>'€', "\x81"=>'', "\x83"=>'ƒ', "\x85"=>'…', "\x86"=>'†', "\x87"=>'‡', "\x88"=>'ˆ', "\x89"=>'‰', "\x8a"=>'Š', "\x8b"=>'‹', "\x8c"=>'Œ', "\x8d"=>'', "\x8e"=>'Ž', "\x8f"=>'', "\x90"=>'', "\x95"=>'•', "\x96"=>'–', "\x97"=>'—', "\x98"=>'˜', "\x99"=>'™', "\x9a"=>'š', "\x9b"=>'›', "\x9c"=>'œ', "\x9d"=>'', "\x9e"=>'ž', "\x9f"=>'Ÿ'); - $x = $x + ($C['clean_ms_char'] == 1 ? array("\x82"=>'‚', "\x84"=>'„', "\x91"=>'‘', "\x92"=>'’', "\x93"=>'“', "\x94"=>'”') : array("\x82"=>'\'', "\x84"=>'"', "\x91"=>'\'', "\x92"=>'\'', "\x93"=>'"', "\x94"=>'"')); - $t = strtr($t, $x); -} -if($C['cdata'] or $C['comment']){$t = preg_replace_callback('``sm', '\DataTables\Vendor\htmLawed::hl_cmtcd', $t);} -$t = preg_replace_callback('`&([A-Za-z][A-Za-z0-9]{1,30}|#(?:[0-9]{1,8}|[Xx][0-9A-Fa-f]{1,7}));`', '\DataTables\Vendor\htmLawed::hl_ent', str_replace('&', '&', $t)); -if($C['unique_ids'] && !isset($GLOBALS['hl_Ids'])){$GLOBALS['hl_Ids'] = array();} -if($C['hook']){$t = $C['hook']($t, $C, $S);} -if($C['show_setting'] && preg_match('`^[a-z][a-z0-9_]*$`i', $C['show_setting'])){ - $GLOBALS[$C['show_setting']] = array('config'=>$C, 'spec'=>$S, 'time'=>microtime()); -} -// main -$t = preg_replace_callback('`<(?:(?:\s|$)|(?:[^>]*(?:>|$)))|>`m', '\DataTables\Vendor\htmLawed::hl_tag', $t); -$t = $C['balance'] ? \DataTables\Vendor\htmLawed::hl_bal($t, $C['keep_bad'], $C['parent']) : $t; -$t = (($C['cdata'] or $C['comment']) && strpos($t, "\x01") !== false) ? str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05"), array('', '', '&', '<', '>'), $t) : $t; -$t = $C['tidy'] ? \DataTables\Vendor\htmLawed::hl_tidy($t, $C['tidy'], $C['parent']) : $t; -unset($C, $e); -if(isset($reC)){$GLOBALS['C'] = $reC;} -if(isset($reS)){$GLOBALS['S'] = $reS;} -return $t; -// eof -} - -public static function hl_attrval($a, $t, $p){ -// check attr val against $S -static $ma = array('accesskey', 'class', 'rel'); -$s = in_array($a, $ma) ? ' ' : ''; -$r = array(); -$t = !empty($s) ? explode($s, $t) : array($t); -foreach($t as $tk=>$tv){ - $o = 1; $l = strlen($tv); - foreach($p as $k=>$v){ - switch($k){ - case 'maxlen': if($l > $v){$o = 0;} - break; case 'minlen': if($l < $v){$o = 0;} - break; case 'maxval': if((float)($tv) > $v){$o = 0;} - break; case 'minval': if((float)($tv) < $v){$o = 0;} - break; case 'match': if(!preg_match($v, $tv)){$o = 0;} - break; case 'nomatch': if(preg_match($v, $tv)){$o = 0;} - break; case 'oneof': - $m = 0; - foreach(explode('|', $v) as $n){if($tv == $n){$m = 1; break;}} - $o = $m; - break; case 'noneof': - $m = 1; - foreach(explode('|', $v) as $n){if($tv == $n){$m = 0; break;}} - $o = $m; - break; default: - break; - } - if(!$o){break;} - } - if($o){$r[] = $tv;} -} -$r = implode($s, $r); -return (isset($r[0]) ? $r : (isset($p['default']) ? $p['default'] : 0)); -// eof -} - -public static function hl_bal($t, $do=1, $in='div'){ -// balance tags -// by content -$cB = array('blockquote'=>1, 'form'=>1, 'map'=>1, 'noscript'=>1); // Block -$cE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1); // Empty -$cF = array('button'=>1, 'del'=>1, 'div'=>1, 'dd'=>1, 'fieldset'=>1, 'iframe'=>1, 'ins'=>1, 'li'=>1, 'noscript'=>1, 'object'=>1, 'td'=>1, 'th'=>1); // Flow; later context-wise dynamic move of ins & del to $cI -$cI = array('a'=>1, 'abbr'=>1, 'acronym'=>1, 'address'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'caption'=>1, 'cite'=>1, 'code'=>1, 'dfn'=>1, 'dt'=>1, 'em'=>1, 'font'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'i'=>1, 'kbd'=>1, 'label'=>1, 'legend'=>1, 'p'=>1, 'pre'=>1, 'q'=>1, 'rb'=>1, 'rt'=>1, 's'=>1, 'samp'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'tt'=>1, 'u'=>1, 'var'=>1); // Inline -$cN = array('a'=>array('a'=>1), 'button'=>array('a'=>1, 'button'=>1, 'fieldset'=>1, 'form'=>1, 'iframe'=>1, 'input'=>1, 'label'=>1, 'select'=>1, 'textarea'=>1), 'fieldset'=>array('fieldset'=>1), 'form'=>array('form'=>1), 'label'=>array('label'=>1), 'noscript'=>array('script'=>1), 'pre'=>array('big'=>1, 'font'=>1, 'img'=>1, 'object'=>1, 'script'=>1, 'small'=>1, 'sub'=>1, 'sup'=>1), 'rb'=>array('ruby'=>1), 'rt'=>array('ruby'=>1)); // Illegal -$cN2 = array_keys($cN); -$cR = array('blockquote'=>1, 'dir'=>1, 'dl'=>1, 'form'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1); -$cS = array('colgroup'=>array('col'=>1), 'dir'=>array('li'=>1), 'dl'=>array('dd'=>1, 'dt'=>1), 'menu'=>array('li'=>1), 'ol'=>array('li'=>1), 'optgroup'=>array('option'=>1), 'option'=>array('#pcdata'=>1), 'rbc'=>array('rb'=>1), 'rp'=>array('#pcdata'=>1), 'rtc'=>array('rt'=>1), 'ruby'=>array('rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1), 'select'=>array('optgroup'=>1, 'option'=>1), 'script'=>array('#pcdata'=>1), 'table'=>array('caption'=>1, 'col'=>1, 'colgroup'=>1, 'tfoot'=>1, 'tbody'=>1, 'tr'=>1, 'thead'=>1), 'tbody'=>array('tr'=>1), 'tfoot'=>array('tr'=>1), 'textarea'=>array('#pcdata'=>1), 'thead'=>array('tr'=>1), 'tr'=>array('td'=>1, 'th'=>1), 'ul'=>array('li'=>1)); // Specific - immediate parent-child -if($GLOBALS['C']['direct_list_nest']){$cS['ol'] = $cS['ul'] += array('ol'=>1, 'ul'=>1);} -$cO = array('address'=>array('p'=>1), 'applet'=>array('param'=>1), 'blockquote'=>array('script'=>1), 'fieldset'=>array('legend'=>1, '#pcdata'=>1), 'form'=>array('script'=>1), 'map'=>array('area'=>1), 'object'=>array('param'=>1, 'embed'=>1)); // Other -$cT = array('colgroup'=>1, 'dd'=>1, 'dt'=>1, 'li'=>1, 'option'=>1, 'p'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1); // Omitable closing -// block/inline type; ins & del both type; #pcdata: text -$eB = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'del'=>1, 'dir'=>1, 'dl'=>1, 'div'=>1, 'fieldset'=>1, 'form'=>1, 'ins'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'isindex'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'p'=>1, 'pre'=>1, 'table'=>1, 'ul'=>1); -$eI = array('#pcdata'=>1, 'a'=>1, 'abbr'=>1, 'acronym'=>1, 'applet'=>1, 'b'=>1, 'bdo'=>1, 'big'=>1, 'br'=>1, 'button'=>1, 'cite'=>1, 'code'=>1, 'del'=>1, 'dfn'=>1, 'em'=>1, 'embed'=>1, 'font'=>1, 'i'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'ins'=>1, 'kbd'=>1, 'label'=>1, 'map'=>1, 'object'=>1, 'q'=>1, 'ruby'=>1, 's'=>1, 'samp'=>1, 'select'=>1, 'script'=>1, 'small'=>1, 'span'=>1, 'strike'=>1, 'strong'=>1, 'sub'=>1, 'sup'=>1, 'textarea'=>1, 'tt'=>1, 'u'=>1, 'var'=>1); -$eN = array('a'=>1, 'big'=>1, 'button'=>1, 'fieldset'=>1, 'font'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'label'=>1, 'object'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'small'=>1, 'sub'=>1, 'sup'=>1, 'textarea'=>1); // Exclude from specific ele; $cN values -$eO = array('area'=>1, 'caption'=>1, 'col'=>1, 'colgroup'=>1, 'dd'=>1, 'dt'=>1, 'legend'=>1, 'li'=>1, 'optgroup'=>1, 'option'=>1, 'param'=>1, 'rb'=>1, 'rbc'=>1, 'rp'=>1, 'rt'=>1, 'rtc'=>1, 'script'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'thead'=>1, 'th'=>1, 'tr'=>1); // Missing in $eB & $eI -$eF = $eB + $eI; - -// $in sets allowed child -$in = ((isset($eF[$in]) && $in != '#pcdata') or isset($eO[$in])) ? $in : 'div'; -if(isset($cE[$in])){ - return (!$do ? '' : str_replace(array('<', '>'), array('<', '>'), $t)); -} -if(isset($cS[$in])){$inOk = $cS[$in];} -elseif(isset($cI[$in])){$inOk = $eI; $cI['del'] = 1; $cI['ins'] = 1;} -elseif(isset($cF[$in])){$inOk = $eF; unset($cI['del'], $cI['ins']);} -elseif(isset($cB[$in])){$inOk = $eB; unset($cI['del'], $cI['ins']);} -if(isset($cO[$in])){$inOk = $inOk + $cO[$in];} -if(isset($cN[$in])){$inOk = array_diff_assoc($inOk, $cN[$in]);} - -$t = explode('<', $t); -$ok = $q = array(); // $q seq list of open non-empty ele -ob_start(); - -for($i=-1, $ci=count($t); ++$i<$ci;){ - // allowed $ok in parent $p - if($ql = count($q)){ - $p = array_pop($q); - $q[] = $p; - if(isset($cS[$p])){$ok = $cS[$p];} - elseif(isset($cI[$p])){$ok = $eI; $cI['del'] = 1; $cI['ins'] = 1;} - elseif(isset($cF[$p])){$ok = $eF; unset($cI['del'], $cI['ins']);} - elseif(isset($cB[$p])){$ok = $eB; unset($cI['del'], $cI['ins']);} - if(isset($cO[$p])){$ok = $ok + $cO[$p];} - if(isset($cN[$p])){$ok = array_diff_assoc($ok, $cN[$p]);} - }else{$ok = $inOk; unset($cI['del'], $cI['ins']);} - // bad tags, & ele content - if(isset($e) && ($do == 1 or (isset($ok['#pcdata']) && ($do == 3 or $do == 5)))){ - echo '<', $s, $e, $a, '>'; - } - if(isset($x[0])){ - if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){ - echo '
', $x, '
'; - } - elseif($do < 3 or isset($ok['#pcdata'])){echo $x;} - elseif(strpos($x, "\x02\x04")){ - foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){ - echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : '')); - } - }elseif($do > 4){echo preg_replace('`\S`', '', $x);} - } - // get markup - if(!preg_match('`^(/?)([a-z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;} - $s = null; $e = null; $a = null; $x = null; list($all, $s, $e, $a, $x) = $r; - // close tag - if($s){ - if(isset($cE[$e]) or !in_array($e, $q)){continue;} // Empty/unopen - if($p == $e){array_pop($q); echo ''; unset($e); continue;} // Last open - $add = ''; // Nesting - close open tags that need to be - for($j=-1, $cj=count($q); ++$j<$cj;){ - if(($d = array_pop($q)) == $e){break;} - else{$add .= "";} - } - echo $add, ''; unset($e); continue; - } - // open tag - // $cB ele needs $eB ele as child - if(isset($cB[$e]) && strlen(trim($x))){ - $t[$i] = "{$e}{$a}>"; - array_splice($t, $i+1, 0, 'div>'. $x); unset($e, $x); ++$ci; --$i; continue; - } - if((($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql)) && !isset($eB[$e]) && !isset($ok[$e])){ - array_splice($t, $i, 0, 'div>'); unset($e, $x); ++$ci; --$i; continue; - } - // if no open ele, $in = parent; mostly immediate parent-child relation should hold - if(!$ql or !isset($eN[$e]) or !array_intersect($q, $cN2)){ - if(!isset($ok[$e])){ - if($ql && isset($cT[$p])){echo ''; unset($e, $x); --$i;} - continue; - } - if(!isset($cE[$e])){$q[] = $e;} - echo '<', $e, $a, '>'; unset($e); continue; - } - // specific parent-child - if(isset($cS[$p][$e])){ - if(!isset($cE[$e])){$q[] = $e;} - echo '<', $e, $a, '>'; unset($e); continue; - } - // nesting - $add = ''; - $q2 = array(); - for($k=-1, $kc=count($q); ++$k<$kc;){ - $d = $q[$k]; - $ok2 = array(); - if(isset($cS[$d])){$q2[] = $d; continue;} - $ok2 = isset($cI[$d]) ? $eI : $eF; - if(isset($cO[$d])){$ok2 = $ok2 + $cO[$d];} - if(isset($cN[$d])){$ok2 = array_diff_assoc($ok2, $cN[$d]);} - if(!isset($ok2[$e])){ - if(!$k && !isset($inOk[$e])){continue 2;} - $add = ""; - for(;++$k<$kc;){$add = "{$add}";} - break; - } - else{$q2[] = $d;} - } - $q = $q2; - if(!isset($cE[$e])){$q[] = $e;} - echo $add, '<', $e, $a, '>'; unset($e); continue; -} - -// end -if($ql = count($q)){ - $p = array_pop($q); - $q[] = $p; - if(isset($cS[$p])){$ok = $cS[$p];} - elseif(isset($cI[$p])){$ok = $eI; $cI['del'] = 1; $cI['ins'] = 1;} - elseif(isset($cF[$p])){$ok = $eF; unset($cI['del'], $cI['ins']);} - elseif(isset($cB[$p])){$ok = $eB; unset($cI['del'], $cI['ins']);} - if(isset($cO[$p])){$ok = $ok + $cO[$p];} - if(isset($cN[$p])){$ok = array_diff_assoc($ok, $cN[$p]);} -}else{$ok = $inOk; unset($cI['del'], $cI['ins']);} -if(isset($e) && ($do == 1 or (isset($ok['#pcdata']) && ($do == 3 or $do == 5)))){ - echo '<', $s, $e, $a, '>'; -} -if(isset($x[0])){ - if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){ - echo '
', $x, '
'; - } - elseif($do < 3 or isset($ok['#pcdata'])){echo $x;} - elseif(strpos($x, "\x02\x04")){ - foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){ - echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : '')); - } - }elseif($do > 4){echo preg_replace('`\S`', '', $x);} -} -while(!empty($q) && ($e = array_pop($q))){echo '';} -$o = ob_get_contents(); -ob_end_clean(); -return $o; -// eof -} - -public static function hl_cmtcd($t){ -// comment/CDATA sec handler -$t = $t[0]; -global $C; -if(!($v = $C[$n = $t[3] == '-' ? 'comment' : 'cdata'])){return $t;} -if($v == 1){return '';} -if($n == 'comment'){ - if(substr(($t = preg_replace('`--+`', '-', substr($t, 4, -3))), -1) != ' '){$t .= ' ';} -} -else{$t = substr($t, 1, -1);} -$t = $v == 2 ? str_replace(array('&', '<', '>'), array('&', '<', '>'), $t) : $t; -return str_replace(array('&', '<', '>'), array("\x03", "\x04", "\x05"), ($n == 'comment' ? "\x01\x02\x04!--$t--\x05\x02\x01" : "\x01\x01\x04$t\x05\x01\x01")); -// eof -} - -public static function hl_ent($t){ -// entitity handler -global $C; -$t = $t[1]; -static $U = array('quot'=>1,'amp'=>1,'lt'=>1,'gt'=>1); -static $N = array('fnof'=>'402', 'Alpha'=>'913', 'Beta'=>'914', 'Gamma'=>'915', 'Delta'=>'916', 'Epsilon'=>'917', 'Zeta'=>'918', 'Eta'=>'919', 'Theta'=>'920', 'Iota'=>'921', 'Kappa'=>'922', 'Lambda'=>'923', 'Mu'=>'924', 'Nu'=>'925', 'Xi'=>'926', 'Omicron'=>'927', 'Pi'=>'928', 'Rho'=>'929', 'Sigma'=>'931', 'Tau'=>'932', 'Upsilon'=>'933', 'Phi'=>'934', 'Chi'=>'935', 'Psi'=>'936', 'Omega'=>'937', 'alpha'=>'945', 'beta'=>'946', 'gamma'=>'947', 'delta'=>'948', 'epsilon'=>'949', 'zeta'=>'950', 'eta'=>'951', 'theta'=>'952', 'iota'=>'953', 'kappa'=>'954', 'lambda'=>'955', 'mu'=>'956', 'nu'=>'957', 'xi'=>'958', 'omicron'=>'959', 'pi'=>'960', 'rho'=>'961', 'sigmaf'=>'962', 'sigma'=>'963', 'tau'=>'964', 'upsilon'=>'965', 'phi'=>'966', 'chi'=>'967', 'psi'=>'968', 'omega'=>'969', 'thetasym'=>'977', 'upsih'=>'978', 'piv'=>'982', 'bull'=>'8226', 'hellip'=>'8230', 'prime'=>'8242', 'Prime'=>'8243', 'oline'=>'8254', 'frasl'=>'8260', 'weierp'=>'8472', 'image'=>'8465', 'real'=>'8476', 'trade'=>'8482', 'alefsym'=>'8501', 'larr'=>'8592', 'uarr'=>'8593', 'rarr'=>'8594', 'darr'=>'8595', 'harr'=>'8596', 'crarr'=>'8629', 'lArr'=>'8656', 'uArr'=>'8657', 'rArr'=>'8658', 'dArr'=>'8659', 'hArr'=>'8660', 'forall'=>'8704', 'part'=>'8706', 'exist'=>'8707', 'empty'=>'8709', 'nabla'=>'8711', 'isin'=>'8712', 'notin'=>'8713', 'ni'=>'8715', 'prod'=>'8719', 'sum'=>'8721', 'minus'=>'8722', 'lowast'=>'8727', 'radic'=>'8730', 'prop'=>'8733', 'infin'=>'8734', 'ang'=>'8736', 'and'=>'8743', 'or'=>'8744', 'cap'=>'8745', 'cup'=>'8746', 'int'=>'8747', 'there4'=>'8756', 'sim'=>'8764', 'cong'=>'8773', 'asymp'=>'8776', 'ne'=>'8800', 'equiv'=>'8801', 'le'=>'8804', 'ge'=>'8805', 'sub'=>'8834', 'sup'=>'8835', 'nsub'=>'8836', 'sube'=>'8838', 'supe'=>'8839', 'oplus'=>'8853', 'otimes'=>'8855', 'perp'=>'8869', 'sdot'=>'8901', 'lceil'=>'8968', 'rceil'=>'8969', 'lfloor'=>'8970', 'rfloor'=>'8971', 'lang'=>'9001', 'rang'=>'9002', 'loz'=>'9674', 'spades'=>'9824', 'clubs'=>'9827', 'hearts'=>'9829', 'diams'=>'9830', 'apos'=>'39', 'OElig'=>'338', 'oelig'=>'339', 'Scaron'=>'352', 'scaron'=>'353', 'Yuml'=>'376', 'circ'=>'710', 'tilde'=>'732', 'ensp'=>'8194', 'emsp'=>'8195', 'thinsp'=>'8201', 'zwnj'=>'8204', 'zwj'=>'8205', 'lrm'=>'8206', 'rlm'=>'8207', 'ndash'=>'8211', 'mdash'=>'8212', 'lsquo'=>'8216', 'rsquo'=>'8217', 'sbquo'=>'8218', 'ldquo'=>'8220', 'rdquo'=>'8221', 'bdquo'=>'8222', 'dagger'=>'8224', 'Dagger'=>'8225', 'permil'=>'8240', 'lsaquo'=>'8249', 'rsaquo'=>'8250', 'euro'=>'8364', 'nbsp'=>'160', 'iexcl'=>'161', 'cent'=>'162', 'pound'=>'163', 'curren'=>'164', 'yen'=>'165', 'brvbar'=>'166', 'sect'=>'167', 'uml'=>'168', 'copy'=>'169', 'ordf'=>'170', 'laquo'=>'171', 'not'=>'172', 'shy'=>'173', 'reg'=>'174', 'macr'=>'175', 'deg'=>'176', 'plusmn'=>'177', 'sup2'=>'178', 'sup3'=>'179', 'acute'=>'180', 'micro'=>'181', 'para'=>'182', 'middot'=>'183', 'cedil'=>'184', 'sup1'=>'185', 'ordm'=>'186', 'raquo'=>'187', 'frac14'=>'188', 'frac12'=>'189', 'frac34'=>'190', 'iquest'=>'191', 'Agrave'=>'192', 'Aacute'=>'193', 'Acirc'=>'194', 'Atilde'=>'195', 'Auml'=>'196', 'Aring'=>'197', 'AElig'=>'198', 'Ccedil'=>'199', 'Egrave'=>'200', 'Eacute'=>'201', 'Ecirc'=>'202', 'Euml'=>'203', 'Igrave'=>'204', 'Iacute'=>'205', 'Icirc'=>'206', 'Iuml'=>'207', 'ETH'=>'208', 'Ntilde'=>'209', 'Ograve'=>'210', 'Oacute'=>'211', 'Ocirc'=>'212', 'Otilde'=>'213', 'Ouml'=>'214', 'times'=>'215', 'Oslash'=>'216', 'Ugrave'=>'217', 'Uacute'=>'218', 'Ucirc'=>'219', 'Uuml'=>'220', 'Yacute'=>'221', 'THORN'=>'222', 'szlig'=>'223', 'agrave'=>'224', 'aacute'=>'225', 'acirc'=>'226', 'atilde'=>'227', 'auml'=>'228', 'aring'=>'229', 'aelig'=>'230', 'ccedil'=>'231', 'egrave'=>'232', 'eacute'=>'233', 'ecirc'=>'234', 'euml'=>'235', 'igrave'=>'236', 'iacute'=>'237', 'icirc'=>'238', 'iuml'=>'239', 'eth'=>'240', 'ntilde'=>'241', 'ograve'=>'242', 'oacute'=>'243', 'ocirc'=>'244', 'otilde'=>'245', 'ouml'=>'246', 'divide'=>'247', 'oslash'=>'248', 'ugrave'=>'249', 'uacute'=>'250', 'ucirc'=>'251', 'uuml'=>'252', 'yacute'=>'253', 'thorn'=>'254', 'yuml'=>'255'); -if($t[0] != '#'){ - return ($C['and_mark'] ? "\x06" : '&'). (isset($U[$t]) ? $t : (isset($N[$t]) ? (!$C['named_entity'] ? '#'. ($C['hexdec_entity'] > 1 ? 'x'. dechex($N[$t]) : $N[$t]) : $t) : 'amp;'. $t)). ';'; -} -if(($n = ctype_digit($t = substr($t, 1)) ? intval($t) : hexdec(substr($t, 1))) < 9 or ($n > 13 && $n < 32) or $n == 11 or $n == 12 or ($n > 126 && $n < 160 && $n != 133) or ($n > 55295 && ($n < 57344 or ($n > 64975 && $n < 64992) or $n == 65534 or $n == 65535 or $n > 1114111))){ - return ($C['and_mark'] ? "\x06" : '&'). "amp;#{$t};"; -} -return ($C['and_mark'] ? "\x06" : '&'). '#'. (((ctype_digit($t) && $C['hexdec_entity'] < 2) or !$C['hexdec_entity']) ? $n : 'x'. dechex($n)). ';'; -// eof -} - -public static function hl_prot($p, $c=null){ -// check URL scheme -global $C; -$b = $a = ''; -if($c == null){$c = 'style'; $b = $p[1]; $a = $p[3]; $p = trim($p[2]);} -$c = isset($C['schemes'][$c]) ? $C['schemes'][$c] : $C['schemes']['*']; -static $d = 'denied:'; -if(isset($c['!']) && substr($p, 0, 7) != $d){$p = "$d$p";} -if(isset($c['*']) or !strcspn($p, '#?;') or (substr($p, 0, 7) == $d)){return "{$b}{$p}{$a}";} // All ok, frag, query, param -if(preg_match('`^([^:?[@!$()*,=/\'\]]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot - return "{$b}{$d}{$p}{$a}"; -} -if($C['abs_url']){ - if($C['abs_url'] == -1 && strpos($p, $C['base_url']) === 0){ // Make url rel - $p = substr($p, strlen($C['base_url'])); - }elseif(empty($m[1])){ // Make URL abs - if(substr($p, 0, 2) == '//'){$p = substr($C['base_url'], 0, strpos($C['base_url'], ':')+1). $p;} - elseif($p[0] == '/'){$p = preg_replace('`(^.+?://[^/]+)(.*)`', '$1', $C['base_url']). $p;} - elseif(strcspn($p, './')){$p = $C['base_url']. $p;} - else{ - preg_match('`^([a-zA-Z\d\-+.]+://[^/]+)(.*)`', $C['base_url'], $m); - $p = preg_replace('`(?<=/)\./`', '', $m[2]. $p); - while(preg_match('`(?<=/)([^/]{3,}|[^/.]+?|\.[^/.]|[^/.]\.)/\.\./`', $p)){ - $p = preg_replace('`(?<=/)([^/]{3,}|[^/.]+?|\.[^/.]|[^/.]\.)/\.\./`', '', $p); - } - $p = $m[1]. $p; - } - } -} -return "{$b}{$p}{$a}"; -// eof -} - -public static function hl_regex($p){ -// ?regex -if(empty($p)){return 0;} -if($t = ini_get('track_errors')){$o = isset($php_errormsg) ? $php_errormsg : null;} -else{ini_set('track_errors', 1);} -unset($php_errormsg); -if(($d = ini_get('display_errors'))){ini_set('display_errors', 0);} -preg_match($p, ''); -if($d){ini_set('display_errors', 1);} -$r = isset($php_errormsg) ? 0 : 1; -if($t){$php_errormsg = isset($o) ? $o : null;} -else{ini_set('track_errors', 0);} -return $r; -// eof -} - -public static function hl_spec($t){ -// final $spec -$s = array(); -$t = str_replace(array("\t", "\r", "\n", ' '), '', preg_replace_callback('/"(?>(`.|[^"])*)"/sm', create_function('$m', 'return substr(str_replace(array(";", "|", "~", " ", ",", "/", "(", ")", \'`"\'), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\""), $m[0]), 1, -1);'), trim($t))); -for($i = count(($t = explode(';', $t))); --$i>=0;){ - $w = $t[$i]; - if(empty($w) or ($e = strpos($w, '=')) === false or !strlen(($a = substr($w, $e+1)))){continue;} - $y = $n = array(); - foreach(explode(',', $a) as $v){ - if(!preg_match('`^([a-z:\-\*]+)(?:\((.*?)\))?`i', $v, $m)){continue;} - if(($x = strtolower($m[1])) == '-*'){$n['*'] = 1; continue;} - if($x[0] == '-'){$n[substr($x, 1)] = 1; continue;} - if(!isset($m[2])){$y[$x] = 1; continue;} - foreach(explode('/', $m[2]) as $m){ - if(empty($m) or ($p = strpos($m, '=')) == 0 or $p < 5){$y[$x] = 1; continue;} - $y[$x][strtolower(substr($m, 0, $p))] = str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08"), array(";", "|", "~", " ", ",", "/", "(", ")"), substr($m, $p+1)); - } - if(isset($y[$x]['match']) && !\DataTables\Vendor\htmLawed::hl_regex($y[$x]['match'])){unset($y[$x]['match']);} - if(isset($y[$x]['nomatch']) && !\DataTables\Vendor\htmLawed::hl_regex($y[$x]['nomatch'])){unset($y[$x]['nomatch']);} - } - if(!count($y) && !count($n)){continue;} - foreach(explode(',', substr($w, 0, $e)) as $v){ - if(!strlen(($v = strtolower($v)))){continue;} - if(count($y)){$s[$v] = $y;} - if(count($n)){$s[$v]['n'] = $n;} - } -} -return $s; -// eof -} - -public static function hl_tag($t){ -// tag/attribute handler -global $C; -$t = $t[0]; -// invalid < > -if($t == '< '){return '< ';} -if($t == '>'){return '>';} -if(!preg_match('`^<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>$`m', $t, $m)){ - return str_replace(array('<', '>'), array('<', '>'), $t); -}elseif(!isset($C['elements'][($e = strtolower($m[2]))])){ - return (($C['keep_bad']%2) ? str_replace(array('<', '>'), array('<', '>'), $t) : ''); -} -// attr string -$a = str_replace(array("\n", "\r", "\t"), ' ', trim($m[3])); -// tag transform -static $eD = array('applet'=>1, 'center'=>1, 'dir'=>1, 'embed'=>1, 'font'=>1, 'isindex'=>1, 'menu'=>1, 's'=>1, 'strike'=>1, 'u'=>1); // Deprecated -if($C['make_tag_strict'] && isset($eD[$e])){ - $trt = \DataTables\Vendor\htmLawed::hl_tag2($e, $a, $C['make_tag_strict']); - if(!$e){return (($C['keep_bad']%2) ? str_replace(array('<', '>'), array('<', '>'), $t) : '');} -} -// close tag -static $eE = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1); // Empty ele -if(!empty($m[1])){ - return (!isset($eE[$e]) ? (empty($C['hook_tag']) ? "" : $C['hook_tag']($e)) : (($C['keep_bad'])%2 ? str_replace(array('<', '>'), array('<', '>'), $t) : '')); -} - -// open tag & attr -static $aN = array('abbr'=>array('td'=>1, 'th'=>1), 'accept-charset'=>array('form'=>1), 'accept'=>array('form'=>1, 'input'=>1), 'accesskey'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'label'=>1, 'legend'=>1, 'textarea'=>1), 'action'=>array('form'=>1), 'align'=>array('caption'=>1, 'embed'=>1, 'applet'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'object'=>1, 'legend'=>1, 'table'=>1, 'hr'=>1, 'div'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'p'=>1, 'col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'allowfullscreen'=>array('iframe'=>1), 'alt'=>array('applet'=>1, 'area'=>1, 'img'=>1, 'input'=>1), 'archive'=>array('applet'=>1, 'object'=>1), 'axis'=>array('td'=>1, 'th'=>1), 'bgcolor'=>array('embed'=>1, 'table'=>1, 'tr'=>1, 'td'=>1, 'th'=>1), 'border'=>array('table'=>1, 'img'=>1, 'object'=>1), 'bordercolor'=>array('table'=>1, 'td'=>1, 'tr'=>1), 'cellpadding'=>array('table'=>1), 'cellspacing'=>array('table'=>1), 'char'=>array('col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'charoff'=>array('col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'charset'=>array('a'=>1, 'script'=>1), 'checked'=>array('input'=>1), 'cite'=>array('blockquote'=>1, 'q'=>1, 'del'=>1, 'ins'=>1), 'classid'=>array('object'=>1), 'clear'=>array('br'=>1), 'code'=>array('applet'=>1), 'codebase'=>array('object'=>1, 'applet'=>1), 'codetype'=>array('object'=>1), 'color'=>array('font'=>1), 'cols'=>array('textarea'=>1), 'colspan'=>array('td'=>1, 'th'=>1), 'compact'=>array('dir'=>1, 'dl'=>1, 'menu'=>1, 'ol'=>1, 'ul'=>1), 'coords'=>array('area'=>1, 'a'=>1), 'data'=>array('object'=>1), 'datetime'=>array('del'=>1, 'ins'=>1), 'declare'=>array('object'=>1), 'defer'=>array('script'=>1), 'dir'=>array('bdo'=>1), 'disabled'=>array('button'=>1, 'input'=>1, 'optgroup'=>1, 'option'=>1, 'select'=>1, 'textarea'=>1), 'enctype'=>array('form'=>1), 'face'=>array('font'=>1), 'for'=>array('label'=>1), 'frame'=>array('table'=>1), 'frameborder'=>array('iframe'=>1), 'headers'=>array('td'=>1, 'th'=>1), 'height'=>array('embed'=>1, 'iframe'=>1, 'td'=>1, 'th'=>1, 'img'=>1, 'object'=>1, 'applet'=>1), 'href'=>array('a'=>1, 'area'=>1), 'hreflang'=>array('a'=>1), 'hspace'=>array('applet'=>1, 'img'=>1, 'object'=>1), 'ismap'=>array('img'=>1, 'input'=>1), 'label'=>array('option'=>1, 'optgroup'=>1), 'language'=>array('script'=>1), 'longdesc'=>array('img'=>1, 'iframe'=>1), 'marginheight'=>array('iframe'=>1), 'marginwidth'=>array('iframe'=>1), 'maxlength'=>array('input'=>1), 'method'=>array('form'=>1), 'model'=>array('embed'=>1), 'multiple'=>array('select'=>1), 'name'=>array('button'=>1, 'embed'=>1, 'textarea'=>1, 'applet'=>1, 'select'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'a'=>1, 'input'=>1, 'object'=>1, 'map'=>1, 'param'=>1), 'nohref'=>array('area'=>1), 'noshade'=>array('hr'=>1), 'nowrap'=>array('td'=>1, 'th'=>1), 'object'=>array('applet'=>1), 'onblur'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'label'=>1, 'select'=>1, 'textarea'=>1), 'onchange'=>array('input'=>1, 'select'=>1, 'textarea'=>1), 'onfocus'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'label'=>1, 'select'=>1, 'textarea'=>1), 'onreset'=>array('form'=>1), 'onselect'=>array('input'=>1, 'textarea'=>1), 'onsubmit'=>array('form'=>1), 'pluginspage'=>array('embed'=>1), 'pluginurl'=>array('embed'=>1), 'prompt'=>array('isindex'=>1), 'readonly'=>array('textarea'=>1, 'input'=>1), 'rel'=>array('a'=>1), 'rev'=>array('a'=>1), 'rows'=>array('textarea'=>1), 'rowspan'=>array('td'=>1, 'th'=>1), 'rules'=>array('table'=>1), 'scope'=>array('td'=>1, 'th'=>1), 'scrolling'=>array('iframe'=>1), 'selected'=>array('option'=>1), 'shape'=>array('area'=>1, 'a'=>1), 'size'=>array('hr'=>1, 'font'=>1, 'input'=>1, 'select'=>1), 'span'=>array('col'=>1, 'colgroup'=>1), 'src'=>array('embed'=>1, 'script'=>1, 'input'=>1, 'iframe'=>1, 'img'=>1), 'standby'=>array('object'=>1), 'start'=>array('ol'=>1), 'summary'=>array('table'=>1), 'tabindex'=>array('a'=>1, 'area'=>1, 'button'=>1, 'input'=>1, 'object'=>1, 'select'=>1, 'textarea'=>1), 'target'=>array('a'=>1, 'area'=>1, 'form'=>1), 'type'=>array('a'=>1, 'embed'=>1, 'object'=>1, 'param'=>1, 'script'=>1, 'input'=>1, 'li'=>1, 'ol'=>1, 'ul'=>1, 'button'=>1), 'usemap'=>array('img'=>1, 'input'=>1, 'object'=>1), 'valign'=>array('col'=>1, 'colgroup'=>1, 'tbody'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1), 'value'=>array('input'=>1, 'option'=>1, 'param'=>1, 'button'=>1, 'li'=>1), 'valuetype'=>array('param'=>1), 'vspace'=>array('applet'=>1, 'img'=>1, 'object'=>1), 'width'=>array('embed'=>1, 'hr'=>1, 'iframe'=>1, 'img'=>1, 'object'=>1, 'table'=>1, 'td'=>1, 'th'=>1, 'applet'=>1, 'col'=>1, 'colgroup'=>1, 'pre'=>1), 'wmode'=>array('embed'=>1), 'xml:space'=>array('pre'=>1, 'script'=>1, 'style'=>1)); // Ele-specific -static $aNE = array('checked'=>1, 'compact'=>1, 'declare'=>1, 'defer'=>1, 'disabled'=>1, 'ismap'=>1, 'multiple'=>1, 'nohref'=>1, 'noresize'=>1, 'noshade'=>1, 'nowrap'=>1, 'readonly'=>1, 'selected'=>1); // Empty -static $aNP = array('action'=>1, 'cite'=>1, 'classid'=>1, 'codebase'=>1, 'data'=>1, 'href'=>1, 'longdesc'=>1, 'model'=>1, 'pluginspage'=>1, 'pluginurl'=>1, 'usemap'=>1); // Need scheme check; excludes style, on* & src -static $aNU = array('class'=>array('param'=>1, 'script'=>1), 'dir'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'iframe'=>1, 'param'=>1, 'script'=>1), 'id'=>array('script'=>1), 'lang'=>array('applet'=>1, 'br'=>1, 'iframe'=>1, 'param'=>1, 'script'=>1), 'xml:lang'=>array('applet'=>1, 'br'=>1, 'iframe'=>1, 'param'=>1, 'script'=>1), 'onclick'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'ondblclick'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onkeydown'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onkeypress'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onkeyup'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmousedown'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmousemove'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmouseout'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmouseover'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'onmouseup'=>array('applet'=>1, 'bdo'=>1, 'br'=>1, 'font'=>1, 'iframe'=>1, 'isindex'=>1, 'param'=>1, 'script'=>1), 'style'=>array('param'=>1, 'script'=>1), 'title'=>array('param'=>1, 'script'=>1)); // Univ & exceptions - -if($C['lc_std_val']){ - // predef attr vals for $eAL & $aNE ele - static $aNL = array('all'=>1, 'baseline'=>1, 'bottom'=>1, 'button'=>1, 'center'=>1, 'char'=>1, 'checkbox'=>1, 'circle'=>1, 'col'=>1, 'colgroup'=>1, 'cols'=>1, 'data'=>1, 'default'=>1, 'file'=>1, 'get'=>1, 'groups'=>1, 'hidden'=>1, 'image'=>1, 'justify'=>1, 'left'=>1, 'ltr'=>1, 'middle'=>1, 'none'=>1, 'object'=>1, 'password'=>1, 'poly'=>1, 'post'=>1, 'preserve'=>1, 'radio'=>1, 'rect'=>1, 'ref'=>1, 'reset'=>1, 'right'=>1, 'row'=>1, 'rowgroup'=>1, 'rows'=>1, 'rtl'=>1, 'submit'=>1, 'text'=>1, 'top'=>1); - static $eAL = array('a'=>1, 'area'=>1, 'bdo'=>1, 'button'=>1, 'col'=>1, 'form'=>1, 'img'=>1, 'input'=>1, 'object'=>1, 'optgroup'=>1, 'option'=>1, 'param'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'td'=>1, 'tfoot'=>1, 'th'=>1, 'thead'=>1, 'tr'=>1, 'xml:space'=>1); - $lcase = isset($eAL[$e]) ? 1 : 0; -} - -$depTr = 0; -if($C['no_deprecated_attr']){ - // dep attr:applicable ele - static $aND = array('align'=>array('caption'=>1, 'div'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'legend'=>1, 'object'=>1, 'p'=>1, 'table'=>1), 'bgcolor'=>array('table'=>1, 'td'=>1, 'th'=>1, 'tr'=>1), 'border'=>array('img'=>1, 'object'=>1), 'bordercolor'=>array('table'=>1, 'td'=>1, 'tr'=>1), 'clear'=>array('br'=>1), 'compact'=>array('dl'=>1, 'ol'=>1, 'ul'=>1), 'height'=>array('td'=>1, 'th'=>1), 'hspace'=>array('img'=>1, 'object'=>1), 'language'=>array('script'=>1), 'name'=>array('a'=>1, 'form'=>1, 'iframe'=>1, 'img'=>1, 'map'=>1), 'noshade'=>array('hr'=>1), 'nowrap'=>array('td'=>1, 'th'=>1), 'size'=>array('hr'=>1), 'start'=>array('ol'=>1), 'type'=>array('li'=>1, 'ol'=>1, 'ul'=>1), 'value'=>array('li'=>1), 'vspace'=>array('img'=>1, 'object'=>1), 'width'=>array('hr'=>1, 'pre'=>1, 'td'=>1, 'th'=>1)); - static $eAD = array('a'=>1, 'br'=>1, 'caption'=>1, 'div'=>1, 'dl'=>1, 'form'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'hr'=>1, 'iframe'=>1, 'img'=>1, 'input'=>1, 'legend'=>1, 'li'=>1, 'map'=>1, 'object'=>1, 'ol'=>1, 'p'=>1, 'pre'=>1, 'script'=>1, 'table'=>1, 'td'=>1, 'th'=>1, 'tr'=>1, 'ul'=>1); - $depTr = isset($eAD[$e]) ? 1 : 0; -} - -// attr name-vals -if(strpos($a, "\x01") !== false){$a = preg_replace('`\x01[^\x01]*\x01`', '', $a);} // No comment/CDATA sec -$mode = 0; $a = trim($a, ' /'); $aA = array(); -while(strlen($a)){ - $w = 0; - switch($mode){ - case 0: // Name - if(preg_match('`^[a-zA-Z][\-a-zA-Z:]+`', $a, $m)){ - $nm = strtolower($m[0]); - $w = $mode = 1; $a = ltrim(substr_replace($a, '', 0, strlen($m[0]))); - } - break; case 1: - if($a[0] == '='){ // = - $w = 1; $mode = 2; $a = ltrim($a, '= '); - }else{ // No val - $w = 1; $mode = 0; $a = ltrim($a); - $aA[$nm] = ''; - } - break; case 2: // Val - if(preg_match('`^((?:"[^"]*")|(?:\'[^\']*\')|(?:\s*[^\s"\']+))(.*)`', $a, $m)){ - $a = ltrim($m[2]); $m = $m[1]; $w = 1; $mode = 0; - $aA[$nm] = trim(str_replace('<', '<', ($m[0] == '"' or $m[0] == '\'') ? substr($m, 1, -1) : $m)); - } - break; - } - if($w == 0){ // Parse errs, deal with space, " & ' - $a = preg_replace('`^(?:"[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*`', '', $a); - $mode = 0; - } -} -if($mode == 1){$aA[$nm] = '';} - -// clean attrs -global $S; -$rl = isset($S[$e]) ? $S[$e] : array(); -$a = array(); $nfr = 0; -foreach($aA as $k=>$v){ - if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e]))) && !isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])){ - if(isset($aNE[$k])){$v = $k;} - elseif(!empty($lcase) && (($e != 'button' or $e != 'input') or $k == 'type')){ // Rather loose but ?not cause issues - $v = (isset($aNL[($v2 = strtolower($v))])) ? $v2 : $v; - } - if($k == 'style' && !$C['style_pass']){ - if(false !== strpos($v, '&#')){ - static $sC = array(' '=>' ', ' '=>' ', 'E'=>'e', 'E'=>'e', 'e'=>'e', 'e'=>'e', 'X'=>'x', 'X'=>'x', 'x'=>'x', 'x'=>'x', 'P'=>'p', 'P'=>'p', 'p'=>'p', 'p'=>'p', 'S'=>'s', 'S'=>'s', 's'=>'s', 's'=>'s', 'I'=>'i', 'I'=>'i', 'i'=>'i', 'i'=>'i', 'O'=>'o', 'O'=>'o', 'o'=>'o', 'o'=>'o', 'N'=>'n', 'N'=>'n', 'n'=>'n', 'n'=>'n', 'U'=>'u', 'U'=>'u', 'u'=>'u', 'u'=>'u', 'R'=>'r', 'R'=>'r', 'r'=>'r', 'r'=>'r', 'L'=>'l', 'L'=>'l', 'l'=>'l', 'l'=>'l', '('=>'(', '('=>'(', ')'=>')', ')'=>')', ' '=>':', ' '=>':', '"'=>'"', '"'=>'"', '''=>"'", '''=>"'", '/'=>'/', '/'=>'/', '*'=>'*', '*'=>'*', '\'=>'\\', '\'=>'\\'); - $v = strtr($v, $sC); - } - $v = preg_replace_callback('`(url(?:\()(?: )*(?:\'|"|&(?:quot|apos);)?)(.+?)((?:\'|"|&(?:quot|apos);)?(?: )*(?:\)))`iS', '\DataTables\Vendor\htmLawed::hl_prot', $v); - $v = !$C['css_expression'] ? preg_replace('`expression`i', ' ', preg_replace('`\\\\\S|(/|(%2f))(\*|(%2a))`i', ' ', $v)) : $v; - }elseif(isset($aNP[$k]) or strpos($k, 'src') !== false or $k[0] == 'o'){ - $v = str_replace("­", ' ', (strpos($v, '&') !== false ? str_replace(array('­', '­', '­'), ' ', $v) : $v)); # double-quoted char is soft-hyphen; appears here as "­" or hyphen or something else depending on viewing software - $v = \DataTables\Vendor\htmLawed::hl_prot($v, $k); - if($k == 'href'){ // X-spam - if($C['anti_mail_spam'] && strpos($v, 'mailto:') === 0){ - $v = str_replace('@', htmlspecialchars($C['anti_mail_spam']), $v); - }elseif($C['anti_link_spam']){ - $r1 = $C['anti_link_spam'][1]; - if(!empty($r1) && preg_match($r1, $v)){continue;} - $r0 = $C['anti_link_spam'][0]; - if(!empty($r0) && preg_match($r0, $v)){ - if(isset($a['rel'])){ - if(!preg_match('`\bnofollow\b`i', $a['rel'])){$a['rel'] .= ' nofollow';} - }elseif(isset($aA['rel'])){ - if(!preg_match('`\bnofollow\b`i', $aA['rel'])){$nfr = 1;} - }else{$a['rel'] = 'nofollow';} - } - } - } - } - if(isset($rl[$k]) && is_array($rl[$k]) && ($v = \DataTables\Vendor\htmLawed::hl_attrval($k, $v, $rl[$k])) === 0){continue;} - $a[$k] = str_replace('"', '"', $v); - } -} -if($nfr){$a['rel'] = isset($a['rel']) ? $a['rel']. ' nofollow' : 'nofollow';} - -// rqd attr -static $eAR = array('area'=>array('alt'=>'area'), 'bdo'=>array('dir'=>'ltr'), 'form'=>array('action'=>''), 'img'=>array('src'=>'', 'alt'=>'image'), 'map'=>array('name'=>''), 'optgroup'=>array('label'=>''), 'param'=>array('name'=>''), 'script'=>array('type'=>'text/javascript'), 'textarea'=>array('rows'=>'10', 'cols'=>'50')); -if(isset($eAR[$e])){ - foreach($eAR[$e] as $k=>$v){ - if(!isset($a[$k])){$a[$k] = isset($v[0]) ? $v : $k;} - } -} - -// depr attrs -if($depTr){ - $c = array(); - foreach($a as $k=>$v){ - if($k == 'style' or !isset($aND[$k][$e])){continue;} - if($k == 'align'){ - unset($a['align']); - if($e == 'img' && ($v == 'left' or $v == 'right')){$c[] = 'float: '. $v;} - elseif(($e == 'div' or $e == 'table') && $v == 'center'){$c[] = 'margin: auto';} - else{$c[] = 'text-align: '. $v;} - }elseif($k == 'bgcolor'){ - unset($a['bgcolor']); - $c[] = 'background-color: '. $v; - }elseif($k == 'border'){ - unset($a['border']); $c[] = "border: {$v}px"; - }elseif($k == 'bordercolor'){ - unset($a['bordercolor']); $c[] = 'border-color: '. $v; - }elseif($k == 'clear'){ - unset($a['clear']); $c[] = 'clear: '. ($v != 'all' ? $v : 'both'); - }elseif($k == 'compact'){ - unset($a['compact']); $c[] = 'font-size: 85%'; - }elseif($k == 'height' or $k == 'width'){ - unset($a[$k]); $c[] = $k. ': '. ($v[0] != '*' ? $v. (ctype_digit($v) ? 'px' : '') : 'auto'); - }elseif($k == 'hspace'){ - unset($a['hspace']); $c[] = "margin-left: {$v}px; margin-right: {$v}px"; - }elseif($k == 'language' && !isset($a['type'])){ - unset($a['language']); - $a['type'] = 'text/'. strtolower($v); - }elseif($k == 'name'){ - if($C['no_deprecated_attr'] == 2 or ($e != 'a' && $e != 'map')){unset($a['name']);} - if(!isset($a['id']) && preg_match('`[a-zA-Z][a-zA-Z\d.:_\-]*`', $v)){$a['id'] = $v;} - }elseif($k == 'noshade'){ - unset($a['noshade']); $c[] = 'border-style: none; border: 0; background-color: gray; color: gray'; - }elseif($k == 'nowrap'){ - unset($a['nowrap']); $c[] = 'white-space: nowrap'; - }elseif($k == 'size'){ - unset($a['size']); $c[] = 'size: '. $v. 'px'; - }elseif($k == 'start' or $k == 'value'){ - unset($a[$k]); - }elseif($k == 'type'){ - unset($a['type']); - static $ol_type = array('i'=>'lower-roman', 'I'=>'upper-roman', 'a'=>'lower-latin', 'A'=>'upper-latin', '1'=>'decimal'); - $c[] = 'list-style-type: '. (isset($ol_type[$v]) ? $ol_type[$v] : 'decimal'); - }elseif($k == 'vspace'){ - unset($a['vspace']); $c[] = "margin-top: {$v}px; margin-bottom: {$v}px"; - } - } - if(count($c)){ - $c = implode('; ', $c); - $a['style'] = isset($a['style']) ? rtrim($a['style'], ' ;'). '; '. $c. ';': $c. ';'; - } -} -// unique ID -if($C['unique_ids'] && isset($a['id'])){ - if(!preg_match('`^[A-Za-z][A-Za-z0-9_\-.:]*$`', ($id = $a['id'])) or (isset($GLOBALS['hl_Ids'][$id]) && $C['unique_ids'] == 1)){unset($a['id']); - }else{ - while(isset($GLOBALS['hl_Ids'][$id])){$id = $C['unique_ids']. $id;} - $GLOBALS['hl_Ids'][($a['id'] = $id)] = 1; - } -} -// xml:lang -if($C['xml:lang'] && isset($a['lang'])){ - $a['xml:lang'] = isset($a['xml:lang']) ? $a['xml:lang'] : $a['lang']; - if($C['xml:lang'] == 2){unset($a['lang']);} -} -// for transformed tag -if(!empty($trt)){ - $a['style'] = isset($a['style']) ? rtrim($a['style'], ' ;'). '; '. $trt : $trt; -} -// return with empty ele / -if(empty($C['hook_tag'])){ - $aA = ''; - foreach($a as $k=>$v){$aA .= " {$k}=\"{$v}\"";} - return "<{$e}{$aA}". (isset($eE[$e]) ? ' /' : ''). '>'; -} -else{return $C['hook_tag']($e, $a);} -// eof -} - -public static function hl_tag2(&$e, &$a, $t=1){ -// transform tag -if($e == 'center'){$e = 'div'; return 'text-align: center;';} -if($e == 'dir' or $e == 'menu'){$e = 'ul'; return '';} -if($e == 's' or $e == 'strike'){$e = 'span'; return 'text-decoration: line-through;';} -if($e == 'u'){$e = 'span'; return 'text-decoration: underline;';} -static $fs = array('0'=>'xx-small', '1'=>'xx-small', '2'=>'small', '3'=>'medium', '4'=>'large', '5'=>'x-large', '6'=>'xx-large', '7'=>'300%', '-1'=>'smaller', '-2'=>'60%', '+1'=>'larger', '+2'=>'150%', '+3'=>'200%', '+4'=>'300%'); -if($e == 'font'){ - $a2 = ''; - while(preg_match('`(^|\s)(color|size)\s*=\s*(\'|")?(.+?)(\\3|\s|$)`i', $a, $m)){ - $a = str_replace($m[0], ' ', $a); - $a2 .= strtolower($m[2]) == 'color' ? (' color: '. str_replace('"', '\'', trim($m[4])). ';') : (isset($fs[($m = trim($m[4]))]) ? ($a2 .= ' font-size: '. str_replace('"', '\'', $fs[$m]). ';') : ''); - } - while(preg_match('`(^|\s)face\s*=\s*(\'|")?([^=]+?)\\2`i', $a, $m) or preg_match('`(^|\s)face\s*=(\s*)(\S+)`i', $a, $m)){ - $a = str_replace($m[0], ' ', $a); - $a2 .= ' font-family: '. str_replace('"', '\'', trim($m[3])). ';'; - } - $e = 'span'; return ltrim(str_replace('<', '', $a2)); -} -if($t == 2){$e = 0; return 0;} -return ''; -// eof -} - -public static function hl_tidy($t, $w, $p){ -// Tidy/compact HTM -if(strpos(' pre,script,textarea', "$p,")){return $t;} -$t = preg_replace('`\s+`', ' ', preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)()`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t)); -if(($w = strtolower($w)) == -1){ - return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t); -} -$s = strpos(" $w", 't') ? "\t" : ' '; -$s = preg_match('`\d`', $w, $m) ? str_repeat($s, $m[0]) : str_repeat($s, ($s == "\t" ? 1 : 2)); -$N = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0; -$a = array('br'=>1); -$b = array('button'=>1, 'input'=>1, 'option'=>1, 'param'=>1); -$c = array('caption'=>1, 'dd'=>1, 'dt'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'isindex'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'object'=>1, 'p'=>1, 'pre'=>1, 'td'=>1, 'textarea'=>1, 'th'=>1); -$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1); -$T = explode('<', $t); -$X = 1; -while($X){ - $n = $N; - $t = $T; - ob_start(); - if(isset($d[$p])){echo str_repeat($s, ++$n);} - echo ltrim(array_shift($t)); - for($i=-1, $j=count($t); ++$i<$j;){ - $r = ''; list($e, $r) = explode('>', $t[$i]); - $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1)); - $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0); - $e = "<$e>"; - if(isset($d[$y])){ - if(!$x){ - if($n){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);} - else{++$N; ob_end_clean(); continue 2;} - } - else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));} - echo $r; continue; - } - $f = "\n". str_repeat($s, $n); - if(isset($c[$y])){ - if(!$x){echo $e, $f, $r;} - else{echo $f, $e, $r;} - }elseif(isset($b[$y])){echo $f, $e, $r; - }elseif(isset($a[$y])){echo $e, $f, $r; - }elseif(!$y){echo $f, $e, $f, $r; - }else{echo $e, $r;} - } - $X = 0; -} -$t = str_replace(array("\n ", " \n"), "\n", preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents())); -ob_end_clean(); -if(($l = strpos(" $w", 'r') ? (strpos(" $w", 'n') ? "\r\n" : "\r") : 0)){ - $t = str_replace("\n", $l, $t); -} -return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t); -// eof -} - -public static function hl_version(){ -// rel -return '1.1.22'; -// eof -} - -public static function kses($t, $h, $p=array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'gopher', 'mailto')){ -// kses compat -foreach($h as $k=>$v){ - $h[$k]['n']['*'] = 1; -} -$C['cdata'] = $C['comment'] = $C['make_tag_strict'] = $C['no_deprecated_attr'] = $C['unique_ids'] = 0; -$C['keep_bad'] = 1; -$C['elements'] = count($h) ? strtolower(implode(',', array_keys($h))) : '-*'; -$C['hook'] = '\DataTables\Vendor\htmLawed::kses_hook'; -$C['schemes'] = '*:'. implode(',', $p); -return \DataTables\Vendor\htmLawed::hl($t, $C, $h); -// eof -} - -public static function kses_hook($t, &$C, &$S){ -// kses compat -return $t; -// eof -} -// end class -} diff --git a/php/_notes/dwsync.xml b/php/_notes/dwsync.xml deleted file mode 100644 index cb12058..0000000 --- a/php/_notes/dwsync.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/php/diff_update.php b/php/diff_update.php deleted file mode 100644 index 6a00d59..0000000 --- a/php/diff_update.php +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..16886c9 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,53 @@ +import { defineConfig, devices } from "@playwright/test"; + +/* End-to-end against a production build and the mock platform. The + database is a throwaway file so every run starts from a clean slate. */ +const PORT = 3100; +const MOCK_PORT = 4110; + +export default defineConfig({ + testDir: "./e2e", + timeout: 90_000, + expect: { timeout: 15_000 }, + fullyParallel: false, + workers: 1, + retries: process.env.CI ? 1 : 0, + reporter: process.env.CI ? [["list"], ["html", { open: "never" }]] : "list", + use: { + baseURL: `http://localhost:${PORT}`, + trace: "retain-on-failure", + screenshot: "only-on-failure", + ...devices["Desktop Chrome"], + /* A machine with a Chromium already on disk can point at it instead of + downloading the version this @playwright/test pins. */ + ...(process.env.PLAYWRIGHT_CHROMIUM_PATH ? { launchOptions: { executablePath: process.env.PLAYWRIGHT_CHROMIUM_PATH } } : {}), + }, + webServer: [ + { + command: `MOCK_PLATFORM_PORT=${MOCK_PORT} MOCK_PLATFORM_DELAY_MS=800 node scripts/mock-platform.mjs`, + port: MOCK_PORT, + reuseExistingServer: false, + }, + { + command: `rm -rf data/e2e && node scripts/start-standalone.mjs`, + port: PORT, + timeout: 120_000, + reuseExistingServer: false, + env: { + NODE_ENV: "production", + PORT: String(PORT), + HOSTNAME: "127.0.0.1", + HF_API_BASE_URL: `http://localhost:${MOCK_PORT}`, + APP_SECRET: "e2e-secret-not-for-production", + APP_URL: `http://localhost:${PORT}`, + PLATFORM_API_KEY: "e2e-id:e2e-secret", + /* The standalone server runs from .next/standalone, so the paths are + absolute to keep the throwaway database in the repo's data folder. */ + DATABASE_URL: `file:${process.cwd()}/data/e2e/vitrina.db`, + DATA_DIR: `${process.cwd()}/data/e2e`, + STORAGE_DRIVER: "local", + NEXT_PUBLIC_STORAGE_DRIVER: "local", + }, + }, + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..034c507 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,6099 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@libsql/client': + specifier: 0.18.0 + version: 0.18.0 + '@tanstack/react-virtual': + specifier: ^3.14.10 + version: 3.14.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@vercel/blob': + specifier: ^2.8.0 + version: 2.8.0 + better-auth: + specifier: 1.7.5 + version: 1.7.5(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client@0.18.0)(kysely@0.29.6))(next@16.3.3(@babel/core@7.29.7)(@playwright/test@1.63.0)(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.1(@types/node@22.20.1)(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13))) + drizzle-orm: + specifier: 0.45.2 + version: 0.45.2(@libsql/client@0.18.0)(kysely@0.29.6) + fflate: + specifier: 0.8.3 + version: 0.8.3 + next: + specifier: ^16.0.0 + version: 16.3.3(@babel/core@7.29.7)(@playwright/test@1.63.0)(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: + specifier: ^19.2.0 + version: 19.2.8 + react-dom: + specifier: ^19.2.0 + version: 19.2.8(react@19.2.8) + stripe: + specifier: 22.6.2 + version: 22.6.2(@types/node@22.20.1) + zod: + specifier: 4.6.5 + version: 4.6.5 + zustand: + specifier: ^5.0.15 + version: 5.0.15(@types/react@19.2.18)(react@19.2.8) + devDependencies: + '@playwright/test': + specifier: 1.63.0 + version: 1.63.0 + '@types/node': + specifier: ^22.10.2 + version: 22.20.1 + '@types/react': + specifier: ^19.2.0 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.0 + version: 19.2.5(@types/react@19.2.18) + drizzle-kit: + specifier: 0.31.10 + version: 0.31.10 + eslint: + specifier: 9.39.5 + version: 9.39.5 + eslint-config-next: + specifier: 16.3.5 + version: 16.3.5(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + tsx: + specifier: ^4.23.13 + version: 4.23.13 + typescript: + specifier: ^5.9.2 + version: 5.9.3 + vitest: + specifier: 5.0.1 + version: 5.0.1(@types/node@22.20.1)(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13)) + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@better-auth/core@1.7.5': + resolution: {integrity: sha512-kVlSu4H8OKQfjg4b/Zj5MOaospt83N0JbX38wsDzE58Yw95jzovFkU3pxzB1eUFYc4mkuhUMZD8iT1gpUjNMcQ==} + peerDependencies: + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + '@opentelemetry/api': ^1.9.0 + better-call: 1.4.0 + jose: ^6.1.0 + kysely: ^0.28.5 || ^0.29.0 + nanostores: ^1.0.1 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@better-auth/drizzle-adapter@1.7.5': + resolution: {integrity: sha512-9SM7v1735SoaedRDcDbHc5ULgXEd2vUlEJkvRHpMF2Q9qf59TRh1b5A9hryyecyi56bm/0CNUDU3nY0uVWj5/Q==} + peerDependencies: + '@better-auth/core': ^1.7.5 + '@better-auth/utils': 0.4.2 + drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 + peerDependenciesMeta: + drizzle-orm: + optional: true + + '@better-auth/kysely-adapter@1.7.5': + resolution: {integrity: sha512-1wE5gvnjW+c1i4GrLtL9HLn3s0Xrq4YneCDan1NO1dpz0mEguLFNlzfnUGykTFrDwvFh2X5rkIbA8ALCj6WzXQ==} + peerDependencies: + '@better-auth/core': ^1.7.5 + '@better-auth/utils': 0.4.2 + kysely: ^0.28.17 || ^0.29.0 + peerDependenciesMeta: + kysely: + optional: true + + '@better-auth/memory-adapter@1.7.5': + resolution: {integrity: sha512-YDmnfR9zOXbn5SNYYwfHBPuc19hg1d1C1vUXb+Hm8Q91pTsstFNX5OTlZbo7f28q06FpogAEfGGCN/2QV39cig==} + peerDependencies: + '@better-auth/core': ^1.7.5 + '@better-auth/utils': 0.4.2 + + '@better-auth/mongo-adapter@1.7.5': + resolution: {integrity: sha512-Yq0LfF0VlA9Kfjcjp/v43MSsChv7vKd1ct0Mt15px4zlqaCPIGfPbjw9YNM6jTo0fGpqLR0n15PllSWmLLRp9g==} + peerDependencies: + '@better-auth/core': ^1.7.5 + '@better-auth/utils': 0.4.2 + mongodb: ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + mongodb: + optional: true + + '@better-auth/prisma-adapter@1.7.5': + resolution: {integrity: sha512-QfW6HS9vK0FMcbI/GsQLdplICxOz0EPzYWGONZT4ovL3cSItd4YH/09USbPPVl+VUQCdznAQIk+n+NKvHdmSag==} + peerDependencies: + '@better-auth/core': ^1.7.5 + '@better-auth/utils': 0.4.2 + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + '@prisma/client': + optional: true + prisma: + optional: true + + '@better-auth/telemetry@1.7.5': + resolution: {integrity: sha512-e/REPqMy9Em+gC6G0xWBikiMLRuy532Er7jqdoNkPBa65FbywgWcm1cZbgdW5CnHsrM3OLZsmKn14yeGoDISeg==} + peerDependencies: + '@better-auth/core': ^1.7.5 + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + + '@better-auth/utils@0.4.2': + resolution: {integrity: sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==} + + '@better-auth/utils@0.5.0': + resolution: {integrity: sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==} + + '@better-fetch/fetch@1.3.2': + resolution: {integrity: sha512-Gs7n99b5tqUC6cQAPbV0uED3IraHB6xQbHLQ/C3l7ZFafHScOx9pQ+DYmP5blbLFShVWLqxNUlI9wi4xU/X+ow==} + + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.hirok.io' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.hirok.io' + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.7': + resolution: {integrity: sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.3': + resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.3': + resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.3': + resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.2': + resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.2': + resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.2': + resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.2': + resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.2': + resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.2': + resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.2': + resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.2': + resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.3': + resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.3': + resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.3': + resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.3': + resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.3': + resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.3': + resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.3': + resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.3': + resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.3': + resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.3': + resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.3': + resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.3': + resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.3': + resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@libsql/client@0.18.0': + resolution: {integrity: sha512-zMCCo58vv7w27j7+cnt/GW+X6/Rih6cHZ86PKi0AxCMOqMfrVgG9OZ5lW2bDMJhSeOjsv2szH2nJF3A9l36UmA==} + + '@libsql/core@0.18.0': + resolution: {integrity: sha512-N8RR2CIpcgtbKZnXs2KVpw2lJQfinUj3I56e7qRDRpK18Hty0Rr8nr00VmFb4R/DsqOanPB/TaP3+QAQLZeXMg==} + + '@libsql/darwin-arm64@0.5.29': + resolution: {integrity: sha512-K+2RIB1OGFPYQbfay48GakLhqf3ArcbHqPFu7EZiaUcRgFcdw8RoltsMyvbj5ix2fY0HV3Q3Ioa/ByvQdaSM0A==} + cpu: [arm64] + os: [darwin] + + '@libsql/darwin-x64@0.5.29': + resolution: {integrity: sha512-OtT+KFHsKFy1R5FVadr8FJ2Bb1mghtXTyJkxv0trocq7NuHntSki1eUbxpO5ezJesDvBlqFjnWaYYY516QNLhQ==} + cpu: [x64] + os: [darwin] + + '@libsql/hrana-client@0.10.0': + resolution: {integrity: sha512-OoA4EMqRAC7kn7V2P6EQqRcpZf2W+AjsNIyCizBg339Tq/aMC7sRnzs3SklderhmQWAqEzvv8A2vhxVmWpkVvw==} + + '@libsql/isomorphic-ws@0.1.5': + resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} + + '@libsql/linux-arm-gnueabihf@0.5.29': + resolution: {integrity: sha512-CD4n4zj7SJTHso4nf5cuMoWoMSS7asn5hHygsDuhRl8jjjCTT3yE+xdUvI4J7zsyb53VO5ISh4cwwOtf6k2UhQ==} + cpu: [arm] + os: [linux] + + '@libsql/linux-arm-musleabihf@0.5.29': + resolution: {integrity: sha512-2Z9qBVpEJV7OeflzIR3+l5yAd4uTOLxklScYTwpZnkm2vDSGlC1PRlueLaufc4EFITkLKXK2MWBpexuNJfMVcg==} + cpu: [arm] + os: [linux] + + '@libsql/linux-arm64-gnu@0.5.29': + resolution: {integrity: sha512-gURBqaiXIGGwFNEaUj8Ldk7Hps4STtG+31aEidCk5evMMdtsdfL3HPCpvys+ZF/tkOs2MWlRWoSq7SOuCE9k3w==} + cpu: [arm64] + os: [linux] + + '@libsql/linux-arm64-musl@0.5.29': + resolution: {integrity: sha512-fwgYZ0H8mUkyVqXZHF3mT/92iIh1N94Owi/f66cPVNsk9BdGKq5gVpoKO+7UxaNzuEH1roJp2QEwsCZMvBLpqg==} + cpu: [arm64] + os: [linux] + + '@libsql/linux-x64-gnu@0.5.29': + resolution: {integrity: sha512-y14V0vY0nmMC6G0pHeJcEarcnGU2H6cm21ZceRkacWHvQAEhAG0latQkCtoS2njFOXiYIg+JYPfAoWKbi82rkg==} + cpu: [x64] + os: [linux] + + '@libsql/linux-x64-musl@0.5.29': + resolution: {integrity: sha512-gquqwA/39tH4pFl+J9n3SOMSymjX+6kZ3kWgY3b94nXFTwac9bnFNMffIomgvlFaC4ArVqMnOZD3nuJ3H3VO1w==} + cpu: [x64] + os: [linux] + + '@libsql/win32-x64-msvc@0.5.29': + resolution: {integrity: sha512-4/0CvEdhi6+KjMxMaVbFM2n2Z44escBRoEYpR+gZg64DdetzGnYm8mcNLcoySaDJZNaBd6wz5DNdgRmcI4hXcg==} + cpu: [x64] + os: [win32] + + '@napi-rs/wasm-runtime@1.2.4': + resolution: {integrity: sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@neon-rs/load@0.0.4': + resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} + + '@next/env@16.3.3': + resolution: {integrity: sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==} + + '@next/eslint-plugin-next@16.3.5': + resolution: {integrity: sha512-PGfSeItHJ12DH8t+6sEbuMe59NE5rAhCfgk06QKTH2ne9VUL1JlaXdYXY3B8RaiF2SO50rDYvd39TulP6A6xZQ==} + + '@next/swc-darwin-arm64@16.3.3': + resolution: {integrity: sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.3.3': + resolution: {integrity: sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.3.3': + resolution: {integrity: sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.3.3': + resolution: {integrity: sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.3.3': + resolution: {integrity: sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.3.3': + resolution: {integrity: sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.3.3': + resolution: {integrity: sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.3.3': + resolution: {integrity: sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@noble/ciphers@2.4.0': + resolution: {integrity: sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw==} + engines: {node: '>= 20.19.0'} + + '@noble/hashes@2.4.0': + resolution: {integrity: sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==} + engines: {node: '>= 20.19.0'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@opentelemetry/semantic-conventions@1.43.0': + resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} + engines: {node: '>=14'} + + '@oxc-project/types@0.150.0': + resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} + + '@playwright/test@1.63.0': + resolution: {integrity: sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==} + engines: {node: '>=20'} + hasBin: true + + '@rolldown/binding-android-arm-eabi@1.2.9': + resolution: {integrity: sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.9': + resolution: {integrity: sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.9': + resolution: {integrity: sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.9': + resolution: {integrity: sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.9': + resolution: {integrity: sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': + resolution: {integrity: sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.9': + resolution: {integrity: sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.9': + resolution: {integrity: sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.9': + resolution: {integrity: sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.9': + resolution: {integrity: sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.9': + resolution: {integrity: sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.9': + resolution: {integrity: sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.9': + resolution: {integrity: sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.9': + resolution: {integrity: sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.9': + resolution: {integrity: sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tanstack/react-virtual@3.14.10': + resolution: {integrity: sha512-SRyoUbdFMRHuYXMijV5H4ZarQWpXkj3iANq8OFre+pybeVap8ZJjZ3Nz9bVjx4d8PfobVUQUdKyyyHYk3E+djw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/virtual-core@3.17.8': + resolution: {integrity: sha512-BfEvehNpOT75r5Ksc5xW6NZuXujTfb7nlSEyVu4XHG3gdxNg1KqXruWbDewXOUaUYIo4oRbSfkjIajz4MAT8tA==} + + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + + '@types/react-dom@19.2.5': + resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@typescript-eslint/eslint-plugin@8.70.0': + resolution: {integrity: sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.70.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.70.0': + resolution: {integrity: sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.70.0': + resolution: {integrity: sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.70.0': + resolution: {integrity: sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.70.0': + resolution: {integrity: sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.70.0': + resolution: {integrity: sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.70.0': + resolution: {integrity: sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.70.0': + resolution: {integrity: sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.70.0': + resolution: {integrity: sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.70.0': + resolution: {integrity: sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} + cpu: [x64] + os: [win32] + + '@vercel/blob@2.8.0': + resolution: {integrity: sha512-Nu+HWKpkgovCh/ezlG7wCVwF7RErTzLzZMbGKFBdGBCbTKyK+s5VXPLl+0+TpNEQPH8AVaGzOpIsXUOtkqylCQ==} + engines: {node: '>=20.0.0'} + + '@vercel/cli-config@0.2.4': + resolution: {integrity: sha512-kZ5SojbrV06GHoU6QIWGwDXLov+s9rWZ7QqdqKfJfBGCNUieGfgaCjeeenNy8Y+QC0bwC0dZ2B4l5Hvdmrgpdw==} + + '@vercel/cli-exec@1.0.1': + resolution: {integrity: sha512-g9XerViJ/paZujufXYcu5XYI2vU2rtB4sgdpjUHde5RnOkdmpu0ngH46LCFGHoPXO/C+qDPSczIHIRN+8Q2YKQ==} + engines: {node: '>= 18'} + + '@vercel/oidc@3.8.5': + resolution: {integrity: sha512-RwXYtnt6za+5UO4IaLywN/6B95AlLqynPRUWRJxeJ/qufwkcLUbZNUxYtzT0uMpuraWhlNcGqPNGkTnZr4BGBw==} + engines: {node: '>= 20'} + + '@vitest/mocker@5.0.1': + resolution: {integrity: sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/spy@5.0.1': + resolution: {integrity: sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.2.0: + resolution: {integrity: sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.13.0: + resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.19: + resolution: {integrity: sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==} + engines: {node: '>=6.0.0'} + hasBin: true + + baseline-browser-mapping@2.11.25: + resolution: {integrity: sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==} + engines: {node: '>=6.0.0'} + hasBin: true + + better-auth@1.7.5: + resolution: {integrity: sha512-aKE0Zt2EPTpFvmq4/oATNyG/mAfc6JUqWkW9pzGlrVnzUb0lso7GJ9BPxD6JPhLqYV1a9zOAb0uAz1Q5fm+eHA==} + peerDependencies: + '@lynx-js/react': '*' + '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 + '@sveltejs/kit': ^2.0.0 + '@tanstack/react-start': ^1.0.0 + '@tanstack/solid-start': ^1.0.0 + drizzle-kit: '>=0.31.4 || >=1.0.0-beta.1' + drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 + mongodb: ^6.0.0 || ^7.0.0 + mysql2: ^3.0.0 + next: ^14.0.0 || ^15.0.0 || ^16.0.0 + pg: ^8.0.0 + prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + solid-js: ^1.0.0 + svelte: ^4.0.0 || ^5.0.0 + vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@lynx-js/react': + optional: true + '@prisma/client': + optional: true + '@sveltejs/kit': + optional: true + '@tanstack/react-start': + optional: true + '@tanstack/solid-start': + optional: true + drizzle-kit: + optional: true + drizzle-orm: + optional: true + mongodb: + optional: true + mysql2: + optional: true + next: + optional: true + pg: + optional: true + prisma: + optional: true + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vitest: + optional: true + vue: + optional: true + + better-call@1.4.0: + resolution: {integrity: sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA==} + peerDependencies: + zod: ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + detect-libc@2.0.2: + resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + drizzle-kit@0.31.10: + resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} + hasBin: true + + drizzle-orm@0.45.2: + resolution: {integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.430: + resolution: {integrity: sha512-e1QEj72Y4zd8RlNZVmoTg+iCOSVwpk05IOiiQwdrkwCSVlZfPthevErhE+nckGd2YbsXfp1SkisznhGVIXP2NQ==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.4.0: + resolution: {integrity: sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@16.3.5: + resolution: {integrity: sha512-wPjq9MLQuWykHs8tsm2gH6OJThk6N27L8Te2JatlaGZ7jT1gtgGmJKukygL28xOlWsg5J1a1bGy/PvLyQC8OYA==} + peerDependencies: + eslint: '>=9.0.0' + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.14.0: + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.3: + resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.9: + resolution: {integrity: sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.17.0: + resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + + jose@6.2.12: + resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} + + js-base64@3.9.3: + resolution: {integrity: sha512-uwYQp+VJ38FVvtim6qNbit6e9uT6dwWQ4Y1+H9TxhW5hcHjpHwoxlR0nMpqUmIFOmu4VqMxwdJA88gIVuZJQ/g==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kysely@0.29.6: + resolution: {integrity: sha512-hHaB8C/rfzDDtr/t8YZwxAuPJTT0zHyaPoVzcXwDYhYNAgH/4sIfVhi/XLLIY+bL/FqaIJnjATDbi8ObSELmxg==} + engines: {node: '>=22.0.0'} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + libsql@0.5.29: + resolution: {integrity: sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg==} + cpu: [x64, arm64, wasm32, arm] + os: [darwin, linux, win32] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@1.4.1: + resolution: {integrity: sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanostores@1.5.3: + resolution: {integrity: sha512-rQLB6eV4f2AW/n3L0JmwCROpaisYy9EDEADvEFSd1C/qG8hB6O5TPlh9A791JRbJr4CnMQBzptDcvD9OR1+6WA==} + engines: {node: ^20.0.0 || >=22.0.0} + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next@16.3.3: + resolution: {integrity: sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} + engines: {node: '>= 0.4'} + + node-releases@2.0.56: + resolution: {integrity: sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==} + engines: {node: '>=18'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + os-paths@4.4.0: + resolution: {integrity: sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg==} + engines: {node: '>= 6.0'} + + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.23: + resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + promise-limit@2.7.0: + resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} + engines: {node: '>= 0.4'} + hasBin: true + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rolldown@1.2.9: + resolution: {integrity: sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rou3@0.9.2: + resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@3.1.2: + resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.35.3: + resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.1.0: + resolution: {integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + stripe@22.6.2: + resolution: {integrity: sha512-PwRE2scocvXqDKPiskMa5vRJEAEkq46W69XyUYmAwrNSLr675lQnfqLwnsndAWizwgihOWw/irLmoFRo3ez7ew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} + engines: {node: '>=20.0.0'} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.23.13: + resolution: {integrity: sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.5: + resolution: {integrity: sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.70.0: + resolution: {integrity: sha512-P/W5cz70/cQAuKfY3xwQMWWTV7BvJ0mAQmi+9mBcsVPaBUpd6Ohpa+fECv9rBFrQcig86jAiNBFNWUqnTjr4pw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@6.28.0: + resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} + engines: {node: '>=18.17'} + + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite@8.3.0: + resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@5.0.1: + resolution: {integrity: sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.1 + '@vitest/browser-preview': 5.0.1 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.1 + '@vitest/coverage-v8': 5.0.1 + '@vitest/ui': 5.0.1 + happy-dom: '*' + jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.23: + resolution: {integrity: sha512-JMh8aK+1B/0bk/YNupICmH5MgCq6yNLKYQUfsZtSDLLCCmxUkHjUY+oOlIa90lO7lHN1woAfpenLdlKpXy9o+A==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-app-paths@5.5.1: + resolution: {integrity: sha512-hI3flOB4PLZIy5prbtTpirobtPE2ZtZ52szO+2mM9Efp6ErM398La+C1lIpNWDfNoQk+6Lsi6nMcCwVB7pxeMQ==} + engines: {node: '>= 6.0'} + + xdg-portable@7.3.0: + resolution: {integrity: sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw==} + engines: {node: '>= 6.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.1.11: + resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} + + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + + zustand@5.0.15: + resolution: {integrity: sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3)': + dependencies: + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + '@opentelemetry/semantic-conventions': 1.43.0 + '@standard-schema/spec': 1.1.0 + better-call: 1.4.0(zod@4.6.5) + jose: 6.2.12 + kysely: 0.29.6 + nanostores: 1.5.3 + zod: 4.6.5 + + '@better-auth/drizzle-adapter@1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@libsql/client@0.18.0)(kysely@0.29.6))': + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/utils': 0.4.2 + optionalDependencies: + drizzle-orm: 0.45.2(@libsql/client@0.18.0)(kysely@0.29.6) + + '@better-auth/kysely-adapter@1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(kysely@0.29.6)': + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/utils': 0.4.2 + optionalDependencies: + kysely: 0.29.6 + + '@better-auth/memory-adapter@1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/utils': 0.4.2 + + '@better-auth/mongo-adapter@1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/utils': 0.4.2 + + '@better-auth/prisma-adapter@1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/utils': 0.4.2 + + '@better-auth/telemetry@1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)': + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + + '@better-auth/utils@0.4.2': + dependencies: + '@noble/hashes': 2.4.0 + + '@better-auth/utils@0.5.0': + dependencies: + '@noble/hashes': 2.4.0 + + '@better-fetch/fetch@1.3.2': {} + + '@drizzle-team/brocli@0.10.2': {} + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.14.3 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': + dependencies: + eslint: 9.39.5 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5)': + dependencies: + eslint: 9.39.5 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.7': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.2 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.2 + optional: true + + '@img/sharp-darwin-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.2 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.2': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.2': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.2': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.2': + optional: true + + '@img/sharp-linux-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.2 + optional: true + + '@img/sharp-linux-arm@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.2 + optional: true + + '@img/sharp-linux-ppc64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.2 + optional: true + + '@img/sharp-linux-riscv64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.2 + optional: true + + '@img/sharp-linux-s390x@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.2 + optional: true + + '@img/sharp-linux-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + optional: true + + '@img/sharp-wasm32@0.35.3': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.3': + dependencies: + '@img/sharp-wasm32': 0.35.3 + optional: true + + '@img/sharp-win32-arm64@0.35.3': + optional: true + + '@img/sharp-win32-ia32@0.35.3': + optional: true + + '@img/sharp-win32-x64@0.35.3': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@libsql/client@0.18.0': + dependencies: + '@libsql/core': 0.18.0 + '@libsql/hrana-client': 0.10.0 + js-base64: 3.9.3 + libsql: 0.5.29 + promise-limit: 2.7.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/core@0.18.0': + dependencies: + js-base64: 3.9.3 + + '@libsql/darwin-arm64@0.5.29': + optional: true + + '@libsql/darwin-x64@0.5.29': + optional: true + + '@libsql/hrana-client@0.10.0': + dependencies: + '@libsql/isomorphic-ws': 0.1.5 + js-base64: 3.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/isomorphic-ws@0.1.5': + dependencies: + '@types/ws': 8.18.1 + ws: 8.21.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@libsql/linux-arm-gnueabihf@0.5.29': + optional: true + + '@libsql/linux-arm-musleabihf@0.5.29': + optional: true + + '@libsql/linux-arm64-gnu@0.5.29': + optional: true + + '@libsql/linux-arm64-musl@0.5.29': + optional: true + + '@libsql/linux-x64-gnu@0.5.29': + optional: true + + '@libsql/linux-x64-musl@0.5.29': + optional: true + + '@libsql/win32-x64-msvc@0.5.29': + optional: true + + '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.4 + optional: true + + '@neon-rs/load@0.0.4': {} + + '@next/env@16.3.3': {} + + '@next/eslint-plugin-next@16.3.5(eslint@9.39.5)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5) + fast-glob: 3.3.1 + transitivePeerDependencies: + - eslint + + '@next/swc-darwin-arm64@16.3.3': + optional: true + + '@next/swc-darwin-x64@16.3.3': + optional: true + + '@next/swc-linux-arm64-gnu@16.3.3': + optional: true + + '@next/swc-linux-arm64-musl@16.3.3': + optional: true + + '@next/swc-linux-x64-gnu@16.3.3': + optional: true + + '@next/swc-linux-x64-musl@16.3.3': + optional: true + + '@next/swc-win32-arm64-msvc@16.3.3': + optional: true + + '@next/swc-win32-x64-msvc@16.3.3': + optional: true + + '@noble/ciphers@2.4.0': {} + + '@noble/hashes@2.4.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.3 + + '@nolyfill/is-core-module@1.0.39': {} + + '@opentelemetry/semantic-conventions@1.43.0': {} + + '@oxc-project/types@0.150.0': {} + + '@playwright/test@1.63.0': + dependencies: + playwright: 1.63.0 + + '@rolldown/binding-android-arm-eabi@1.2.9': + optional: true + + '@rolldown/binding-android-arm64@1.2.9': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.9': + optional: true + + '@rolldown/binding-darwin-x64@1.2.9': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.9': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.9': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.9': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.9': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.9': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.9': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@rtsao/scc@1.1.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tanstack/react-virtual@3.14.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/virtual-core': 3.17.8 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/virtual-core@3.17.8': {} + + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.5(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.20.1 + + '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/type-utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.70.0 + eslint: 9.39.5 + ignore: 7.0.9 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.70.0 + debug: 4.4.3 + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.70.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@5.9.3) + '@typescript-eslint/types': 8.70.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + + '@typescript-eslint/tsconfig-utils@8.70.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.70.0(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.5 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.70.0': {} + + '@typescript-eslint/typescript-estree@8.70.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.70.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.70.0(typescript@5.9.3) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + debug: 4.4.3 + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.70.0(eslint@9.39.5)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + eslint-visitor-keys: 5.0.1 + + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + optional: true + + '@unrs/resolver-binding-android-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@vercel/blob@2.8.0': + dependencies: + '@vercel/oidc': 3.8.5 + async-retry: 1.3.3 + is-buffer: 2.0.5 + is-node-process: 1.2.0 + throttleit: 2.1.0 + undici: 6.28.0 + + '@vercel/cli-config@0.2.4': + dependencies: + xdg-app-paths: 5.5.1 + zod: 4.1.11 + + '@vercel/cli-exec@1.0.1': + dependencies: + execa: 5.1.1 + + '@vercel/oidc@3.8.5': + dependencies: + '@vercel/cli-config': 0.2.4 + '@vercel/cli-exec': 1.0.1 + jose: 5.10.0 + + '@vitest/mocker@5.0.1(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13))': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.1 + estree-walker: 3.0.3 + magic-string: 1.4.1 + optionalDependencies: + vite: 8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13) + + '@vitest/spy@5.0.1': {} + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.13.0: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.19: {} + + baseline-browser-mapping@2.11.25: {} + + better-auth@1.7.5(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@libsql/client@0.18.0)(kysely@0.29.6))(next@16.3.3(@babel/core@7.29.7)(@playwright/test@1.63.0)(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@5.0.1(@types/node@22.20.1)(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13))): + dependencies: + '@better-auth/core': 1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3) + '@better-auth/drizzle-adapter': 1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@libsql/client@0.18.0)(kysely@0.29.6)) + '@better-auth/kysely-adapter': 1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(kysely@0.29.6) + '@better-auth/memory-adapter': 1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2) + '@better-auth/prisma-adapter': 1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2) + '@better-auth/telemetry': 1.7.5(@better-auth/core@1.7.5(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.6)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2) + '@better-auth/utils': 0.4.2 + '@better-fetch/fetch': 1.3.2 + '@noble/ciphers': 2.4.0 + '@noble/hashes': 2.4.0 + better-call: 1.4.0(zod@4.6.5) + defu: 6.1.7 + jose: 6.2.12 + kysely: 0.29.6 + nanostores: 1.5.3 + zod: 4.6.5 + optionalDependencies: + drizzle-kit: 0.31.10 + drizzle-orm: 0.45.2(@libsql/client@0.18.0)(kysely@0.29.6) + next: 16.3.3(@babel/core@7.29.7)(@playwright/test@1.63.0)(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + vitest: 5.0.1(@types/node@22.20.1)(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13)) + transitivePeerDependencies: + - '@opentelemetry/api' + + better-call@1.4.0(zod@4.6.5): + dependencies: + '@better-auth/utils': 0.5.0 + '@better-fetch/fetch': 1.3.2 + rou3: 0.9.2 + set-cookie-parser: 3.1.2 + optionalDependencies: + zod: 4.6.5 + + brace-expansion@1.1.21: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.29.0: + dependencies: + baseline-browser-mapping: 2.11.25 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.430 + node-releases: 2.0.56 + update-browserslist-db: 1.3.3(browserslist@4.29.0) + + buffer-from@1.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001810: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + client-only@0.0.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + defu@6.1.7: {} + + detect-libc@2.0.2: {} + + detect-libc@2.1.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + drizzle-kit@0.31.10: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.12 + tsx: 4.23.13 + + drizzle-orm@0.45.2(@libsql/client@0.18.0)(kysely@0.29.6): + optionalDependencies: + '@libsql/client': 0.18.0 + kysely: 0.29.6 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.430: {} + + emoji-regex@9.2.2: {} + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.2 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.5 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.23 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.4.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@16.3.5(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 16.3.5(eslint@9.39.5) + eslint: 9.39.5 + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5))(eslint@9.39.5) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5) + eslint-plugin-react: 7.37.5(eslint@9.39.5) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5) + globals: 16.4.0 + typescript-eslint: 8.70.0(eslint@9.39.5)(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.10: + dependencies: + debug: 3.2.7 + is-core-module: 2.17.0 + resolve: 2.0.0-next.7 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.5 + get-tsconfig: 4.14.3 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.17 + unrs-resolver: 1.12.2 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5))(eslint@9.39.5) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5))(eslint@9.39.5): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + eslint: 9.39.5 + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5))(eslint@9.39.5): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.2.0 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.5 + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5))(eslint@9.39.5) + hasown: 2.0.4 + is-core-module: 2.17.0 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.10 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.5): + dependencies: + aria-query: 5.3.2 + array-includes: 3.2.0 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.13.0 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.5 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.5): + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.8 + eslint: 9.39.5 + hermes-parser: 0.25.1 + zod: 4.6.5 + zod-validation-error: 4.0.2(zod@4.6.5) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.5): + dependencies: + array-includes: 3.2.0 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.4.0 + eslint: 9.39.5 + estraverse: 5.3.0 + hasown: 2.0.4 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.7 + semver: 6.3.1 + string.prototype.matchall: 4.1.0 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5: + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.7 + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + expect-type@1.4.0: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.3: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fflate@0.8.3: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-stream@6.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.3: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + human-signals@2.1.0: {} + + ignore@5.3.2: {} + + ignore@7.0.9: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@2.0.5: {} + + is-bun-module@2.0.0: + dependencies: + semver: 7.8.5 + + is-callable@1.2.7: {} + + is-core-module@2.17.0: + dependencies: + hasown: 2.0.4 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-node-process@1.2.0: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.23 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jose@5.10.0: {} + + jose@6.2.12: {} + + js-base64@3.9.3: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.2.0 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kysely@0.29.6: {} + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + libsql@0.5.29: + dependencies: + '@neon-rs/load': 0.0.4 + detect-libc: 2.0.2 + optionalDependencies: + '@libsql/darwin-arm64': 0.5.29 + '@libsql/darwin-x64': 0.5.29 + '@libsql/linux-arm-gnueabihf': 0.5.29 + '@libsql/linux-arm-musleabihf': 0.5.29 + '@libsql/linux-arm64-gnu': 0.5.29 + '@libsql/linux-arm64-musl': 0.5.29 + '@libsql/linux-x64-gnu': 0.5.29 + '@libsql/linux-x64-musl': 0.5.29 + '@libsql/win32-x64-msvc': 0.5.29 + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@1.4.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + math-intrinsics@1.1.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mimic-fn@2.1.0: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.21 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + nanostores@1.5.3: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + next@16.3.3(@babel/core@7.29.7)(@playwright/test@1.63.0)(@types/node@22.20.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + dependencies: + '@next/env': 16.3.3 + '@swc/helpers': 0.5.23 + baseline-browser-mapping: 2.11.19 + caniuse-lite: 1.0.30001810 + postcss: 8.5.23 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8) + optionalDependencies: + '@next/swc-darwin-arm64': 16.3.3 + '@next/swc-darwin-x64': 16.3.3 + '@next/swc-linux-arm64-gnu': 16.3.3 + '@next/swc-linux-arm64-musl': 16.3.3 + '@next/swc-linux-x64-gnu': 16.3.3 + '@next/swc-linux-x64-musl': 16.3.3 + '@next/swc-win32-arm64-msvc': 16.3.3 + '@next/swc-win32-x64-msvc': 16.3.3 + '@playwright/test': 1.63.0 + sharp: 0.35.3(@types/node@22.20.1) + transitivePeerDependencies: + - '@babel/core' + - '@types/node' + - babel-plugin-macros + + node-exports-info@1.6.2: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.56: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + obug@2.2.1: {} + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + os-paths@4.4.0: {} + + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.7: {} + + playwright-core@1.63.0: {} + + playwright@1.63.0: + dependencies: + playwright-core: 1.63.0 + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.23: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.28: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + promise-limit@2.7.0: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react@19.2.8: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@2.0.0-next.7: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.17.0 + node-exports-info: 1.6.2 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + retry@0.13.1: {} + + reusify@1.1.0: {} + + rolldown@1.2.9: + dependencies: + '@oxc-project/types': 0.150.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.9 + '@rolldown/binding-android-arm64': 1.2.9 + '@rolldown/binding-darwin-arm64': 1.2.9 + '@rolldown/binding-darwin-x64': 1.2.9 + '@rolldown/binding-freebsd-x64': 1.2.9 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.9 + '@rolldown/binding-linux-arm64-gnu': 1.2.9 + '@rolldown/binding-linux-arm64-musl': 1.2.9 + '@rolldown/binding-linux-ppc64-gnu': 1.2.9 + '@rolldown/binding-linux-s390x-gnu': 1.2.9 + '@rolldown/binding-linux-x64-gnu': 1.2.9 + '@rolldown/binding-linux-x64-musl': 1.2.9 + '@rolldown/binding-openharmony-arm64': 1.2.9 + '@rolldown/binding-win32-arm64-msvc': 1.2.9 + '@rolldown/binding-win32-x64-msvc': 1.2.9 + + rou3@0.9.2: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + set-cookie-parser@3.1.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + + sharp@0.35.3(@types/node@22.20.1): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.3 + '@img/sharp-darwin-x64': 0.35.3 + '@img/sharp-freebsd-wasm32': 0.35.3 + '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-linux-arm': 0.35.3 + '@img/sharp-linux-arm64': 0.35.3 + '@img/sharp-linux-ppc64': 0.35.3 + '@img/sharp-linux-riscv64': 0.35.3 + '@img/sharp-linux-s390x': 0.35.3 + '@img/sharp-linux-x64': 0.35.3 + '@img/sharp-linuxmusl-arm64': 0.35.3 + '@img/sharp-linuxmusl-x64': 0.35.3 + '@img/sharp-webcontainers-wasm32': 0.35.3 + '@img/sharp-win32-arm64': 0.35.3 + '@img/sharp-win32-ia32': 0.35.3 + '@img/sharp-win32-x64': 0.35.3 + '@types/node': 22.20.1 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + stable-hash@0.0.5: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.matchall@4.1.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + stripe@22.6.2(@types/node@22.20.1): + optionalDependencies: + '@types/node': 22.20.1 + + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.8): + dependencies: + client-only: 0.0.1 + react: 19.2.8 + optionalDependencies: + '@babel/core': 7.29.7 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + throttleit@2.1.0: {} + + tinybench@6.1.4: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tsx@4.23.13: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.5: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.70.0(eslint@9.39.5)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/parser': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.70.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.70.0(eslint@9.39.5)(typescript@5.9.3) + eslint: 9.39.5 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + undici@6.28.0: {} + + unrs-resolver@1.12.2: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + + update-browserslist-db@1.3.3(browserslist@4.29.0): + dependencies: + browserslist: 4.29.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.9 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.1 + esbuild: 0.28.2 + fsevents: 2.3.3 + tsx: 4.23.13 + + vitest@5.0.1(@types/node@22.20.1)(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.1(vite@8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13)) + chai: 6.2.2 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.1 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 6.1.4 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + vite: 8.3.0(@types/node@22.20.1)(esbuild@0.28.2)(tsx@4.23.13) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.20.1 + transitivePeerDependencies: + - msw + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.23 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.23: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + ws@8.21.3: {} + + xdg-app-paths@5.5.1: + dependencies: + os-paths: 4.4.0 + xdg-portable: 7.3.0 + + xdg-portable@7.3.0: + dependencies: + os-paths: 4.4.0 + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.6.5): + dependencies: + zod: 4.6.5 + + zod@4.1.11: {} + + zod@4.6.5: {} + + zustand@5.0.15(@types/react@19.2.18)(react@19.2.8): + optionalDependencies: + '@types/react': 19.2.18 + react: 19.2.8 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..20efd30 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,13 @@ +allowBuilds: + esbuild: true + lightningcss: true +onlyBuiltDependencies: + - esbuild + - sharp +# The committed lockfile pins these official packages at versions published +# hours before pnpm's minimum-release-age cutoff; exempt just those names. +minimumReleaseAgeExclude: + - "@rollup/*" + - rollup + - next + - "@next/*" diff --git a/pricing.html b/pricing.html deleted file mode 100644 index 571ccc8..0000000 --- a/pricing.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - - Pricing - - - - - - - -
-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- picture -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-

Pricing

-
-
-
-
-
-
-
- -
-
- -
-
- -
-
- -
-
-
-
-
-
-
- - - - - - - - - \ No newline at end of file diff --git a/profile.html b/profile.html deleted file mode 100644 index fecf14b..0000000 --- a/profile.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - Profile - - - - - - - - -
-
-
-
-
-
- logo - -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - 4 -
-
-
-
- - 5 -
-
-
- picture -
-
- -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-

Profile

-
-
-
-
-
-
-
-
-

Ruth Baby

-
-
-
- # -
-
-

ruth@gmail.com

-

989-983-7397

-

27 Browns Bay New York

-

Developer

-
-
-
- -
-

Ongoing Task

-
- Someting -
- -
- Someting -
- -
- Someting -
- -
- Someting -
- -
-
-
-
-
-
- -
-
-

Enter your basic information

-
- - Upload photo
- -
- - - - - Save -
-
-

Use here to set up your account

-
- - - - Save -
-
-

Change your current password

-
- - - - Save -
-
-

Your security and Privacy settings

-
-
-
- Security -
-
-
- - -
- - - -
-
-
-
-
- Privacy -
- - Do not show my email address -
- - Send me newsletter -
- Do not display my contact information -
-
- -
-
- Save -
-
-
-
-
-
-
-
-
-
-
- - - - - - - - - - \ No newline at end of file diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000..a978792 Binary files /dev/null and b/public/icon-192.png differ diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000..6ee4e15 Binary files /dev/null and b/public/icon-512.png differ diff --git a/public/icon-maskable-512.png b/public/icon-maskable-512.png new file mode 100644 index 0000000..8271243 Binary files /dev/null and b/public/icon-maskable-512.png differ diff --git a/public/model-icons/flux.svg b/public/model-icons/flux.svg new file mode 100644 index 0000000..c46f935 --- /dev/null +++ b/public/model-icons/flux.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/grok.svg b/public/model-icons/grok.svg new file mode 100644 index 0000000..0ad47ed --- /dev/null +++ b/public/model-icons/grok.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/happy-horse.svg b/public/model-icons/happy-horse.svg new file mode 100644 index 0000000..1092fa7 --- /dev/null +++ b/public/model-icons/happy-horse.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/higgsfield.svg b/public/model-icons/higgsfield.svg new file mode 100644 index 0000000..59515d4 --- /dev/null +++ b/public/model-icons/higgsfield.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/ideogram.svg b/public/model-icons/ideogram.svg new file mode 100644 index 0000000..40c86bf --- /dev/null +++ b/public/model-icons/ideogram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/model-icons/kling.svg b/public/model-icons/kling.svg new file mode 100644 index 0000000..ca8f93b --- /dev/null +++ b/public/model-icons/kling.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/ltx.svg b/public/model-icons/ltx.svg new file mode 100644 index 0000000..42b3897 --- /dev/null +++ b/public/model-icons/ltx.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/model-icons/minimax.svg b/public/model-icons/minimax.svg new file mode 100644 index 0000000..869cc40 --- /dev/null +++ b/public/model-icons/minimax.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/pixverse.svg b/public/model-icons/pixverse.svg new file mode 100644 index 0000000..8b08c42 --- /dev/null +++ b/public/model-icons/pixverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/model-icons/qwen.svg b/public/model-icons/qwen.svg new file mode 100644 index 0000000..b3e57b0 --- /dev/null +++ b/public/model-icons/qwen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/model-icons/recraft.svg b/public/model-icons/recraft.svg new file mode 100644 index 0000000..1fa2a94 --- /dev/null +++ b/public/model-icons/recraft.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/wan.svg b/public/model-icons/wan.svg new file mode 100644 index 0000000..587e4c8 --- /dev/null +++ b/public/model-icons/wan.svg @@ -0,0 +1 @@ + diff --git a/public/model-icons/z-image.svg b/public/model-icons/z-image.svg new file mode 100644 index 0000000..59fda8c --- /dev/null +++ b/public/model-icons/z-image.svg @@ -0,0 +1 @@ + diff --git a/public/og.png b/public/og.png new file mode 100644 index 0000000..73230e6 Binary files /dev/null and b/public/og.png differ diff --git a/recursos/Simple-Notification-system-php-master.zip b/recursos/Simple-Notification-system-php-master.zip deleted file mode 100644 index 2f8a678..0000000 Binary files a/recursos/Simple-Notification-system-php-master.zip and /dev/null differ diff --git a/recursos/Simple-Notification-system-php-master/README.md b/recursos/Simple-Notification-system-php-master/README.md deleted file mode 100644 index f997013..0000000 --- a/recursos/Simple-Notification-system-php-master/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Simple-Notification-system-php -There are to files student1.php and student2.php -the index.php has a simple form , and there are options to make announcement for diffrect class students. -Here initially student1 and student 2 are two diffrenct classes as CSE101 and CSE102. -You can make announcement for diffrent class and the notification will show to the student. -ex: If you make announcement to student1 then it will show on student1.php. -Thats it Thanks. -for video : https://www.youtube.com/watch?v=ddki5_HVI10 diff --git a/recursos/Simple-Notification-system-php-master/index.php b/recursos/Simple-Notification-system-php-master/index.php deleted file mode 100644 index d517839..0000000 --- a/recursos/Simple-Notification-system-php-master/index.php +++ /dev/null @@ -1,59 +0,0 @@ -";*/} -else die("could not connect".mysqli_error()); - - date_default_timezone_set('Asia/Dhaka'); - $dat = date("d-m-y h:i:sa"); - echo "Time is : ".$dat; - -?> - - - - - do it - - - -
-
- - Announcement - Task
- - CSE101 - CSE102
- - -
- - - - \ No newline at end of file diff --git a/recursos/Simple-Notification-system-php-master/student1.php b/recursos/Simple-Notification-system-php-master/student1.php deleted file mode 100644 index 3fca989..0000000 --- a/recursos/Simple-Notification-system-php-master/student1.php +++ /dev/null @@ -1,52 +0,0 @@ -";*/} - else die("could not connect".mysqli_error()); - - - - $get_noti_qwr = "select * from 112_noti where seen_unseen = 'u'"; - $qry = mysqli_query($conn,$get_noti_qwr); - $count=mysqli_num_rows($qry); - - - -?> - - - - - sudo rm rf/ - - -
- " type="submit" name="submit" value="notification"/> -
- - It is:".$r['ann']."

"; - } - - - $update_query = "update 112_noti SET seen_unseen='s' where seen_unseen='u';"; - mysqli_query($conn,$update_query); - - - } - - ?> - - \ No newline at end of file diff --git a/recursos/Simple-Notification-system-php-master/student2.php b/recursos/Simple-Notification-system-php-master/student2.php deleted file mode 100644 index 824d965..0000000 --- a/recursos/Simple-Notification-system-php-master/student2.php +++ /dev/null @@ -1,52 +0,0 @@ -";*/} - else die("could not connect".mysqli_error()); - - - - $get_noti_qwr = "select * from 113_noti where seen_unseen = 'u'"; - $qry = mysqli_query($conn,$get_noti_qwr); - $count=mysqli_num_rows($qry); - - - -?> - - - - - sudo rm rf/ - - -
- " type="submit" name="submit" value="notification"/> -
- - It is:".$r['ann']."

"; - } - - - $update_query = "update 113_noti SET seen_unseen='s' where seen_unseen='u';"; - mysqli_query($conn,$update_query); - - - } - - ?> - - \ No newline at end of file diff --git a/register.php b/register.php deleted file mode 100644 index adefa9f..0000000 --- a/register.php +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - Register - - - - - - -
-
-
-
- logo - -
- - - - - - - - -
- Back to Login - Fields Left Blank", "

Some Fields were left blank. Please fill up all fields.

"; - }elseif( !$LS->validEmail($email) ){ - echo "

E-Mail Is Not Valid

", "

The E-Mail you gave is not valid

"; - }elseif( !ctype_alnum($username) ){ - echo "

Invalid Username

", "

The Username is not valid. Only ALPHANUMERIC characters are allowed and shouldn't exceed 10 characters.

"; - }elseif($password != $retyped_password){ - echo "

Passwords Don't Match

", "

The Passwords you entered didn't match

"; - }else{ - $createAccount = $LS->register($username, $password, - array( - "email" => $email, - "name" => $name, - "created" => date("Y-m-d H:i:s") // Just for testing - ) - ); - if($createAccount === "exists"){ - echo ""; - }elseif($createAccount === true){ - echo ""; - } - } - } - ?> -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/reset.php b/reset.php deleted file mode 100644 index 2407e06..0000000 --- a/reset.php +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - Reset Password - - - - - - -
-
-
-
- logo - - forgotPassword(); - ?> -
-
- -
-
- - - - - - - \ No newline at end of file diff --git a/reset2.php b/reset2.php deleted file mode 100644 index b2c717c..0000000 --- a/reset2.php +++ /dev/null @@ -1,9 +0,0 @@ - - - - forgotPassword(); - ?> - - diff --git a/scripts/build-brand-assets.mjs b/scripts/build-brand-assets.mjs new file mode 100644 index 0000000..38757e6 --- /dev/null +++ b/scripts/build-brand-assets.mjs @@ -0,0 +1,187 @@ +/* Builds every raster brand asset from the one mark geometry. + + The mark is authored once as SVG (src/app/icon.svg for the favicon, the + CSS .vt-side-mark for the interface). Apple, the web app manifest and Open + Graph all need rasters, so this script draws the same 32-unit field in a + headless Chromium and screenshots it at each size. + + node scripts/build-brand-assets.mjs + + Chromium comes from a local Playwright install; point CHROME_BIN at any + Chrome build to use a different one. Outputs are committed — this runs when + the mark changes, not on every build. */ + +import { execFileSync } from "node:child_process"; +import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { homedir, tmpdir } from "node:os"; +import { join, resolve } from "node:path"; + +/* ---------- the mark ---------- */ + +const PLATE = "#0e1011"; +const ACCENT = "#d1fe17"; +const INK = "#c2c9c8"; + +/** The Vitrina mark on a 32-unit field: a shop window — an open frame — with + the product standing on its shelf as the one point of accent. Keep in step + with src/app/icon.svg. */ +function brackets({ stroke = 2.5, accent = ACCENT, ink = INK } = {}) { + return ` + + + + + `; +} + +const markSvg = (size, opts) => + `${brackets(opts)}`; + +/* The plate edge is one unit — a true hairline — at favicon size. In the large + rasters it has to stay a hairline, so there it scales as a fraction of a unit. */ +const roundedPlate = (size) => ` + + + ${brackets()} +`; + +/** Full-bleed square: Apple and the maskable spec apply their own mask. */ +const bleedPlate = (size, scale) => ` + + ${brackets()} +`; + +const GRAIN = + "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E\")"; + +/* ---------- renderer ---------- */ + +function findChrome() { + const candidates = [ + process.env.CHROME_BIN, + "/opt/pw-browsers/chromium-1194/chrome-linux/chrome", + ...["1237", "1228"].map((build) => + join( + homedir(), + "Library/Caches/ms-playwright", + `chromium-${build}/chrome-mac-arm64`, + "Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing", + ), + ), + "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Applications/Chromium.app/Contents/MacOS/Chromium", + ].filter(Boolean); + + const found = candidates.find((path) => existsSync(path)); + if (found) return found; + throw new Error( + "No Chrome found. Install one, or set CHROME_BIN to a Chrome/Chromium binary.", + ); +} + +const CHROME = findChrome(); +const ROOT = resolve(import.meta.dirname, ".."); +const WORK = mkdtempSync(join(tmpdir(), "vitrina-brand-")); + +function shoot(out, html, width, height, { transparent = false } = {}) { + const page = join(WORK, `${width}x${height}-${Math.abs(hash(out))}.html`); + writeFileSync(page, html); + mkdirSync(resolve(out, ".."), { recursive: true }); + execFileSync( + CHROME, + [ + "--headless", + "--no-sandbox", + "--disable-gpu", + "--hide-scrollbars", + "--force-device-scale-factor=1", + "--virtual-time-budget=6000", + ...(transparent ? ["--default-background-color=00000000"] : []), + `--window-size=${width},${height}`, + `--screenshot=${out}`, + `file://${page}`, + ], + { stdio: ["ignore", "ignore", "pipe"] }, + ); + console.log(` ${out.replace(`${ROOT}/`, "")} ${width}×${height}`); +} + +function hash(str) { + let h = 0; + for (let i = 0; i < str.length; i++) h = (Math.imul(h, 31) + str.charCodeAt(i)) | 0; + return h; +} + +const shell = (body, bg = "transparent") => + `${body}`; + +/* ---------- app icons ---------- */ + +console.log("app icons"); +shoot(join(ROOT, "public/icon-512.png"), shell(roundedPlate(512)), 512, 512, { + transparent: true, +}); +shoot(join(ROOT, "public/icon-192.png"), shell(roundedPlate(192)), 192, 192, { + transparent: true, +}); +// Maskable keeps the mark inside the 80%-diameter safe circle. +shoot(join(ROOT, "public/icon-maskable-512.png"), shell(bleedPlate(512, 0.82)), 512, 512); +shoot(join(ROOT, "src/app/apple-icon.png"), shell(bleedPlate(180, 1)), 180, 180); + +/* ---------- open graph card ---------- + No stand-in tiles standing in for generated work we do not have. The only + device besides the type is the mark itself, drawn large and hairline-thin: + the card sits inside the field the mark names. */ + +const og = ` + + + + +
${markSvg(454, { stroke: 1.15, accent: "rgba(209,254,23,0.2)", ink: "rgba(255,255,255,0.085)" })}
+
${markSvg(58, { stroke: 2.5 })}
+
+

Vitrina

+
AI product visuals for e-commerce
+
+

Product photos and clips for every channel, from the photo you already have. Presets, brand kit, channels.

+
+
`; + +console.log("open graph"); +// public/, not app/opengraph-image.png — see the OG_IMAGE note in src/site.ts. +shoot(join(ROOT, "public/og.png"), og, 1200, 630); + +rmSync(WORK, { recursive: true, force: true }); diff --git a/scripts/credits.mjs b/scripts/credits.mjs new file mode 100644 index 0000000..2c5abc6 --- /dev/null +++ b/scripts/credits.mjs @@ -0,0 +1,36 @@ +/* Grants credits to a workspace by owner email — the self-hosted operator's + substitute for Stripe. Usage: pnpm credits owner@example.com 500 ["note"] */ +import { createClient } from "@libsql/client"; +import { randomUUID } from "node:crypto"; + +const [email, amountText, note = "manual grant"] = process.argv.slice(2); +const amount = Number(amountText); +if (!email || !Number.isInteger(amount) || amount === 0) { + console.error("usage: pnpm credits [note]"); + process.exit(1); +} +const url = process.env.DATABASE_URL?.trim() || "file:./data/vitrina.db"; +const client = createClient({ url, authToken: process.env.DATABASE_AUTH_TOKEN?.trim() || undefined }); +const user = await client.execute({ sql: "select id from user where email = ?", args: [email] }); +const userId = user.rows[0]?.id; +if (!userId) { + console.error(`no user with email ${email}`); + process.exit(1); +} +const ws = await client.execute({ sql: "select workspace_id from membership where user_id = ? order by created_at limit 1", args: [userId] }); +const workspaceId = ws.rows[0]?.workspace_id; +if (!workspaceId) { + console.error("user has no workspace"); + process.exit(1); +} +const now = Date.now(); +await client.batch( + [ + { sql: "insert into credit_ledger (id, workspace_id, delta, reason, note, created_at) values (?, ?, ?, 'adjustment', ?, ?)", args: [randomUUID(), workspaceId, amount, note, now] }, + { sql: "update workspace set credit_balance = credit_balance + ?, updated_at = ? where id = ?", args: [amount, now, workspaceId] }, + ], + "write", +); +const after = await client.execute({ sql: "select credit_balance from workspace where id = ?", args: [workspaceId] }); +console.log(`[credits] ${amount > 0 ? "+" : ""}${amount} → balance ${after.rows[0]?.credit_balance}`); +client.close(); diff --git a/scripts/migrate.mjs b/scripts/migrate.mjs new file mode 100644 index 0000000..c982934 --- /dev/null +++ b/scripts/migrate.mjs @@ -0,0 +1,12 @@ +import { createClient } from "@libsql/client"; +import { drizzle } from "drizzle-orm/libsql"; +import { migrate } from "drizzle-orm/libsql/migrator"; +import { mkdirSync } from "node:fs"; + +const url = process.env.DATABASE_URL?.trim() || "file:./data/vitrina.db"; +if (url.startsWith("file:")) mkdirSync(url.slice(5).replace(/\/[^/]*$/, "") || ".", { recursive: true }); + +const client = createClient({ url, authToken: process.env.DATABASE_AUTH_TOKEN?.trim() || undefined }); +await migrate(drizzle(client), { migrationsFolder: "./drizzle" }); +console.log(`[migrate] database up to date (${url.startsWith("file:") ? url : "remote"})`); +client.close(); diff --git a/scripts/mock-platform.mjs b/scripts/mock-platform.mjs new file mode 100644 index 0000000..4516182 --- /dev/null +++ b/scripts/mock-platform.mjs @@ -0,0 +1,73 @@ +/* A stand-in for the generation platform, for development and end-to-end + tests. Speaks the two calls the studio makes — POST /{model} and + GET /requests/{id}/status — and answers every run with a placeholder + image or clip after a short delay. Start with: pnpm mock:platform */ +import { createServer } from "node:http"; + +const PORT = Number(process.env.MOCK_PLATFORM_PORT ?? 4010); +const DELAY_MS = Number(process.env.MOCK_PLATFORM_DELAY_MS ?? 1500); +const requests = new Map(); + +const PNG = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="; + +function json(res, status, body) { + res.writeHead(status, { "content-type": "application/json", "access-control-allow-origin": "*" }); + res.end(JSON.stringify(body)); +} + +function readBody(req) { + return new Promise((resolve) => { + let data = ""; + req.on("data", (chunk) => (data += chunk)); + req.on("end", () => { + try { + resolve(data ? JSON.parse(data) : {}); + } catch { + resolve({}); + } + }); + }); +} + +const server = createServer(async (req, res) => { + const url = new URL(req.url ?? "/", `http://localhost:${PORT}`); + const origin = `http://localhost:${PORT}`; + + if (req.method === "GET" && url.pathname.startsWith("/media/")) { + const isVideo = url.pathname.endsWith(".mp4"); + res.writeHead(200, { "content-type": isVideo ? "video/mp4" : "image/png", "access-control-allow-origin": "*" }); + res.end(Buffer.from(PNG, "base64")); + return; + } + + const status = /^\/requests\/([^/]+)\/status$/.exec(url.pathname); + if (req.method === "GET" && status) { + const entry = requests.get(status[1]); + if (!entry) return json(res, 404, { detail: "Unknown request" }); + if (Date.now() < entry.readyAt) return json(res, 200, { request_id: entry.id, status: "in_progress" }); + if (entry.fail) return json(res, 200, { request_id: entry.id, status: "failed", error: "mock platform failure" }); + if (entry.kind === "video") { + return json(res, 200, { request_id: entry.id, status: "completed", video: { url: `${origin}/media/${entry.id}.mp4` } }); + } + const images = Array.from({ length: entry.count }, (_, index) => ({ url: `${origin}/media/${entry.id}-${index}.png` })); + return json(res, 200, { request_id: entry.id, status: "completed", images }); + } + + if (req.method === "POST") { + if (!/^Key [^:]+:.+$/.test(req.headers.authorization ?? "")) return json(res, 401, { detail: "Missing key" }); + const body = await readBody(req); + const model = url.pathname.slice(1); + const id = `mock-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; + const kind = /video|seedance|kling|wan|ltx|hailuo|pixverse|dop|happy-horse|minimax/.test(model) ? "video" : "image"; + const count = Number(body.batch_size ?? body.num_images ?? 1) || 1; + const fail = typeof body.prompt === "string" && body.prompt.includes("MOCK_FAIL"); + requests.set(id, { id, kind, count, fail, readyAt: Date.now() + DELAY_MS }); + console.log(`[mock] ${model} → ${id} (${kind}${fail ? ", will fail" : ""})`); + return json(res, 200, { request_id: id, status: "queued", status_url: `${origin}/requests/${id}/status`, cancel_url: "" }); + } + + json(res, 404, { detail: "Not found" }); +}); + +server.listen(PORT, () => console.log(`[mock] platform listening on http://localhost:${PORT}`)); diff --git a/scripts/start-standalone.mjs b/scripts/start-standalone.mjs new file mode 100644 index 0000000..6e7f8cb --- /dev/null +++ b/scripts/start-standalone.mjs @@ -0,0 +1,20 @@ +/* Serves the production build the way the Dockerfile does: the standalone + server, with the static assets and public folder laid beside it. Next + leaves that copy to the deployer; this does it once and starts. */ +import { cpSync, existsSync } from "node:fs"; +import { spawn } from "node:child_process"; +import { resolve } from "node:path"; + +const root = resolve(import.meta.dirname, ".."); +const standalone = resolve(root, ".next/standalone"); +if (!existsSync(resolve(standalone, "server.js"))) { + console.error("[start] no standalone build found — run `pnpm build` first"); + process.exit(1); +} +cpSync(resolve(root, ".next/static"), resolve(standalone, ".next/static"), { recursive: true }); +cpSync(resolve(root, "public"), resolve(standalone, "public"), { recursive: true }); +cpSync(resolve(root, "drizzle"), resolve(standalone, "drizzle"), { recursive: true }); + +const child = spawn(process.execPath, ["server.js"], { cwd: standalone, stdio: "inherit", env: process.env }); +child.on("exit", (code) => process.exit(code ?? 0)); +for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child.kill(signal)); diff --git a/scripts/wipe-local.mjs b/scripts/wipe-local.mjs new file mode 100644 index 0000000..06edb0d --- /dev/null +++ b/scripts/wipe-local.mjs @@ -0,0 +1,59 @@ +/* Resets the local environment to a clean slate. What goes: + data/ the local database and uploads (DATA_DIR) + .next/ the build output + test-results/ playwright-report/ test artifacts + node_modules/ only with --deps (then reinstalls) + What stays: .env, source, git. Then the database is recreated by running + the migrations, so `pnpm dev` starts on an empty studio. + + node scripts/wipe-local.mjs [--deps] [--keep-uploads] [--yes] [--dry-run] +*/ +import { execSync } from "node:child_process"; +import { existsSync, readdirSync, rmSync } from "node:fs"; +import { resolve } from "node:path"; +import { createInterface } from "node:readline/promises"; + +const args = new Set(process.argv.slice(2)); +const root = resolve(import.meta.dirname, ".."); +const dataDir = resolve(root, process.env.DATA_DIR?.trim() || "./data"); +const dryRun = args.has("--dry-run"); + +const remoteDb = Boolean(process.env.DATABASE_URL?.trim() && !process.env.DATABASE_URL.trim().startsWith("file:")); +if (remoteDb) { + console.error("[wipe-local] DATABASE_URL points at a remote database. This script only resets local files; unset it or point it at a file: URL."); + process.exit(1); +} + +const targets = []; +if (args.has("--keep-uploads")) { + for (const entry of existsSync(dataDir) ? readdirSync(dataDir, { withFileTypes: true }) : []) { + if (entry.name !== "uploads") targets.push(resolve(dataDir, entry.name)); + } +} else { + targets.push(dataDir); +} +targets.push(resolve(root, ".next"), resolve(root, "test-results"), resolve(root, "playwright-report")); +if (args.has("--deps")) targets.push(resolve(root, "node_modules")); + +const present = targets.filter((path) => existsSync(path)); +console.log("[wipe-local] will remove:"); +for (const path of present) console.log(` ${path.replace(`${root}/`, "")}`); +if (present.length === 0) console.log(" (nothing — already clean)"); + +if (dryRun) process.exit(0); +if (!args.has("--yes") && process.stdin.isTTY) { + const rl = createInterface({ input: process.stdin, output: process.stdout }); + const answer = (await rl.question("Type RESET to continue: ")).trim(); + rl.close(); + if (answer !== "RESET") { + console.log("[wipe-local] aborted"); + process.exit(1); + } +} + +for (const path of present) rmSync(path, { recursive: true, force: true }); + +const run = (command) => execSync(command, { cwd: root, stdio: "inherit" }); +if (args.has("--deps")) run("pnpm install --frozen-lockfile"); +run("node scripts/migrate.mjs"); +console.log("[wipe-local] done — `pnpm dev` starts on an empty studio"); diff --git a/src/LS.php b/src/LS.php deleted file mode 100644 index 961a638..0000000 --- a/src/LS.php +++ /dev/null @@ -1,1344 +0,0 @@ - array( - "company" => "Advance Mobile Advertising", - "email" => "sergio.suarez@advancemobile.net", - "email_callback" => false, - - /** - * Callback to override output content - */ - "output_callback" => false - ), - - /** - * Database Configuration - */ - "db" => array( - /** - * @var string "mysql" or "postgresql" or "sqlite" - */ - "type" => "mysql", - - /** - * MySQL options - */ - "host" => "b672472483.db.1and1.com", - "port" => "3306", - - /** - * SQLite options - */ - "sqlite_path" => "/home/simsu/mydb.sqlite", - - "username" => "dbo672472483", - "password" => "sOulm4dnessCocu", - - "name" => "db672472483", - "table" => "users", - "token_table" => "user_tokens", - - "columns" => array( - "id" => "id", - "username" => "username", - "password" => "password", - "email" => "email", - "attempt" => "attempt" - ), - ), - - /** - * Keys used for encryption - * DONT MAKE THIS PUBLIC - */ - "keys" => array( - /** - * Changing cookie key will expire all current active login sessions - */ - "cookie" => "ckxc436jd*^30f840v*9!@#$", - /** - * `salt` should not be changed after users are created - */ - "salt" => "^#$4%9f+1^p9)M@4M)V$" - ), - - /** - * Enable/Disable certain features - */ - "features" => array( - /** - * Should I Call session_start(); - */ - "start_session" => true, - /** - * Enable/Disable Login using Username & E-Mail - */ - "email_login" => true, - /** - * Enable/Disable `Remember Me` feature - */ - "remember_me" => true, - /** - * Should \Fr\LS::init() be called automatically - */ - "auto_init" => false, - - /** - * Prevent Brute Forcing - * --------------------- - * By enabling this, logSys will deny login for the time mentioned - * in the "brute_force"->"time_limit" seconds after "brute_force"->"tries" - * number of incorrect login tries. - */ - "block_brute_force" => true, - - /** - * Two Step Login - * -------------- - * By enabling this, a checking is done when user visits - * whether the device he/she uses is approved by the user. - * Allows the original user to revoke logins in other devices/places - * Useful if the user forgot to logout in some place. - */ - "two_step_login" => false - ), - - /** - * `Blocking Brute Force Attacks` options - */ - "brute_force" => array( - /** - * No of tries alloted to each user - */ - "tries" => 5, - /** - * The time IN SECONDS for which block from login action should be done after - * incorrect login attempts. Use http://www.easysurf.cc/utime.htm#m60s - * for converting minutes to seconds. Default : 5 minutes - */ - "time_limit" => 300 - ), - - /** - * Information about pages - */ - "pages" => array( - /** - * Pages that doesn't require logging in. - * Exclude login page, but include REGISTER page. - * Use RELATIVE links. To find the relative link of - * a page, do var_dump(Fr\LS::curPage()); - */ - "no_login" => array(), - - /** - * Pages that both logged in and not logged in users can access - */ - "everyone" => array(), - - /** - * The login page. ex : /login.php or /accounts/login.php - */ - "login_page" => "/advanceERP/login.php", - - /** - * The home page. The main page for logged in users. - * logSys redirects to here after user logs in - */ - "home_page" => "/advanceERP/index.php" - ), - - /** - * Settings about cookie creation - */ - "cookies" => array( - /** - * Default : cookies expire in 30 days. The value is - * for setting in strtotime() function - * http://php.net/manual/en/function.strtotime.php - */ - "expire" => "+30 days", - "path" => "/", - "domain" => "", - - /** - * Names of cookies created - */ - "names" => array( - "login_token" => "lg", - "remember_me" => "rm", - "device" => "dv", - - /** - * This is used as $_SESSION key - */ - "current_user" => "cu", - "device_verified" => "dvv" - ), - ), - - /** - * 2 Step Login - */ - 'two_step_login' => array( - /** - * Message to show before displaying "Enter Token" form. - */ - 'instruction' => '', - - /** - * Callback when token is generated. - * Used to send message to user (Phone/E-Mail) - */ - 'send_callback' => '', - - /** - * The table to stoe user's sessions - */ - 'devices_table' => 'user_devices', - - /** - * The length of token generated. - * A low value is better for tokens sent via Mobile SMS - */ - 'token_length' => 4, - - /** - * Whether the token should be numeric only ? - * Default Token : Alphabetic + Numeric mixed strings - */ - 'numeric' => false, - - /** - * The expire time of cookie that authorizes the device - * to login using the user's account with 2 Step Verification - * The value is for setting in strtotime() function - * http://php.net/manual/en/function.strtotime.php - */ - 'expire' => '+45 days', - - /** - * Should logSys checks if device is valid, everytime - * logSys is initiated ie everytime a page loads - * If you want to check only the first time a user loads - * a page, then set the value to TRUE, else FALSE - */ - 'first_check_only' => true - ), - - /** - * Debug info - */ - "debug" => array( - /** - * Enable debugging - */ - "enable" => false, - - /** - * Absolute path - */ - "log_file" => "", - ) - ); - - /** - * @var array - */ - private $config = array(); - - /** - * Config - * @param array $config [description] - * @return [type] [description] - */ - public function config($config = array()){ - /** - * Callback to display messages for different states - * @var array - */ - self::$default_config["basic"]["output_callback"] = function(&$LS, $state, $extraInfo = array()){ - if($state === "invalidToken") - return "

Error : Wrong/Invalid Token

"; - else if($state === "fieldsLeftBlank") - return "

Error : Fields Left Blank

"; - else if($state === "passwordDontMatch") - return "

Error : Passwords Don't Match

"; - else if($state === "passwordChanged") - return "

Success : Password Reset Successful

You may now login with your new password.

"; - else if($state === "identityNotProvided") - return "

Error : {$extraInfo["identity_type"]} not provided

"; - else if($state === "userNotFound") - return "

Error : User Not Found

"; - else if($state === "notLoggedIn") - return "

Error : Not Logged In

"; - else if($state === "twoStepLoginVerifyForm") - return "
-

". $LS->config['two_step_login']['instruction'] ."

- - - - ". ($extraInfo["remember_me"] === true ? "" : "") ." - -
"; - else if($state === "resetPasswordRequestForm") - return "
- -

-
- Back to Login"; - else if($state === "resetPasswordForm") - return "

The Token key was Authorized. Now, you can change the password

-
- -
-
-

-
"; - }; - - $this->config = array_replace_recursive(self::$default_config, $config); - - /** - * Add the login page to the array of pages that doesn't need logging in - */ - array_push($this->config['pages']['no_login'], $this->config['pages']['login_page']); - - if( $this->config["debug"]["enable"] ){ - ini_set("display_errors", "on"); - } - } - - /** - * Add messages to log file - * - * @param string $msg Message - * @return boolean Whether message was written - */ - public function log($msg = ""){ - if( $this->config["debug"]["enable"] ){ - $log_file = $this->config["debug"]["log_file"]; - - if( $log_file === "" ) - $log_file = __DIR__ . "/Francium.log"; - - if( $msg !== "" ){ - $message = "[" . date("Y-m-d H:i:s") . "] $msg"; - $fh = fopen($log_file, 'a'); - fwrite($fh, $message . "\n"); - fclose($fh); - return true; - } - } - return false; - } - - /** - * @var boolean Is user logged in - */ - public $loggedIn = false; - - /** - * @var int|boolean User ID - */ - public $userID = false; - - /** - * @var \PDO Database handler - */ - private $dbh; - - /** - * @var boolean Whether Fr\LS::init() was called - */ - private $initCalled = false; - - /** - * Intialize - * @param array $config Configuration - */ - public function __construct($config = array()){ - $this->config( $config ); - - if( $this->config['features']['start_session'] === true ){ - session_start(); - } - - /** - * Try connecting to Database Server - */ - try{ - if( $this->config["db"]["type"] === "sqlite" ) { - - $this->dbh = new \PDO("sqlite:" . $this->config["db"]["sqlite_path"], - $this->config["db"]["username"], - $this->config["db"]["password"], - array( - \PDO::ATTR_PERSISTENT => true, - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION - ) - ); - - /** - * Enable Multithreading Read/Write - */ - $this->dbh->exec("PRAGMA journal_mode=WAL;"); - - } else if( $this->config["db"]["type"] === "postgresql" ) { - - $this->dbh = new \PDO("pgsql:dbname=". $this->config['db']['name'] .";host=". $this->config['db']['host'] .";port=". $this->config['db']['port']. ";", - $this->config['db']['username'], - $this->config['db']['password'], - array( - \PDO::ATTR_PERSISTENT => true, - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION - ) - ); - - } else { - - $this->dbh = new \PDO("mysql:dbname=". $this->config['db']['name'] .";host=". $this->config['db']['host'] .";port=". $this->config['db']['port']. ";charset=utf8", - $this->config['db']['username'], - $this->config['db']['password'], - array( - \PDO::ATTR_PERSISTENT => true, - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION - )); - - } - - $cookieToken = isset($_COOKIE[$this->config['cookies']['names']['login_token']]) ? $_COOKIE[ $this->config['cookies']['names']['login_token'] ] : false; - - /** - * @var int|boolean User ID is stored in session - */ - $sessionUID = isset( $_SESSION[ $this->config['cookies']['names']['current_user'] ] ) ? $_SESSION[ $this->config['cookies']['names']['current_user'] ] : false; - - $rememberMe = isset( $_COOKIE[ $this->config['cookies']['names']['remember_me'] ] ) ? $_COOKIE[ $this->config['cookies']['names']['remember_me'] ] : false; - - if($cookieToken) { - $loginToken = hash("sha256", $this->config['keys']['cookie'] . $sessionUID . $this->config['keys']['cookie']); - - $this->loggedIn = $cookieToken === $loginToken; - } - - /** - * If there is a Remember Me Cookie and the user is not logged in, - * then log in the user with the ID in the remember cookie if it - * matches with the decrypted value in `logSyslogin` cookie - */ - if( $this->config['features']['remember_me'] === true && $rememberMe && !$this->loggedIn ) { - $loginToken = hash("sha256", $this->config['keys']['cookie']. $rememberMe . $this->config['keys']['cookie']); - - $this->loggedIn = $loginToken === $loginToken; - - if($this->loggedIn === true){ - $_SESSION[$this->config["cookies"]["names"]["current_user"]] = $rememberMe; - $sessionUID = $rememberMe; - } - } - - $this->userID = $sessionUID; - - /** - * Check if devices is authorized to use the account - */ - if($this->config['features']['two_step_login'] === true && $this->loggedIn){ - $login_page = self::curPage() === $this->config['pages']['login_page']; - - if(!isset($_SESSION[ $this->config['cookies']['names']['device_verified'] ]) && !isset($_COOKIE[$this->config["cookies"]["names"]["device"]]) && $login_page === false){ - /** - * The device cookie is not even set. So, logout - */ - $this->logout(); - }else if($this->config['two_step_login']['first_check_only'] === false || ($this->config['two_step_login']['first_check_only'] === true && !isset($_SESSION[ $this->config['cookies']['names']['device_verified'] ]))){ - $sql = $this->dbh->prepare("SELECT '1' FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ? AND token = ?"); - $sql->execute(array($this->userID, $_COOKIE[$this->config["cookies"]["names"]["device"]])); - - /** - * Device not authorized, so remove device cookie & logout - */ - if($sql->fetchColumn() !== '1' && $login_page === false){ - setcookie($this->config["cookies"]["names"]["device"], "", time() - 10); - $this->logout(); - }else{ - /** - * This session has been checked and verified - */ - $_SESSION[ $this->config['cookies']['names']['device_verified'] ] = 1; - } - } - } - - if( $this->config['features']['auto_init'] === true ){ - $this->init(); - } - return true; - }catch(\PDOException $e) { - /** - * Couldn't connect to Database - */ - self::log("Could not connect to database. Check config->db credentials. PDO Output: " . $e->getMessage()); - return false; - } - } - - /** - * A function that will automatically redirect user according to his/her login status - * @return void - */ - public function init() { - if(in_array(self::curPage(), $this->config['pages']['everyone'])){ - /** - * No redirects as this page can be accessed - * by anyone whether he/she is logged in or not - */ - }else if($this->loggedIn === true && in_array(self::curPage(), $this->config['pages']['no_login'])){ - self::redirect($this->config['pages']['home_page']); - }else if($this->loggedIn === false && array_search(self::curPage(), $this->config['pages']['no_login']) === false){ - self::redirect($this->config['pages']['login_page']); - } - $this->initCalled = true; - } - - /** - * Login the user - * @param string $username Username or email - * @param boolean|string $password Password as string or - * FALSE for skipping password check - * @param boolean $remember_me Remember me - * @param boolean $cookies Should cookies be created and redirected - * @return array|boolean|string Array if user is blocked - * TRUE if login was success - * User ID if login was success and $cookies FALSE - */ - public function login($username, $password, $remember_me = false, $cookies = true){ - /** - * We Add LIMIT to 1 in SQL query because to - * get an array with key as the column name. - */ - if($this->config['features']['email_login'] === true){ - $query = "SELECT ". $this->config["db"]["columns"]["id"] .", ". $this->config["db"]["columns"]["password"] .", ". $this->config["db"]["columns"]["attempt"] ." FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login OR ". $this->config["db"]["columns"]["email"] ."=:login ORDER BY ". $this->config["db"]["columns"]["id"] ." LIMIT 1"; - }else{ - $query = "SELECT ". $this->config["db"]["columns"]["id"] .", ". $this->config["db"]["columns"]["password"] .", ". $this->config["db"]["columns"]["attempt"] ." FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login ORDER BY ". $this->config["db"]["columns"]["id"] ." LIMIT 1"; - } - - $sql = $this->dbh->prepare($query); - $sql->bindValue(":login", $username); - - $sql->execute(); - $cols = $sql->fetch(\PDO::FETCH_ASSOC); - - if(empty($cols)){ - // No such user like that - return false; - }else{ - /** - * Get the user details - */ - $us_id = $cols['id']; - $us_pass = $cols['password']; - $status = $cols['attempt']; - - if(substr($status, 0, 2) == "b-"){ - $blockedTime = substr($status, 2); - if(time() < $blockedTime){ - $blocked = true; - return array( - "status" => "blocked", - "minutes" => round(abs($blockedTime - time()) / 60, 0), - "seconds" => round(abs($blockedTime - time()) / 60*60, 2) - ); - }else{ - // remove the block, because the time limit is over - $this->updateUser(array( - "attempt" => "" // No tries at all - ), $us_id); - } - } - - if( !isset($blocked) && ($password === false || password_verify($password . $this->config['keys']['salt'], $us_pass) ) ){ - if($cookies === true){ - - $_SESSION[$this->config["cookies"]["names"]["current_user"]] = $us_id; - - setcookie($this->config["cookies"]["names"]["login_token"], hash("sha256", $this->config['keys']['cookie'] . $us_id . $this->config['keys']['cookie']), strtotime($this->config['cookies']['expire']), $this->config['cookies']['path'], $this->config['cookies']['domain']); - - if( $remember_me === true && $this->config['features']['remember_me'] === true ){ - setcookie($this->config["cookies"]["names"]["remember_me"], $us_id, strtotime($this->config['cookies']['expire']), $this->config['cookies']['path'], $this->config['cookies']['domain']); - } - $this->loggedIn = true; - - if($this->config['features']['block_brute_force'] === true){ - /** - * If Brute Force Protection is Enabled, - * Reset the attempt status - */ - $this->updateUser(array( - "attempt" => "0" - ), $us_id); - } - - // Redirect - if($this->initCalled){ - self::redirect($this->config['pages']['home_page']); - } - return true; - }else{ - /** - * If cookies shouldn't be set, - * it means login() was called - * to get the user's ID. So, return it - */ - return $us_id; - } - }else{ - /** - * Incorrect password - * ------------------ - * Check if brute force protection is enabled - */ - if($this->config['features']['block_brute_force'] === true){ - $max_tries = $this->config['brute_force']['tries']; - - if($status == ""){ - // User was not logged in before - $this->updateUser(array( - "attempt" => "1" // Tried 1 time - ), $us_id); - }else if($status == $max_tries){ - /** - * Account Blocked. User will be only able to - * re-login at the time in UNIX timestamp - */ - $eligible_for_next_login_time = strtotime("+". $this->config['brute_force']['time_limit'] ." seconds", time()); - $this->updateUser(array( - "attempt" => "b-" . $eligible_for_next_login_time - ), $us_id); - return array( - "status" => "blocked", - "minutes" => round(abs($eligible_for_next_login_time - time()) / 60, 0), - "seconds" => round(abs($eligible_for_next_login_time - time()) / 60*60, 2) - ); - }else if($status < $max_tries){ - // If the attempts are less than Max and not Max - $this->updateUser(array( - "attempt" => $status + 1 // Increase the no of tries by +1. - ), $us_id); - } - } - return false; - } - } - } - - /** - * Register a user - * @param string $identification Username or email - * @param string $password Password - * @param array $other Values for other columns - * @return boolean|string Whether user exists (exists) or account was created - */ - public function register( $identification, $password, $other = array() ){ - if( $this->userExists($identification) || (isset($other['email']) && $this->userExists($other['email'])) ){ - return "exists"; - }else{ - $hashedPass = password_hash($password. $this->config['keys']['salt'], PASSWORD_DEFAULT); - - if( count($other) == 0 ){ - /* If there is no other fields mentioned, make the default query */ - $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['table'] ." (". $this->config["db"]["columns"]["username"] .", ". $this->config["db"]["columns"]["password"] .") VALUES(:username, :password)"); - }else{ - /* if there are other fields to add value to, make the query and bind values according to it */ - $keys = array_keys($other); - $columns = implode(",", $keys); - $colVals = implode(",:", $keys); - $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['table'] ." (". $this->config["db"]["columns"]["username"] .", ". $this->config["db"]["columns"]["password"] .", $columns) VALUES(:username, :password, :$colVals)"); - foreach($other as $key => $value){ - $value = htmlspecialchars($value); - $sql->bindValue(":$key", $value); - } - } - /* Bind the default values */ - $sql->bindValue(":username", $identification); - $sql->bindValue(":password", $hashedPass); - $sql->execute(); - return true; - } - } - - /** - * Logout the user by destroying the cookies and session - */ - public function logout(){ - session_destroy(); - setcookie($this->config["cookies"]["names"]["login_token"], "", time() - 10, $this->config['cookies']['path'], $this->config['cookies']['domain']); - setcookie($this->config["cookies"]["names"]["remember_me"], "", time() - 10, $this->config['cookies']['path'], $this->config['cookies']['domain']); - - /** - * Wait for the cookies to be removed, then redirect - */ - usleep(2000); - self::redirect($this->config['pages']['login_page']); - return true; - } - - /** - * A function to handle the Forgot Password process - * @return string The current state of the process - */ - public function forgotPassword(){ - $curStatus = "initial"; // The Current Status of Forgot Password process - $identName = $this->config['features']['email_login'] === false ? "Username" : "Username / E-Mail"; - - if( !isset($_POST['logSysForgotPass']) && !isset($_GET['resetPassToken']) && !isset($_POST['logSysForgotPassChange']) ){ - echo $this->getOutput("resetPasswordRequestForm", array( - "identity_type" => $identName - )); - - /** - * The user had moved to the reset password form ie she/he is currently seeing the forgot password form - */ - $curStatus = "resetPasswordRequestForm"; - }elseif( isset($_GET['resetPassToken']) && !isset($_POST['logSysForgotPassChange']) ){ - /** - * The user gave the password reset token. Check if the token is valid. - */ - $reset_pass_token = urldecode($_GET['resetPassToken']); - $sql = $this->dbh->prepare("SELECT COUNT(1) FROM ". $this->config['db']['token_table'] ." WHERE token = ?"); - $sql->execute(array($reset_pass_token)); - - if($sql->fetchColumn() == 0 || $reset_pass_token == ""){ - $curStatus = "invalidToken"; // The token user gave was not valid - echo $this->getOutput($curStatus); - }else{ - /** - * The token is valid, display the new password form - */ - echo $this->getOutput("resetPasswordForm", array( - "resetPassToken" => $reset_pass_token - )); - - /** - * The token was correct, displayed the change/new password form - */ - $curStatus = "resetPasswordForm"; - } - }elseif(isset($_POST['logSysForgotPassChange']) && isset($_POST['logSysForgotPassNewPassword']) && isset($_POST['logSysForgotPassRetypedPassword'])){ - $reset_pass_token = urldecode($_POST['token']); - $sql = $this->dbh->prepare("SELECT uid FROM ". $this->config['db']['token_table'] ." WHERE token = ?"); - $sql->execute(array($reset_pass_token)); - - $userID = $sql->fetchColumn(); - - if( $userID == null || $reset_pass_token == null ){ - $curStatus = "invalidToken"; // The token user gave was not valid - echo $this->getOutput($curStatus); - }else{ - if($_POST['logSysForgotPassNewPassword'] == "" || $_POST['logSysForgotPassRetypedPassword'] == ""){ - $curStatus = "fieldsLeftBlank"; - echo $this->getOutput($curStatus); - }elseif( $_POST['logSysForgotPassNewPassword'] != $_POST['logSysForgotPassRetypedPassword'] ){ - $curStatus = "passwordDontMatch"; // The new password and retype password submitted didn't match - echo $this->getOutput($curStatus); - }else{ - $this->changePassword($_POST['logSysForgotPassNewPassword'], $userID); - - /** - * The token shall not be used again, so remove it. - */ - $sql = $this->dbh->prepare("DELETE FROM ". $this->config['db']['token_table'] ." WHERE token = ?"); - $sql->execute(array($reset_pass_token)); - - $curStatus = "passwordChanged"; // The password was successfully changed - echo $this->getOutput($curStatus); - } - } - }elseif(isset($_POST['identification'])){ - /** - * Check if username/email is provided and if it's valid and exists - */ - $identification = $_POST['identification']; - if($identification == ""){ - $curStatus = "identityNotProvided"; // The identity was not given - echo $this->getOutput($curStatus, array( - "identity_type" => $identName - )); - }else{ - $sql = $this->dbh->prepare("SELECT ". $this->config["db"]["columns"]["email"] .", ". $this->config["db"]["columns"]["id"] ." FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login OR ". $this->config["db"]["columns"]["email"] ."=:login"); - $sql->bindValue(":login", $identification); - - $sql->execute(); - $cols = $sql->fetch(\PDO::FETCH_ASSOC); - - if(empty($cols)){ - $curStatus = "userNotFound"; // The user with the identity given was not found in the users database - echo $this->getOutput($curStatus); - }else{ - $email = $cols['email']; - $uid = $cols['id']; - - /** - * Make token and insert into the table - */ - $token = self::rand_string(40); - $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['token_table'] ." (token, uid, requested) VALUES (?, ?, UNIX_TIMESTAMP())"); - $sql->execute(array($token, $uid)); - $encodedToken = urlencode($token); - - /** - * Prepare the email to be sent - */ - $subject = "Reset Password"; - $body = "You requested for resetting your password on ". $this->config['basic']['company'] .". For this, please click the following link : -
- Reset Password : {$token} -
"; - $this->sendMail($email, $subject, $body); - - echo "

An email has been sent to your email inbox with instructions. Check Your Mail Inbox and SPAM Folders.

You can close this window.

"; - $curStatus = "emailSent"; // E-Mail has been sent - } - } - } - return $curStatus; - } - - /** - * A function that handles the logged in user to change her/his password - * - * @param string $newPassword The new password - * @return boolean Whether operation was succesful - */ - public function changePassword($newPassword, $userID = null){ - if($userID === null){ - $userID = $this->userID; - } - - $hashedPassword = password_hash($newPassword . $this->config['keys']['salt'], PASSWORD_DEFAULT); - $this->updateUser(array( - "password" => $hashedPassword - ), $userID); - - return true; - } - - /** - * Check if user exists - * @param string $identification Username or email - * @return boolean Whether user exist - */ - public function userExists($identification){ - if($this->config['features']['email_login'] === true){ - $query = "SELECT COUNT(1) FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login OR ". $this->config["db"]["columns"]["email"] ."=:login"; - }else{ - $query = "SELECT COUNT(1) FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login"; - } - $sql = $this->dbh->prepare($query); - $sql->execute(array( - ":login" => $identification - )); - return $sql->fetchColumn() == "0" ? false : true; - } - - /** - * Get user's info - * @param string $what Column name - * @param string $user User ID - * @return array|string Value - */ - public function getUser($what = "*", $user = null){ - if($user === null){ - $user = $this->userID; - } - - if( is_array($what) ){ - $columns = implode(",", $what); - $columns = "{$columns}"; - }else{ - $columns = $what != "*" ? "$what" : "*"; - } - - $sql = $this->dbh->prepare("SELECT {$columns} FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["id"] ." = ? ORDER BY ". $this->config["db"]["columns"]["id"] ." LIMIT 1"); - $sql->execute(array($user)); - - $data = $sql->fetch(\PDO::FETCH_ASSOC); - if( !is_array($what) ){ - $data = $what == "*" ? $data : $data[$what]; - } - return $data; - } - - /** - * Updates the info of user - * @param array $toUpdate Fields to update - * @param [type] $user User ID - * @return boolean Whether it was a success - */ - public function updateUser($toUpdate = array(), $user = null){ - if( is_array($toUpdate) && !isset($toUpdate['id']) ){ - - if($user === null){ - $user = $this->userID; - } - - $columns = ""; - foreach($toUpdate as $k => $v){ - $columns .= "$k = :$k, "; - } - $columns = substr($columns, 0, -2); // Remove last "," - - $sql = $this->dbh->prepare("UPDATE ". $this->config['db']['table'] ." SET {$columns} WHERE ". $this->config["db"]["columns"]["id"] ."=:id"); - $sql->bindValue(":id", $user); - foreach($toUpdate as $key => $value){ - $value = htmlspecialchars($value); - $sql->bindValue(":$key", $value); - } - $sql->execute(); - return true; - }else{ - return false; - } - } - - /** - * Get the time since user joined - * @param string $user User ID - * @return string Time since - */ - public function joinedSince($user = null){ - if($user === null){ - $user = $this->userID; - } - - $created = $this->getUser("created"); - $timeFirst = strtotime($created); - $timeSecond = strtotime("now"); - $memsince = $timeSecond - strtotime($created); - $regged = date("n/j/Y", strtotime($created)); - - if($memsince < 60) { - $memfor = $memsince . " Seconds"; - }else if($memsince < 120){ - $memfor = floor ($memsince / 60) . " Minute"; - }else if($memsince < 3600 && $memsince > 120){ - $memfor = floor($memsince / 60) . " Minutes"; - }else if($memsince < 7200 && $memsince > 3600){ - $memfor = floor($memsince / 3600) . " Hour"; - }else if($memsince < 86400 && $memsince > 3600){ - $memfor = floor($memsince / 3600) . " Hours"; - }else if($memsince < 172800){ - $memfor = floor($memsince / 86400) . " Day"; - }else if($memsince < 604800 && $memsince > 172800){ - $memfor = floor($memsince / 86400) . " Days"; - }else if($memsince < 1209600 && $memsince > 604800){ - $memfor = floor($memsince / 604800) . " Week"; - }else if($memsince < 2419200 && $memsince > 1209600){ - $memfor = floor($memsince / 604800) . " Weeks"; - }else if($memsince < 4838400){ - $memfor = floor($memsince / 2419200) . " Month"; - }else if($memsince < 31536000 && $memsince > 4838400){ - $memfor = floor($memsince / 2419200) . " Months"; - }else if($memsince < 63072000){ - $memfor = floor($memsince / 31536000) . " Year"; - }else if($memsince > 63072000){ - $memfor = floor($memsince / 31536000) . " Years"; - } - return (string) $memfor; - } - - /** - * 2 Step Verification Login Process - * --------------------------------- - * When user logs in, it checks whether there is a device cookie. - * If there is : - * * Checks whether device is registered - * * If registered and username & password is correct, user is logged in - * If there is not : - * * Token is sent - * * The "Enter Received token" form is shown - * * If the token entered is correct, then a device ID is inserted to table - * * The cookie with device ID is created - * @see LS::login() Parameters are similar - * - * @param string $identification Similar to LS::login() - * @param string $password Similar to LS::login() - * @param boolean $remember_me Similar to LS::login() - * @return boolean|string Whether login was successful - * Current state of 2 Step Login - */ - public function twoStepLogin($identification = "", $password = "", $remember_me = false){ - if(isset($_POST['logSys_two_step_login-token']) && isset($_POST['logSys_two_step_login-uid']) && $_SESSION['logSys_two_step_login-first_step'] === '1'){ - /** - * The user's ID and token is got through the form - * User = One who is about to log in and is stuck at 2 step verification - */ - $uid = $_POST['logSys_two_step_login-uid']; - $token = $_POST['logSys_two_step_login-token']; - - $sql = $this->dbh->prepare("SELECT COUNT(1) FROM ". $this->config['db']['token_table'] ." WHERE token = ? AND uid = ?"); - $sql->execute(array($token, $uid)); - - if($sql->fetchColumn() == 0){ - /** - * To prevent user from Brute Forcing the token, we set the - * status of the first login step to false, - * so that the user would have to login again - */ - $_SESSION['logSys_two_step_login-first_step'] = '0'; - echo $this->getOutput("invalidToken"); - return "invalidToken"; - }else{ - /** - * Register User's new device if and only if - * the user wants to remember the device from - * which the user is logging in - */ - if(isset($_POST['logSys_two_step_login-dontask'])){ - $device_token = self::rand_string(10); - $sql = $this->dbh->prepare("INSERT INTO ". $this->config['two_step_login']['devices_table'] ." (uid, token, last_access) VALUES (?, ?, NOW())"); - $sql->execute(array($uid, $device_token)); - setcookie($this->config["cookies"]["names"]["device"], $device_token, strtotime($this->config['two_step_login']['expire']), $this->config['cookies']['path'], $this->config['cookies']['domain']); - }else{ - /** - * Verify login for this session - */ - $_SESSION[ $this->config['cookies']['names']['device_verified'] ] = '1'; - } - - /** - * Revoke token from reusing - */ - $sql = $this->dbh->prepare("DELETE FROM ". $this->config['db']['token_table'] ." WHERE token = ? AND uid = ?"); - $sql->execute(array($token, $uid)); - $this->login($this->getUser("username", $uid), false, isset($_POST['logSys_two_step_login-remember_me'])); - } - return true; - }else if($identification !== "" && $password !== ""){ - $login = $this->login($identification, $password, $remember_me, false); - if($login === false){ - /** - * Username/Password wrong - */ - return false; - }else if(is_array($login) && $login['status'] == "blocked"){ - return $login; - }else{ - /** - * Get the user ID from \Fr\LS::login() - */ - $uid = $login; - - /** - * Check if device is verfied so that 2 Step Verification can be skipped - */ - if(isset($_COOKIE[$this->config["cookies"]["names"]["device"]])){ - $sql = $this->dbh->prepare("SELECT 1 FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ? AND token = ?"); - $sql->execute(array($uid, $_COOKIE[$this->config["cookies"]["names"]["device"]])); - if($sql->fetchColumn() == "1"){ - $verfied = true; - /** - * Update last accessed time - */ - $sql = $this->dbh->prepare("UPDATE ". $this->config['two_step_login']['devices_table'] ." SET last_access = NOW() WHERE uid = ? AND token = ?"); - $sql->execute(array($uid, $_COOKIE[$this->config["cookies"]["names"]["device"]])); - - $this->login($this->getUser("username", $uid), false, $remember_me); - return true; - } - } - /** - * Start the 2 Step Verification Process - * Do only if callback is present and if - * the device is not verified - */ - if(is_callable($this->config['two_step_login']['send_callback']) && !isset($verified)){ - /** - * The first part of 2 Step Login is completed - */ - $_SESSION['logSys_two_step_login-first_step'] = '1'; - - /** - * The 2nd parameter depends on `config` -> `two_step_login` -> `numeric` - */ - $token = self::rand_string($this->config['two_step_login']['token_length'], $this->config['two_step_login']['numeric']); - - /** - * Save the token in DB - */ - $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['token_table'] ." (token, uid, requested) VALUES (?, ?, NOW())"); - $sql->execute(array($token, $uid)); - - $that = $this; - call_user_func_array($this->config['two_step_login']['send_callback'], array(&$that, $uid, $token)); - - /** - * Display the form - */ - echo $this->getOutput("twoStepLoginVerifyForm", array( - "remember_me" => $remember_me, - "uid" => $uid - )); - return "formDisplay"; - }else{ - self::log("two_step_login: Token Callback not present"); - } - } - } - /** - * 2 Step Login is not doing any actions or - * hasn't returned anything before. If so, - * then return false to indicate that the - * function is not doing anything - */ - return false; - } - - /** - * Get authorized devices - * @return array Authorized devices - */ - public function getDevices(){ - if($this->loggedIn){ - $sql = $this->dbh->prepare("SELECT * FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ?"); - $sql->execute(array($this->userID)); - return $sql->fetchAll(\PDO::FETCH_ASSOC); - }else{ - return false; - } - } - - /** - * Revoke a device - * @param string $device_token Device ID - * @return boolean Whether it was revoked - */ - public function revokeDevice($device_token){ - if($this->loggedIn){ - $sql = $this->dbh->prepare("DELETE FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ? AND token = ?"); - $sql->execute(array($this->userID, $device_token)); - if(isset($_SESSION[ $this->config['cookies']['names']['device_verified'] ])){ - unset($_SESSION[ $this->config['cookies']['names']['device_verified'] ]); - } - return $sql->rowCount() == 1; - } - } - - /** - * Get output for each states - * @param string $state The output of the state - * @param array $extraInfo Extra parameters about the state - * @return string HTML output - */ - public function getOutput($state, $extraInfo = array()){ - if(is_callable($this->config["basic"]["output_callback"])){ - $that = $this; - return call_user_func_array($this->config["basic"]["output_callback"], array( - &$that, - $state, - $extraInfo - )); - }else{ - return null; - } - } - - /** - * Whether a user is logged in - * @return boolean - */ - public function isLoggedIn(){ - return $this->loggedIn; - } - - /** - * --------------------- - * Extra Tools/Functions - * --------------------- - */ - - /** - * Check if E-Mail is valid - * @param string $email Email - * @return boolean - */ - public static function validEmail($email = ""){ - return filter_var($email, FILTER_VALIDATE_EMAIL); - } - - /** - * Get the current page URL - * @return string URL - */ - public static function curPageURL() { - $pageURL = 'http'; - if(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on"){$pageURL .= "s";} - $pageURL .= "://"; - if($_SERVER["SERVER_PORT"] != "80") { - $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"]; - }else{ - $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]; - } - return $pageURL; - } - - /** - * Generate a Random String - * @param int $length Length of string - * @param boolean $int Should string be integer - * @return string Random string - */ - public static function rand_string($length, $int = false) { - $random_str = ""; - $chars = $int ? "0516243741506927589" : "subinsblogabcdefghijklmanopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - $size = strlen($chars) - 1; - for($i = 0;$i < $length;$i++) { - $random_str .= $chars[rand(0, $size)]; - } - return $random_str; - } - - /** - * Get the current page path. - * @return string Example: /mypage, /folder/mypage.php - */ - public static function curPage(){ - $parts = parse_url(self::curPageURL()); - return $parts["path"]; - } - - /** - * Do a redirect - * @param [type] $url Where to redirect to - * @param integer $status Redirect status code - * @return void - */ - public static function redirect($url, $status = 302){ - header("Location: $url", true, $status); - exit; - } - - /** - * Send an email - * @param string $email Recipient's email - * @param string $subject Subject - * @param string $body Message - * @return void - */ - public function sendMail($email, $subject, $body){ - /** - * If there is a callback for email sending, use it else PHP's mail() - */ - if(is_callable($this->config['basic']['email_callback'])){ - $that = $this; - call_user_func_array($this->config['basic']['email_callback'], array(&$that, $email, $subject, $body)); - }else{ - $headers = array(); - $headers[] = "MIME-Version: 1.0"; - $headers[] = "Content-type: text/html; charset=iso-8859-1"; - $headers[] = "From: ". $this->config['basic']['email']; - $headers[] = "Reply-To: ". $this->config['basic']['email']; - mail($email, $subject, $body, implode("\r\n", $headers)); - } - } - - /** - * CSRF Protection - */ - public function csrf($type = ""){ - if(!isset($_COOKIE['csrf_token'])){ - $csrf_token = self::rand_string(5); - setcookie("csrf_token", $csrf_token, 0, $this->config['cookies']['path'], $this->config['cookies']['domain']); - }else{ - $csrf_token = $_COOKIE['csrf_token']; - } - if($type == "s"){ - /** - * Output as string - */ - return urlencode($csrf_token); - }elseif($type == "g"){ - /** - * Output as a GET parameter - */ - return "&csrf_token=" . urlencode($csrf_token); - }elseif($type == "i"){ - /** - * Output as an input field - */ - echo ""; - }else{ - /** - * Check CSRF validity - */ - if((isset($_POST['csrf_token']) && $_COOKIE['csrf_token'] == $_POST['csrf_token']) || (isset($_GET['csrf_token']) && $_COOKIE['csrf_token'] == $_GET['csrf_token'])){ - return true; - }else{ - /** - * CSRF Token doesn't match. - */ - return false; - } - } - } - - /** - * ------------------------- - * End Extra Tools/Functions - * ------------------------- - */ -} diff --git a/src/_notes/dwsync.xml b/src/_notes/dwsync.xml deleted file mode 100644 index a94c403..0000000 --- a/src/_notes/dwsync.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/app/api/auth/[...all]/route.ts b/src/app/api/auth/[...all]/route.ts new file mode 100644 index 0000000..f65e032 --- /dev/null +++ b/src/app/api/auth/[...all]/route.ts @@ -0,0 +1,5 @@ +import { toNextJsHandler } from "better-auth/next-js"; + +import { auth } from "@/auth/auth"; + +export const { GET, POST } = toNextJsHandler(auth); diff --git a/src/app/api/billing/webhook/route.ts b/src/app/api/billing/webhook/route.ts new file mode 100644 index 0000000..03fe000 --- /dev/null +++ b/src/app/api/billing/webhook/route.ts @@ -0,0 +1,32 @@ +import { NextResponse } from "next/server"; + +import { applyStripeEvent, stripeClient } from "@/server/billing"; + +/** Stripe's webhook. The signature is checked against STRIPE_WEBHOOK_SECRET + before anything is read; each event is applied once. */ +export async function POST(request: Request): Promise { + const stripe = stripeClient(); + const secret = process.env.STRIPE_WEBHOOK_SECRET?.trim(); + if (!stripe || !secret) return NextResponse.json({ error: "Billing is not configured" }, { status: 503 }); + + const signature = request.headers.get("stripe-signature"); + if (!signature) return NextResponse.json({ error: "Missing signature" }, { status: 400 }); + const payload = await request.text(); + + let event; + try { + event = stripe.webhooks.constructEvent(payload, signature, secret); + } catch (caught) { + console.warn("[billing] rejected webhook", caught instanceof Error ? caught.message : caught); + return NextResponse.json({ error: "Invalid signature" }, { status: 400 }); + } + + try { + const outcome = await applyStripeEvent(event); + console.info("[billing]", event.type, outcome); + return NextResponse.json({ received: true, outcome }); + } catch (caught) { + console.error("[billing] failed to apply", event.type, caught instanceof Error ? caught.message : caught); + return NextResponse.json({ error: "Failed to apply event" }, { status: 500 }); + } +} diff --git a/src/app/api/blob/route.ts b/src/app/api/blob/route.ts new file mode 100644 index 0000000..e66899e --- /dev/null +++ b/src/app/api/blob/route.ts @@ -0,0 +1,46 @@ +import { handleUpload, type HandleUploadBody } from "@vercel/blob/client"; +import { NextResponse } from "next/server"; + +import { getViewer } from "@/server/session"; +import { ALLOWED_UPLOAD_TYPES, MAX_UPLOAD_BYTES, sanitizeFilename } from "@/server/storage"; + +/** Vercel Blob driver: issues a scoped client token so the browser uploads + straight to the store. Only a signed-in member gets one, and every file is + filed under the workspace. */ +export async function POST(request: Request): Promise { + const incoming = (await request.json()) as HandleUploadBody; + const token = process.env.BLOB_READ_WRITE_TOKEN; + if (!token) return NextResponse.json({ error: "Blob storage is not configured" }, { status: 500 }); + + if (incoming.type === "blob.generate-client-token") { + const viewer = await getViewer(); + if (!viewer) return NextResponse.json({ error: "Sign in to upload" }, { status: 401 }); + const pathname = `${viewer.workspace.id}/${sanitizeFilename(incoming.payload.pathname)}`; + const body: HandleUploadBody = { ...incoming, payload: { ...incoming.payload, pathname } }; + try { + const json = await handleUpload({ + body, + request, + token, + onBeforeGenerateToken: async () => ({ + allowedContentTypes: [...ALLOWED_UPLOAD_TYPES], + maximumSizeInBytes: MAX_UPLOAD_BYTES, + addRandomSuffix: true, + }), + }); + return NextResponse.json({ ...json, pathname }); + } catch (error) { + console.error("[blob] token failed", error instanceof Error ? error.message : error); + return NextResponse.json({ error: "Could not start the upload" }, { status: 500 }); + } + } + + /* Upload-completed callbacks arrive from Vercel, not the browser; the shelf + is written by the client action instead, so nothing is needed here. */ + try { + const json = await handleUpload({ body: incoming, request, token, onBeforeGenerateToken: async () => ({}) }); + return NextResponse.json(json); + } catch { + return new NextResponse(null, { status: 400 }); + } +} diff --git a/src/app/api/files/[...path]/route.ts b/src/app/api/files/[...path]/route.ts new file mode 100644 index 0000000..1629d27 --- /dev/null +++ b/src/app/api/files/[...path]/route.ts @@ -0,0 +1,29 @@ +import { createReadStream } from "node:fs"; +import { stat } from "node:fs/promises"; +import { Readable } from "node:stream"; + +import { NextResponse } from "next/server"; + +import { contentTypeFor, localPath } from "@/server/storage"; + +/** Serves locally stored uploads. Unauthenticated on purpose: the generation + platform fetches these URLs with no session, and the path carries a random + id in place of a guessable name. */ +export async function GET(_request: Request, context: { params: Promise<{ path: string[] }> }) { + const { path } = await context.params; + const file = localPath(path); + if (!file) return new NextResponse(null, { status: 404 }); + const info = await stat(file).catch(() => null); + if (!info?.isFile()) return new NextResponse(null, { status: 404 }); + const stream = Readable.toWeb(createReadStream(file)) as ReadableStream; + return new NextResponse(stream, { + headers: { + "Content-Type": contentTypeFor(file), + "Content-Length": String(info.size), + /* Whatever bytes were uploaded, the browser treats them as the declared + media type and never as a document. */ + "X-Content-Type-Options": "nosniff", + "Cache-Control": "public, max-age=31536000, immutable", + }, + }); +} diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts new file mode 100644 index 0000000..c5956e0 --- /dev/null +++ b/src/app/api/upload/route.ts @@ -0,0 +1,23 @@ +import { NextResponse } from "next/server"; + +import { getViewer } from "@/server/session"; +import { ALLOWED_UPLOAD_TYPES, MAX_UPLOAD_BYTES, publicOrigin, storeLocal } from "@/server/storage"; + +/** Local storage driver: the browser posts the file here and gets back a URL + under /api/files the platform can fetch. */ +export async function POST(request: Request): Promise { + const viewer = await getViewer(); + if (!viewer) return NextResponse.json({ error: "Sign in to upload" }, { status: 401 }); + + const form = await request.formData().catch(() => null); + const file = form?.get("file"); + if (!(file instanceof File)) return NextResponse.json({ error: "No file" }, { status: 400 }); + if (!(ALLOWED_UPLOAD_TYPES as readonly string[]).includes(file.type)) { + return NextResponse.json({ error: `Unsupported file type ${file.type || "(unknown)"}` }, { status: 415 }); + } + if (file.size > MAX_UPLOAD_BYTES) { + return NextResponse.json({ error: "File is larger than 64 MB" }, { status: 413 }); + } + const path = await storeLocal(viewer.workspace.id, file.name, new Uint8Array(await file.arrayBuffer())); + return NextResponse.json({ url: `${publicOrigin(request)}/api/files/${path}` }); +} diff --git a/src/app/app/brand/page.tsx b/src/app/app/brand/page.tsx new file mode 100644 index 0000000..8ec2dd1 --- /dev/null +++ b/src/app/app/brand/page.tsx @@ -0,0 +1,24 @@ +import type { Metadata } from "next"; + +import { getBrandKit } from "@/server/actions/brand"; +import { viewerOrRedirect } from "@/server/session"; +import { BrandEditor } from "@/ui/brand-editor"; + +export const metadata: Metadata = { title: "Brand kit" }; +export const dynamic = "force-dynamic"; + +export default async function BrandPage() { + await viewerOrRedirect(); + const brand = await getBrandKit(); + return ( +
+
+
+

Brand kit

+

Written once, folded into every prompt the studio sends.

+
+
+ +
+ ); +} diff --git a/src/app/app/campaigns/[id]/page.tsx b/src/app/app/campaigns/[id]/page.tsx new file mode 100644 index 0000000..67f99d8 --- /dev/null +++ b/src/app/app/campaigns/[id]/page.tsx @@ -0,0 +1,37 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; + +import { platformKeyStatus } from "@/generation/actions"; +import { readBrandKit } from "@/server/actions/brand"; +import { getCampaign } from "@/server/actions/campaigns"; +import { getProduct } from "@/server/actions/products"; +import { listCampaignRuns } from "@/server/runs"; +import { viewerOrRedirect } from "@/server/session"; +import { CampaignView } from "@/ui/campaign-view"; + +export const metadata: Metadata = { title: "Campaign" }; +export const dynamic = "force-dynamic"; + +export default async function CampaignPage({ params }: { params: Promise<{ id: string }> }) { + const viewer = await viewerOrRedirect(); + const { id } = await params; + const campaign = await getCampaign(id); + if (!campaign) notFound(); + const [product, brand, runs, key] = await Promise.all([ + getProduct(campaign.productId), + readBrandKit(viewer.workspace.id), + listCampaignRuns(viewer.workspace.id, campaign.id), + platformKeyStatus(), + ]); + if (!product) notFound(); + return ( + + ); +} diff --git a/src/app/app/campaigns/new/page.tsx b/src/app/app/campaigns/new/page.tsx new file mode 100644 index 0000000..a7701c3 --- /dev/null +++ b/src/app/app/campaigns/new/page.tsx @@ -0,0 +1,37 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { listProducts } from "@/server/actions/products"; +import { viewerOrRedirect } from "@/server/session"; +import { CampaignForm } from "@/ui/campaign-form"; + +export const metadata: Metadata = { title: "New campaign" }; +export const dynamic = "force-dynamic"; + +export default async function NewCampaignPage({ searchParams }: { searchParams: Promise<{ product?: string }> }) { + await viewerOrRedirect(); + const [products, params] = await Promise.all([listProducts(), searchParams]); + return ( +
+
+
+

+ Campaigns +

+

New campaign

+
+
+ {products.length === 0 ? ( +
+

Add a product first

+

A campaign is built around one product and its photos.

+ + Add a product + +
+ ) : ( + + )} +
+ ); +} diff --git a/src/app/app/campaigns/page.tsx b/src/app/app/campaigns/page.tsx new file mode 100644 index 0000000..df14474 --- /dev/null +++ b/src/app/app/campaigns/page.tsx @@ -0,0 +1,63 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { getChannel } from "@/domain/channels"; +import { findPreset } from "@/domain/presets"; +import { listCampaigns } from "@/server/actions/campaigns"; +import { listProducts } from "@/server/actions/products"; +import { viewerOrRedirect } from "@/server/session"; + +export const metadata: Metadata = { title: "Campaigns" }; +export const dynamic = "force-dynamic"; + +export default async function CampaignsPage() { + await viewerOrRedirect(); + const [campaigns, products] = await Promise.all([listCampaigns(), listProducts()]); + const productName = (id: string) => products.find((product) => product.id === id)?.name ?? "Archived product"; + return ( +
+
+
+

Campaigns

+

One product, a set of presets, the channels it ships to — generated together, exported as one folder.

+
+ + New campaign + +
+ + {campaigns.length === 0 ? ( +
+

No campaigns yet

+

{products.length === 0 ? "Add a product first, then build a campaign around it." : "Pick a product, choose presets and channels, and generate the whole set in one press."}

+ + {products.length === 0 ? "Add a product" : "Create a campaign"} + +
+ ) : ( +
    + {campaigns.map((campaign) => ( +
  • + + {campaign.name} + {productName(campaign.productId)} + + {campaign.presetIds.map((id) => ( + + {findPreset(id)?.label ?? id} + + ))} + {campaign.channelIds.map((id) => ( + + {getChannel(id).label} + + ))} + + +
  • + ))} +
+ )} +
+ ); +} diff --git a/src/app/app/layout.tsx b/src/app/app/layout.tsx new file mode 100644 index 0000000..103cc12 --- /dev/null +++ b/src/app/app/layout.tsx @@ -0,0 +1,35 @@ +import { Inter } from "next/font/google"; +import { redirect } from "next/navigation"; +import type { ReactNode } from "react"; + +import { getViewer } from "@/server/session"; +import { workspacesForUser } from "@/server/workspaces"; +import { Sidebar } from "@/ui/sidebar"; + +import "@/studio/studio.css"; +import "@/ui/app.css"; + +const inter = Inter({ subsets: ["latin"], variable: "--font-vt-inter", display: "swap" }); + +/* Everything under /app is behind the session. The check lives here rather + than in a proxy so every page gets the viewer for free and a missing + session lands on the sign-in page with the way back. */ +export default async function AppLayout({ children }: { children: ReactNode }) { + const viewer = await getViewer(); + if (!viewer) redirect("/login"); + const memberships = await workspacesForUser(viewer.user.id); + return ( +
+ ({ id: entry.workspace.id, name: entry.workspace.name, role: entry.role }))} + planId={viewer.workspace.planId} + credits={viewer.workspace.creditBalance} + byok={Boolean(viewer.workspace.platformKeySealed)} + userName={viewer.user.name || viewer.user.email} + /> +
{children}
+
+ ); +} diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx new file mode 100644 index 0000000..38828cf --- /dev/null +++ b/src/app/app/page.tsx @@ -0,0 +1,14 @@ +import type { Metadata } from "next"; + +import { viewerOrRedirect } from "@/server/session"; +import { loadStudioData } from "@/server/studio-data"; +import { StudioApp } from "@/studio/studio-app"; + +export const metadata: Metadata = { title: "Studio" }; +export const dynamic = "force-dynamic"; + +export default async function StudioPage() { + const viewer = await viewerOrRedirect(); + const data = await loadStudioData(viewer); + return ; +} diff --git a/src/app/app/products/[id]/page.tsx b/src/app/app/products/[id]/page.tsx new file mode 100644 index 0000000..4a1c2db --- /dev/null +++ b/src/app/app/products/[id]/page.tsx @@ -0,0 +1,35 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { notFound } from "next/navigation"; + +import { getProduct } from "@/server/actions/products"; +import { viewerOrRedirect } from "@/server/session"; +import { ProductEditor } from "@/ui/product-editor"; + +export const metadata: Metadata = { title: "Product" }; +export const dynamic = "force-dynamic"; + +export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) { + await viewerOrRedirect(); + const { id } = await params; + const product = id === "new" ? null : await getProduct(id); + if (id !== "new" && !product) notFound(); + return ( +
+
+
+

+ Products +

+

{product ? product.name : "New product"}

+
+ {product && ( + + Open in studio + + )} +
+ +
+ ); +} diff --git a/src/app/app/products/page.tsx b/src/app/app/products/page.tsx new file mode 100644 index 0000000..7552ac2 --- /dev/null +++ b/src/app/app/products/page.tsx @@ -0,0 +1,60 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { listProducts } from "@/server/actions/products"; +import { viewerOrRedirect } from "@/server/session"; + +export const metadata: Metadata = { title: "Products" }; +export const dynamic = "force-dynamic"; + +export default async function ProductsPage() { + await viewerOrRedirect(); + const products = await listProducts(); + return ( +
+
+
+

Products

+

Every product carries its photos and the words the prompts are built from.

+
+ + New product + +
+ + {products.length === 0 ? ( +
+

No products yet

+

Add your first product — a name, how it looks, and a photo — and the studio does the rest.

+ + Add a product + +
+ ) : ( +
    + {products.map((product) => ( +
  • + + + {product.assets[0] ? ( + /* eslint-disable-next-line @next/next/no-img-element */ + + ) : ( + No photo + )} + + + {product.name} + + {product.category !== "Other" ? `${product.category} · ` : ""} + {product.assets.length} photo{product.assets.length === 1 ? "" : "s"} + + + +
  • + ))} +
+ )} +
+ ); +} diff --git a/src/app/app/settings/page.tsx b/src/app/app/settings/page.tsx new file mode 100644 index 0000000..ea97b0f --- /dev/null +++ b/src/app/app/settings/page.tsx @@ -0,0 +1,43 @@ +import type { Metadata } from "next"; + +import { platformKeyStatus } from "@/generation/actions"; +import { getWorkspaceSummary, listLedger } from "@/server/actions/billing"; +import { getTeam, listMyWorkspaces } from "@/server/actions/team"; +import { viewerOrRedirect } from "@/server/session"; +import { KeyPanel, LedgerPanel, PlanPanel } from "@/ui/settings"; +import { TeamPanel, WorkspacePanel } from "@/ui/team"; + +export const metadata: Metadata = { title: "Settings" }; +export const dynamic = "force-dynamic"; + +export default async function SettingsPage({ searchParams }: { searchParams: Promise<{ checkout?: string }> }) { + const viewer = await viewerOrRedirect(); + const [summary, key, ledger, team, workspaces, params] = await Promise.all([ + getWorkspaceSummary(), + platformKeyStatus(), + listLedger(), + getTeam(), + listMyWorkspaces(), + searchParams, + ]); + const origin = (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/+$/, ""); + return ( +
+
+
+

Settings

+

+ {viewer.workspace.name} · {viewer.user.email} +

+
+
+
+ + + + + +
+
+ ); +} diff --git a/src/app/apple-icon.png b/src/app/apple-icon.png new file mode 100644 index 0000000..b5c101c Binary files /dev/null and b/src/app/apple-icon.png differ diff --git a/src/app/base.css b/src/app/base.css new file mode 100644 index 0000000..1da2ec5 --- /dev/null +++ b/src/app/base.css @@ -0,0 +1,16 @@ +/* Document base. The studio at / ships its own scoped system in + src/vitrina/studio.css; this file only owns what lives outside + that scope — the document canvas, which overscroll rubber-banding exposes. + Literal because --bg lives on .vt; it matches STUDIO_BG in src/site.ts. */ + +html { + -webkit-text-size-adjust: 100%; +} + +html, +body { + margin: 0; + padding: 0; + background: #0a0a0b; + color-scheme: dark; +} diff --git a/src/app/forgot-password/page.tsx b/src/app/forgot-password/page.tsx new file mode 100644 index 0000000..4e7251d --- /dev/null +++ b/src/app/forgot-password/page.tsx @@ -0,0 +1,30 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { redirect } from "next/navigation"; + +import { mailConfigured } from "@/server/mail"; +import { getViewer } from "@/server/session"; +import { AuthShell } from "@/ui/auth-shell"; +import { ForgotPasswordForm } from "@/ui/password-forms"; + +export const metadata: Metadata = { title: "Reset password" }; +export const dynamic = "force-dynamic"; + +export default async function ForgotPasswordPage() { + if (await getViewer()) redirect("/app"); + return ( + + {mailConfigured() ? ( + + ) : ( +
+

Reset not available

+

This studio has no email sender configured, so passwords cannot be reset by link. Ask the operator.

+ + Back to sign in + +
+ )} +
+ ); +} diff --git a/src/app/icon.svg b/src/app/icon.svg new file mode 100644 index 0000000..90d448a --- /dev/null +++ b/src/app/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/app/join/[token]/route.ts b/src/app/join/[token]/route.ts new file mode 100644 index 0000000..1a019eb --- /dev/null +++ b/src/app/join/[token]/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from "next/server"; + +import { getViewer, WORKSPACE_COOKIE, WORKSPACE_COOKIE_OPTIONS } from "@/server/session"; +import { publicOrigin } from "@/server/storage"; +import { acceptInvite, InviteError } from "@/server/workspaces"; + +/** The landing for an invitation link. Signed out, it sends the visitor to + create an account and comes back here; signed in, it joins them, makes + the workspace current and opens the studio. A route handler rather than a + page because spending the token and setting the cookie are writes, and a + page render can be retried. */ +export async function GET(request: Request, context: { params: Promise<{ token: string }> }): Promise { + const { token } = await context.params; + /* APP_URL when set, so a forged Host header cannot turn this into a redirect elsewhere. */ + const origin = publicOrigin(request); + const viewer = await getViewer(); + if (!viewer) { + return NextResponse.redirect(`${origin}/register?next=${encodeURIComponent(`/join/${token}`)}`); + } + try { + const workspaceId = await acceptInvite(token, viewer.user.id); + const response = NextResponse.redirect(`${origin}/app`); + response.cookies.set(WORKSPACE_COOKIE, workspaceId, WORKSPACE_COOKIE_OPTIONS); + return response; + } catch (caught) { + const code = caught instanceof InviteError ? caught.code : "unknown"; + return NextResponse.redirect(`${origin}/join/failed?code=${code}`); + } +} diff --git a/src/app/join/failed/page.tsx b/src/app/join/failed/page.tsx new file mode 100644 index 0000000..f6f354f --- /dev/null +++ b/src/app/join/failed/page.tsx @@ -0,0 +1,27 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { AuthShell } from "@/ui/auth-shell"; + +export const metadata: Metadata = { title: "Invitation not usable" }; + +const REASONS: Record = { + unknown: "This invitation does not exist.", + expired: "This invitation has expired or was already used.", + full: "This workspace has no seats left on its plan. Ask the owner to upgrade and send a new link.", +}; + +export default async function JoinFailedPage({ searchParams }: { searchParams: Promise<{ code?: string }> }) { + const { code } = await searchParams; + return ( + +
+

Invitation not usable

+

{REASONS[code ?? ""] ?? REASONS.unknown}

+ + Go to your studio + +
+
+ ); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..02e597a --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,56 @@ +import type { Metadata, Viewport } from "next"; +import type { ReactNode } from "react"; + +import { + SITE_DESCRIPTION, + SITE_NAME, + SITE_TITLE, + SITE_URL, + STUDIO_BG, + openGraphFor, + twitterFor, +} from "@/site"; + +import "./base.css"; + +export const metadata: Metadata = { + metadataBase: new URL(SITE_URL), + title: { default: SITE_TITLE, template: `%s — ${SITE_NAME}` }, + description: SITE_DESCRIPTION, + applicationName: SITE_NAME, + creator: SITE_NAME, + publisher: SITE_NAME, + referrer: "origin-when-cross-origin", + /* Nothing on the surface is a phone number, address, or email; leaving the + heuristic on lets iOS Safari rewrite prompt text and model ids as links. */ + formatDetection: { telephone: false, address: false, email: false }, + appleWebApp: { title: SITE_NAME }, + openGraph: openGraphFor({ path: "/" }), + twitter: twitterFor(), + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + "max-image-preview": "large", + "max-snippet": -1, + "max-video-preview": -1, + }, + }, +}; + +/* The studio is the only surface, and it ships a single look, so the browser + chrome is pinned to its ground rather than following a preference. */ +export const viewport: Viewport = { + colorScheme: "dark", + themeColor: STUDIO_BG, +}; + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 0000000..56d4ae9 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; +import { redirect } from "next/navigation"; + +import { mailConfigured } from "@/server/mail"; +import { getViewer } from "@/server/session"; +import { AuthForm } from "@/ui/auth-form"; +import { AuthShell } from "@/ui/auth-shell"; + +export const metadata: Metadata = { title: "Sign in" }; +export const dynamic = "force-dynamic"; + +/** Only same-origin paths may be a return target. */ +function safeNext(value: string | undefined): string { + return value && value.startsWith("/") && !value.startsWith("//") ? value : "/app"; +} + +export default async function LoginPage({ searchParams }: { searchParams: Promise<{ next?: string }> }) { + const next = safeNext((await searchParams).next); + if (await getViewer()) redirect(next); + return ( + + + + ); +} diff --git a/src/app/manifest.ts b/src/app/manifest.ts new file mode 100644 index 0000000..86a4272 --- /dev/null +++ b/src/app/manifest.ts @@ -0,0 +1,30 @@ +import type { MetadataRoute } from "next"; + +import { SITE_DESCRIPTION, SITE_NAME, SITE_TITLE, STUDIO_BG } from "@/site"; + +export default function manifest(): MetadataRoute.Manifest { + return { + name: SITE_TITLE, + short_name: SITE_NAME, + description: SITE_DESCRIPTION, + start_url: "/app", + scope: "/", + display: "standalone", + /* Installed, the app opens straight into the studio, so both colors are + the studio ground rather than the marketing chrome's. */ + background_color: STUDIO_BG, + theme_color: STUDIO_BG, + categories: ["productivity", "graphics", "photo"], + icons: [ + { src: "/icon.svg", sizes: "any", type: "image/svg+xml", purpose: "any" }, + { src: "/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any" }, + { src: "/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any" }, + { + src: "/icon-maskable-512.png", + sizes: "512x512", + type: "image/png", + purpose: "maskable", + }, + ], + }; +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..e560d71 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,135 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import Link from "next/link"; + +import { CHANNELS } from "@/domain/channels"; +import { PLANS, formatPrice } from "@/domain/plans"; +import { PRESETS } from "@/domain/presets"; +import { MODELS } from "@/generation/catalog"; +import { VERTICAL } from "@/domain"; + +import "@/studio/studio.css"; +import "@/ui/app.css"; + +const inter = Inter({ subsets: ["latin"], variable: "--font-vt-inter", display: "swap" }); + +export const metadata: Metadata = { alternates: { canonical: "/" } }; + +const STEPS = [ + { title: "Add a product", body: "Name, category, a few lines on how it looks, and the photos you already have." }, + { title: "Pick a preset and a channel", body: "Pack shot for Amazon, lifestyle for Instagram, a 9:16 reveal for TikTok — ratio and length resolve themselves." }, + { title: "Generate, review, export", body: "Approve the keepers, export a folder per channel, publish." }, +]; + +export default function LandingPage() { + const images = PRESETS.filter((preset) => preset.surface === "image").length; + const videos = PRESETS.length - images; + return ( +
+
+ + + {VERTICAL.name} + + +
+ +
+

{VERTICAL.descriptor}

+

Product photos and clips for every channel, from the photo you already have.

+

+ Upload a product shot. Pick a preset — pack shot, lifestyle scene, on-model, product reveal, UGC-style clip — and the + channel it is for. {VERTICAL.name} writes the prompt from your brand kit, picks the right model out of {MODELS.length}, + fixes the ratio and length the channel wants, and files every approved result by product and channel. +

+
+ + Create your studio — 30 free credits + + + How it works + +
+
    +
  • + {images} image presets +
  • +
  • + {videos} video presets +
  • +
  • + {CHANNELS.length} channels +
  • +
  • + {MODELS.length} models +
  • +
+
+ +
+

Three steps, one folder per channel

+
    + {STEPS.map((step, index) => ( +
  1. + {index + 1} +

    {step.title}

    +

    {step.body}

    +
  2. + ))} +
+
+ +
+

Presets an e-commerce team asks for by name

+
    + {PRESETS.map((preset) => ( +
  • + {preset.surface} +

    {preset.label}

    +

    {preset.description}

    +
  • + ))} +
+
+ +
+

Pricing

+

+ One credit is one standard image. Video is priced per second. Bring your own platform key on Pro and Studio and pay + the platform directly instead. +

+
    + {PLANS.map((plan) => ( +
  • +

    {plan.label}

    +

    + {plan.priceCents === 0 ? "Free" : formatPrice(plan.priceCents)} + {plan.priceCents > 0 && / month} +

    +

    {plan.blurb}

    +
      + {plan.features.map((feature) => ( +
    • {feature}
    • + ))} +
    + + {plan.priceCents === 0 ? "Start free" : `Start with ${plan.label}`} + +
  • + ))} +
+
+ +
+ {VERTICAL.name} — {VERTICAL.descriptor} + Built on an open studio for image and video generation. +
+
+ ); +} diff --git a/src/app/register/page.tsx b/src/app/register/page.tsx new file mode 100644 index 0000000..c9194a7 --- /dev/null +++ b/src/app/register/page.tsx @@ -0,0 +1,39 @@ +import type { Metadata } from "next"; +import Link from "next/link"; +import { redirect } from "next/navigation"; + +import { signupsClosed } from "@/auth/auth"; +import { getViewer } from "@/server/session"; +import { AuthForm } from "@/ui/auth-form"; +import { AuthShell } from "@/ui/auth-shell"; + +export const metadata: Metadata = { title: "Create your studio" }; +export const dynamic = "force-dynamic"; + +/** Only same-origin paths may be a return target. */ +function safeNext(value: string | undefined): string { + return value && value.startsWith("/") && !value.startsWith("//") ? value : "/app"; +} + +export default async function RegisterPage({ searchParams }: { searchParams: Promise<{ next?: string }> }) { + const next = safeNext((await searchParams).next); + if (await getViewer()) redirect(next); + if (signupsClosed()) { + return ( + +
+

Sign-ups are closed

+

This studio seats people by invitation. Ask the operator for a link, or sign in if you already have an account.

+ + Sign in + +
+
+ ); + } + return ( + + + + ); +} diff --git a/src/app/reset-password/page.tsx b/src/app/reset-password/page.tsx new file mode 100644 index 0000000..370b9dd --- /dev/null +++ b/src/app/reset-password/page.tsx @@ -0,0 +1,16 @@ +import type { Metadata } from "next"; + +import { AuthShell } from "@/ui/auth-shell"; +import { ResetPasswordForm } from "@/ui/password-forms"; + +export const metadata: Metadata = { title: "New password" }; +export const dynamic = "force-dynamic"; + +export default async function ResetPasswordPage({ searchParams }: { searchParams: Promise<{ token?: string; error?: string }> }) { + const params = await searchParams; + return ( + + + + ); +} diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..6317685 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,16 @@ +import type { MetadataRoute } from "next"; + +import { SITE_URL } from "@/site"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + /* The workspace and its API are behind a session. */ + disallow: ["/api/", "/app/"], + }, + sitemap: `${SITE_URL}/sitemap.xml`, + host: SITE_URL, + }; +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..e8aa953 --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,13 @@ +import type { MetadataRoute } from "next"; + +import { SITE_URL } from "@/site"; + +export default function sitemap(): MetadataRoute.Sitemap { + const lastModified = new Date(); + + return [ + { url: `${SITE_URL}/`, lastModified, changeFrequency: "weekly", priority: 1 }, + { url: `${SITE_URL}/register`, lastModified, changeFrequency: "monthly", priority: 0.6 }, + { url: `${SITE_URL}/login`, lastModified, changeFrequency: "monthly", priority: 0.3 }, + ]; +} diff --git a/src/auth/auth.ts b/src/auth/auth.ts new file mode 100644 index 0000000..0340f58 --- /dev/null +++ b/src/auth/auth.ts @@ -0,0 +1,81 @@ +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { nextCookies } from "better-auth/next-js"; + +import { db, schema } from "@/db/client"; +import { mailConfigured, sendMail } from "@/server/mail"; +import { bootstrapWorkspace } from "@/server/workspaces"; + +const secret = process.env.APP_SECRET?.trim(); +/* The build evaluates route modules without the runtime environment; the + guard is for a server that would otherwise sign sessions with a known + default. */ +const building = process.env.NEXT_PHASE === "phase-production-build"; +if (!secret && process.env.NODE_ENV === "production" && !building) { + throw new Error("Missing APP_SECRET — set it before starting the server"); +} + +export function signupsClosed(): boolean { + return process.env.SIGNUPS?.trim().toLowerCase() === "closed"; +} + +export const auth = betterAuth({ + appName: "Vitrina", + secret: secret ?? "vitrina-dev-secret-change-me", + baseURL: process.env.APP_URL?.trim() || undefined, + database: drizzleAdapter(db, { + provider: "sqlite", + schema: { + user: schema.user, + session: schema.session, + account: schema.account, + verification: schema.verification, + }, + }), + emailAndPassword: { + enabled: true, + minPasswordLength: 8, + /* A private install can close the door: SIGNUPS=closed refuses new + accounts at the API and the register page says so. Invitations still + create accounts through the same door, so the setting is for studios + that seat people by hand. */ + disableSignUp: signupsClosed(), + /* No mail provider is wired in by default; a self-hosted install has no + need for a verification round trip, and a SaaS operator can turn it on + once they have a sender. */ + requireEmailVerification: false, + /* Password resets go out only when a sender exists; the login page hides + the link otherwise. */ + sendResetPassword: async ({ user, url }) => { + if (!mailConfigured()) throw new Error("Password reset is not available on this studio"); + await sendMail({ + to: user.email, + subject: "Reset your Vitrina password", + text: `Someone asked to reset the password for ${user.email}. Open this link to choose a new one:\n\n${url}\n\nIf that was not you, ignore this message.`, + }); + }, + resetPasswordTokenExpiresIn: 60 * 60, + }, + session: { + expiresIn: 60 * 60 * 24 * 30, + updateAge: 60 * 60 * 24, + cookieCache: { enabled: true, maxAge: 5 * 60 }, + }, + user: { deleteUser: { enabled: true } }, + databaseHooks: { + user: { + create: { + /* A person who signs up gets a workspace to work in, their brand kit + ready to fill in, and the trial credits — in one place, so no route + can forget a step. */ + after: async (created) => { + await bootstrapWorkspace({ id: created.id, name: created.name, email: created.email }); + }, + }, + }, + }, + advanced: { database: { generateId: "uuid" } }, + plugins: [nextCookies()], +}); + +export type Session = typeof auth.$Infer.Session; diff --git a/src/auth/client.ts b/src/auth/client.ts new file mode 100644 index 0000000..fd694de --- /dev/null +++ b/src/auth/client.ts @@ -0,0 +1,6 @@ +"use client"; + +import { createAuthClient } from "better-auth/react"; + +export const authClient = createAuthClient(); +export const { signIn, signUp, signOut, useSession } = authClient; diff --git a/src/db/client.ts b/src/db/client.ts new file mode 100644 index 0000000..0fdce4e --- /dev/null +++ b/src/db/client.ts @@ -0,0 +1,32 @@ +import { createClient, type Client } from "@libsql/client"; +import { drizzle, type LibSQLDatabase } from "drizzle-orm/libsql"; + +import * as schema from "./schema"; + +export type Db = LibSQLDatabase; + +/** One connection per process. Local development and single-node self-hosting + use a file; Turso (or any libsql server) takes a URL and a token. */ +function resolveUrl(): { url: string; authToken?: string } { + const url = process.env.DATABASE_URL?.trim(); + if (url) return { url, authToken: process.env.DATABASE_AUTH_TOKEN?.trim() || undefined }; + return { url: "file:./data/vitrina.db" }; +} + +declare global { + var __vitrinaDb: { client: Client; db: Db } | undefined; +} + +function connect(): { client: Client; db: Db } { + const client = createClient(resolveUrl()); + return { client, db: drizzle(client, { schema }) }; +} + +/* Next's dev server re-evaluates modules on every edit; the handle is parked + on globalThis so a session of edits does not leak a connection each. */ +const handle = globalThis.__vitrinaDb ?? connect(); +if (process.env.NODE_ENV !== "production") globalThis.__vitrinaDb = handle; + +export const db: Db = handle.db; +export const dbClient: Client = handle.client; +export { schema }; diff --git a/src/db/migrate.ts b/src/db/migrate.ts new file mode 100644 index 0000000..d51f49c --- /dev/null +++ b/src/db/migrate.ts @@ -0,0 +1,11 @@ +import { migrate } from "drizzle-orm/libsql/migrator"; +import { resolve } from "node:path"; + +import { db } from "./client"; + +/** Applies every migration in drizzle/ that the database has not seen. Safe to + run on every boot: an up-to-date database is a no-op. The folder sits next + to the server in both a checkout and the standalone build. */ +export async function runMigrations(): Promise { + await migrate(db, { migrationsFolder: resolve(/*turbopackIgnore: true*/ process.cwd(), "drizzle") }); +} diff --git a/src/db/schema.ts b/src/db/schema.ts new file mode 100644 index 0000000..065a630 --- /dev/null +++ b/src/db/schema.ts @@ -0,0 +1,296 @@ +import { sql } from "drizzle-orm"; +import { index, integer, primaryKey, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core"; + +/* ---------- better-auth ---------- */ + +export const user = sqliteTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: integer("email_verified", { mode: "boolean" }).default(false).notNull(), + image: text("image"), + createdAt: integer("created_at", { mode: "timestamp_ms" }).default(sql`(unixepoch() * 1000)`).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).default(sql`(unixepoch() * 1000)`).notNull(), +}); + +export const session = sqliteTable( + "session", + { + id: text("id").primaryKey(), + expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), + token: text("token").notNull().unique(), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + }, + (table) => [index("session_user_idx").on(table.userId)], +); + +export const account = sqliteTable( + "account", + { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: integer("access_token_expires_at", { mode: "timestamp_ms" }), + refreshTokenExpiresAt: integer("refresh_token_expires_at", { mode: "timestamp_ms" }), + scope: text("scope"), + password: text("password"), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [index("account_user_idx").on(table.userId)], +); + +export const verification = sqliteTable( + "verification", + { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), + createdAt: integer("created_at", { mode: "timestamp_ms" }).default(sql`(unixepoch() * 1000)`).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).default(sql`(unixepoch() * 1000)`).notNull(), + }, + (table) => [index("verification_identifier_idx").on(table.identifier)], +); + +/* ---------- workspace ---------- */ + +export const workspace = sqliteTable( + "workspace", + { + id: text("id").primaryKey(), + name: text("name").notNull(), + slug: text("slug").notNull().unique(), + ownerId: text("owner_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + planId: text("plan_id").notNull().default("free"), + /** Cached sum of the ledger, kept in the same transaction as every entry. */ + creditBalance: integer("credit_balance").notNull().default(0), + stripeCustomerId: text("stripe_customer_id"), + stripeSubscriptionId: text("stripe_subscription_id"), + /** Unix ms of the current period's end, from the subscription. */ + planRenewsAt: integer("plan_renews_at", { mode: "timestamp_ms" }), + /** Bring-your-own-key: the platform key, AES-GCM sealed with APP_SECRET. */ + platformKeySealed: text("platform_key_sealed"), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [index("workspace_owner_idx").on(table.ownerId)], +); + +export const membership = sqliteTable( + "membership", + { + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + role: text("role", { enum: ["owner", "editor"] }).notNull().default("editor"), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [primaryKey({ columns: [table.workspaceId, table.userId] }), index("membership_user_idx").on(table.userId)], +); + +/** An invitation link. Whoever signs in holding the token joins the + workspace, up to the plan's seats. */ +export const invite = sqliteTable( + "invite", + { + id: text("id").primaryKey(), + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + token: text("token").notNull().unique(), + role: text("role", { enum: ["owner", "editor"] }).notNull().default("editor"), + createdBy: text("created_by").references(() => user.id, { onDelete: "set null" }), + expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), + acceptedBy: text("accepted_by").references(() => user.id, { onDelete: "set null" }), + acceptedAt: integer("accepted_at", { mode: "timestamp_ms" }), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [index("invite_workspace_idx").on(table.workspaceId)], +); + +export const brandKit = sqliteTable("brand_kit", { + workspaceId: text("workspace_id") + .primaryKey() + .references(() => workspace.id, { onDelete: "cascade" }), + name: text("name").notNull().default(""), + tagline: text("tagline").notNull().default(""), + tone: text("tone").notNull().default(""), + style: text("style").notNull().default(""), + palette: text("palette", { mode: "json" }).$type().notNull().default([]), + audience: text("audience").notNull().default(""), + avoid: text("avoid").notNull().default(""), + logoUrl: text("logo_url").notNull().default(""), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), +}); + +export const product = sqliteTable( + "product", + { + id: text("id").primaryKey(), + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + name: text("name").notNull(), + slug: text("slug").notNull(), + category: text("category").notNull().default("Other"), + description: text("description").notNull().default(""), + features: text("features", { mode: "json" }).$type().notNull().default([]), + appearance: text("appearance").notNull().default(""), + audience: text("audience").notNull().default(""), + priceLabel: text("price_label").notNull().default(""), + archivedAt: integer("archived_at", { mode: "timestamp_ms" }), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [ + index("product_workspace_idx").on(table.workspaceId), + uniqueIndex("product_workspace_slug_idx").on(table.workspaceId, table.slug), + ], +); + +export const productAsset = sqliteTable( + "product_asset", + { + id: text("id").primaryKey(), + productId: text("product_id") + .notNull() + .references(() => product.id, { onDelete: "cascade" }), + url: text("url").notNull(), + kind: text("kind", { enum: ["image", "video"] }).notNull().default("image"), + name: text("name").notNull().default(""), + position: integer("position").notNull().default(0), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [index("product_asset_product_idx").on(table.productId)], +); + +export const upload = sqliteTable( + "upload", + { + id: text("id").primaryKey(), + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + url: text("url").notNull(), + kind: text("kind", { enum: ["image", "video", "audio"] }).notNull().default("image"), + name: text("name").notNull().default(""), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [index("upload_workspace_idx").on(table.workspaceId)], +); + +export const campaign = sqliteTable( + "campaign", + { + id: text("id").primaryKey(), + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + productId: text("product_id") + .notNull() + .references(() => product.id, { onDelete: "cascade" }), + name: text("name").notNull(), + scene: text("scene").notNull().default(""), + presetIds: text("preset_ids", { mode: "json" }).$type().notNull().default([]), + channelIds: text("channel_ids", { mode: "json" }).$type().notNull().default([]), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [index("campaign_workspace_idx").on(table.workspaceId)], +); + +export const run = sqliteTable( + "run", + { + id: text("id").primaryKey(), + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + userId: text("user_id").references(() => user.id, { onDelete: "set null" }), + /** Platform request. One request can deliver several rows (a batch). */ + requestId: text("request_id").notNull(), + productId: text("product_id").references(() => product.id, { onDelete: "set null" }), + campaignId: text("campaign_id").references(() => campaign.id, { onDelete: "set null" }), + presetId: text("preset_id"), + channelId: text("channel_id"), + surface: text("surface", { enum: ["image", "video"] }).notNull(), + modelId: text("model_id").notNull(), + modelLabel: text("model_label").notNull(), + prompt: text("prompt").notNull(), + scene: text("scene").notNull().default(""), + ratio: text("ratio").notNull(), + meta: text("meta").notNull().default(""), + badge: text("badge"), + urls: text("urls", { mode: "json" }).$type().notNull().default([]), + status: text("status", { enum: ["running", "completed", "failed"] }).notNull(), + error: text("error"), + favorite: integer("favorite", { mode: "boolean" }).notNull().default(false), + review: text("review", { enum: ["pending", "approved", "rejected"] }).notNull().default("pending"), + settings: text("settings", { mode: "json" }).$type>().notNull().default({}), + credits: integer("credits").notNull().default(0), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull(), + deletedAt: integer("deleted_at", { mode: "timestamp_ms" }), + }, + (table) => [ + index("run_workspace_created_idx").on(table.workspaceId, table.createdAt), + index("run_request_idx").on(table.requestId), + index("run_campaign_idx").on(table.campaignId), + ], +); + +export const creditLedger = sqliteTable( + "credit_ledger", + { + id: text("id").primaryKey(), + workspaceId: text("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + delta: integer("delta").notNull(), + reason: text("reason", { + enum: ["trial", "plan_grant", "purchase", "generation", "refund", "adjustment"], + }).notNull(), + requestId: text("request_id"), + stripeEventId: text("stripe_event_id"), + note: text("note"), + createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(), + }, + (table) => [ + index("ledger_workspace_idx").on(table.workspaceId, table.createdAt), + uniqueIndex("ledger_request_reason_idx").on(table.requestId, table.reason), + ], +); + +/** Every Stripe event handled, so a redelivered webhook is a no-op. */ +export const stripeEvent = sqliteTable("stripe_event", { + id: text("id").primaryKey(), + type: text("type").notNull(), + processedAt: integer("processed_at", { mode: "timestamp_ms" }).notNull(), +}); + +export type Workspace = typeof workspace.$inferSelect; +export type InviteRow = typeof invite.$inferSelect; +export type ProductRow = typeof product.$inferSelect; +export type ProductAssetRow = typeof productAsset.$inferSelect; +export type RunRow = typeof run.$inferSelect; +export type CampaignRow = typeof campaign.$inferSelect; +export type UploadRow = typeof upload.$inferSelect; +export type BrandKitRow = typeof brandKit.$inferSelect; diff --git a/src/domain/__tests__/pricing.test.ts b/src/domain/__tests__/pricing.test.ts new file mode 100644 index 0000000..4a1e4a4 --- /dev/null +++ b/src/domain/__tests__/pricing.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from "vitest"; + +import { getModel } from "@/generation/catalog"; + +import { exportPath } from "../export"; +import { PLANS, formatPrice, getPlan } from "../plans"; +import { creditsFor } from "../pricing"; + +describe("creditsFor", () => { + it("prices images by resolution and count", () => { + const flux = getModel("flux-2"); + expect(creditsFor(flux, { resolution: "1k" })).toBe(1); + expect(creditsFor(flux, { resolution: "2k" })).toBe(2); + expect(creditsFor(flux, { resolution: "4k" })).toBe(4); + const soul = getModel("soul-2"); + expect(creditsFor(soul, { resolution: "720p", batchSize: "4" })).toBe(4); + expect(creditsFor(getModel("soul-cinema"), { resolution: "1080p", batchSize: "1" })).toBe(3); + }); + + it("prices video per second, by resolution and tier, with a floor", () => { + const seedance = getModel("seedance-2"); + expect(creditsFor(seedance, { duration: 5, resolution: "720p" })).toBe(5); + expect(creditsFor(seedance, { duration: 10, resolution: "1080p" })).toBe(20); + expect(creditsFor(getModel("seedance-2-fast"), { duration: 4, resolution: "480p" })).toBe(4); + expect(creditsFor(getModel("kling-3-pro"), { duration: 10, resolution: "1080p" })).toBe(30); + expect(creditsFor(getModel("kling-3-4k"), { duration: 5 })).toBe(10); + }); + + it("always returns a positive integer", () => { + for (const id of ["flux-2", "seedance-2.5", "kling-3-turbo", "ltx-2.5-fast"]) { + const credits = creditsFor(getModel(id), {}); + expect(Number.isInteger(credits)).toBe(true); + expect(credits).toBeGreaterThan(0); + } + }); +}); + +describe("plans", () => { + it("has a free tier first and ascending prices", () => { + expect(PLANS[0]!.id).toBe("free"); + for (let i = 1; i < PLANS.length; i++) { + expect(PLANS[i]!.priceCents).toBeGreaterThan(PLANS[i - 1]!.priceCents); + } + expect(getPlan("nope").id).toBe("free"); + expect(formatPrice(2900)).toMatch(/29/); + }); +}); + +describe("exportPath", () => { + it("files runs by brand, product and channel with a stable name", () => { + const path = exportPath({ + brand: "Aurora Home", + product: "Aurora Ceramic Mug — Sage", + channelTag: "ig-story", + presetId: "lifestyle-scene", + url: "https://cdn.example/abc.jpg?x=1", + kind: "image", + index: 2, + }); + expect(path).toBe("aurora-home/aurora-ceramic-mug-sage/ig-story/lifestyle-scene-03.jpg"); + expect( + exportPath({ brand: "", product: "", channelTag: null, presetId: null, url: "https://x/y", kind: "video", index: 0 }), + ).toBe("brand/product/studio/run-01.mp4"); + }); +}); diff --git a/src/domain/__tests__/prompt.test.ts b/src/domain/__tests__/prompt.test.ts new file mode 100644 index 0000000..8e50717 --- /dev/null +++ b/src/domain/__tests__/prompt.test.ts @@ -0,0 +1,124 @@ +import { describe, expect, it } from "vitest"; + +import { MODELS, getModel } from "@/generation/catalog"; +import { parseSettings } from "@/generation/catalog"; + +import { EMPTY_BRAND, type BrandKit, type ProductFields } from "../brand"; +import { getChannel, resolveAspect, resolveDuration } from "../channels"; +import { PRESETS, getPreset } from "../presets"; +import { compilePrompt, pickModel, resolveSettings } from "../prompt"; + +const product: ProductFields = { + name: "Aurora ceramic mug", + category: "Home & furniture", + description: "A hand-glazed stoneware mug.", + features: ["350 ml", "dishwasher safe"], + appearance: "matte sage green glaze, speckled, natural clay rim", + audience: "", + priceLabel: "€24", +}; + +const brand: BrandKit = { + ...EMPTY_BRAND, + name: "Aurora Home", + style: "minimal scandinavian, soft daylight", + palette: ["#9CAF88", "#F4F1EA"], + audience: "design-conscious home cooks", + avoid: "text, clutter, other brands", +}; + +describe("compilePrompt", () => { + it("folds product, brand, scene and channel into the lifestyle template", () => { + const text = compilePrompt(getPreset("lifestyle-scene").template, { + product, + brand, + channel: getChannel("instagram_story"), + scene: "a sunlit kitchen counter at breakfast", + }); + expect(text).toContain("Aurora ceramic mug (home & furniture) in a sunlit kitchen counter at breakfast."); + expect(text).toContain("Appearance: matte sage green glaze"); + expect(text).toContain("Visual style: minimal scandinavian"); + expect(text).toContain("Made to appeal to design-conscious home cooks."); + expect(text).toContain("Vertical full-screen framing"); + expect(text).toContain("Avoid: text, clutter, other brands."); + expect(text).not.toMatch(/\{\{/); + }); + + it("drops lines whose tokens are all empty and never leaves dangling connectors", () => { + const text = compilePrompt(getPreset("lifestyle-scene").template, { + product: { ...product, appearance: "", category: "Other" }, + brand: null, + channel: null, + scene: "", + }); + expect(text.startsWith("Lifestyle product photograph of Aurora ceramic mug.")).toBe(true); + expect(text).not.toContain("Appearance"); + expect(text).not.toContain("Avoid"); + expect(text).not.toMatch(/\sin\.$/m); + expect(text).not.toContain(" "); + }); + + it("works with nothing but a scene", () => { + const text = compilePrompt(getPreset("pack-shot").template, { + product: null, + brand: null, + channel: null, + scene: "", + }); + expect(text).toContain("Seamless pure white studio background"); + expect(text).not.toContain("photograph of ."); + }); +}); + +describe("resolveSettings", () => { + it("applies the preset then the channel, only for keys the model declares", () => { + const model = getModel("seedance-2.5"); + const settings = resolveSettings(model, getPreset("lifestyle-broll"), getChannel("tiktok")); + expect(settings.aspectRatio).toBe("9:16"); + expect(settings.duration).toBe(10); + expect(settings.resolution).toBeUndefined(); // 1080p is not a seedance-2.5 value + expect(settings.generateAudio).toBe(true); + expect(() => parseSettings(model, settings)).not.toThrow(); + }); + + it("lets explicit overrides win over preset and channel", () => { + const model = getModel("kling-3-std"); + const settings = resolveSettings(model, getPreset("product-spin"), getChannel("youtube_ad"), { + duration: 7, + aspectRatio: "1:1", + }); + expect(settings.duration).toBe(7); + expect(settings.aspectRatio).toBe("1:1"); + expect(settings.sound).toBe(false); + }); + + it("falls back to the closest ratio the model can make", () => { + const flux = getModel("flux-2"); + expect(resolveAspect(getChannel("pinterest"), flux)).toBe("3:4"); + expect(resolveAspect(getChannel("instagram_feed"), flux)).toBe("1:1"); + expect(resolveDuration(getChannel("tiktok"), getModel("kling-3-turbo"))).toBe(10); + expect(resolveDuration(getChannel("tiktok"), flux)).toBeNull(); + }); +}); + +describe("presets", () => { + it("every preset resolves to a real model of its surface and parses cleanly", () => { + for (const preset of PRESETS) { + const model = pickModel(preset, MODELS); + expect(model.surface).toBe(preset.surface); + for (const channelId of preset.channels) { + const channel = getChannel(channelId); + expect(channel.surfaces).toContain(preset.surface); + const settings = resolveSettings(model, preset, channel); + expect(() => parseSettings(model, settings)).not.toThrow(); + } + if (preset.productRole) expect(model.roles[preset.productRole]).toBeGreaterThan(0); + } + }); + + it("prefers the first catalog model in the list", () => { + expect(pickModel(getPreset("pack-shot"), MODELS).id).toBe("flux-2"); + const noFlux = MODELS.filter((entry) => entry.id !== "flux-2"); + expect(pickModel(getPreset("pack-shot"), noFlux).id).toBe("qwen-image-3"); + }); +}); diff --git a/src/domain/brand.ts b/src/domain/brand.ts new file mode 100644 index 0000000..31ccd18 --- /dev/null +++ b/src/domain/brand.ts @@ -0,0 +1,97 @@ +import { z } from "zod"; + +/** Everything the studio knows about the brand, written once and folded into + every prompt. Empty fields are simply left out of the compiled prompt. */ +export const brandKitSchema = z.object({ + name: z.string().trim().min(1, "Brand name is required").max(80), + tagline: z.string().trim().max(160).default(""), + /** How the brand speaks — "warm and playful", "clinical and premium". */ + tone: z.string().trim().max(160).default(""), + /** Visual direction — "minimal scandinavian", "bold streetwear". */ + style: z.string().trim().max(240).default(""), + /** Hex colors, most important first. */ + palette: z.array(z.string().regex(/^#[0-9a-fA-F]{6}$/, "Use #RRGGBB")).max(6).default([]), + /** Who buys: "urban runners 25–40", "new parents". */ + audience: z.string().trim().max(200).default(""), + /** Things never to show — competitors, text, clutter. */ + avoid: z.string().trim().max(240).default(""), + logoUrl: z.string().url().or(z.literal("")).default(""), +}); + +export type BrandKit = z.infer; +export type BrandKitInput = z.input; + +export const EMPTY_BRAND: BrandKit = { + name: "", + tagline: "", + tone: "", + style: "", + palette: [], + audience: "", + avoid: "", + logoUrl: "", +}; + +export const PRODUCT_CATEGORIES = [ + "Apparel", + "Footwear", + "Beauty & skincare", + "Food & beverage", + "Home & furniture", + "Electronics", + "Jewelry & accessories", + "Sports & outdoor", + "Toys & kids", + "Pet", + "Other", +] as const; + +export type ProductCategory = (typeof PRODUCT_CATEGORIES)[number]; + +export const productSchema = z.object({ + name: z.string().trim().min(1, "Product name is required").max(120), + category: z.enum(PRODUCT_CATEGORIES).default("Other"), + description: z.string().trim().max(600).default(""), + /** Short selling points, one per line in the form. */ + features: z.array(z.string().trim().min(1).max(120)).max(8).default([]), + /** Materials, colorway, finish — what the model must reproduce faithfully. */ + appearance: z.string().trim().max(300).default(""), + audience: z.string().trim().max(200).default(""), + priceLabel: z.string().trim().max(40).default(""), +}); + +export type ProductInput = z.input; +export type ProductFields = z.infer; + +export type ProductAsset = { + id: string; + url: string; + kind: "image" | "video"; + name: string; +}; + +export type Product = ProductFields & { + id: string; + slug: string; + assets: ProductAsset[]; +}; + +export function slugify(text: string, fallback = "item"): string { + const slug = text + .normalize("NFKD") + .replace(/[̀-ͯ]/g, "") + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(0, 48) + .replace(/-+$/, ""); + return slug || fallback; +} + +/** "one per line" text areas arrive as a string; the schema wants a list. */ +export function linesToList(text: string): string[] { + return text + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean); +} diff --git a/src/domain/channels.ts b/src/domain/channels.ts new file mode 100644 index 0000000..17cf5f7 --- /dev/null +++ b/src/domain/channels.ts @@ -0,0 +1,144 @@ +import type { ModelEntry, Surface } from "@/generation/catalog/types"; + +/** Where a finished asset is going to be published. A channel is the one thing + an e-commerce team never negotiates: Instagram wants 4:5 or 1:1, a Story is + 9:16, Amazon's main image is square on white. The channel decides ratio and + duration; the preset decides what is in the frame. */ +export type ChannelId = + | "instagram_feed" + | "instagram_story" + | "tiktok" + | "amazon_listing" + | "shopify_hero" + | "youtube_ad" + | "pinterest" + | "email_banner"; + +export type Channel = { + id: ChannelId; + label: string; + network: string; + surfaces: readonly Surface[]; + /** Preferred aspect ratio first, then the closest fallbacks in order. The + resolver walks the list until the model declares one of them. */ + aspects: readonly string[]; + video?: { min: number; max: number; preferred: number }; + /** Composition guidance appended to the prompt — text-safe areas, the white + background a marketplace mandates, the vertical framing of a Story. */ + guidance: string; + /** Short tag used in exported file names. */ + tag: string; +}; + +export const CHANNELS: readonly Channel[] = [ + { + id: "instagram_feed", + label: "Instagram feed", + network: "Instagram", + surfaces: ["image", "video"], + aspects: ["4:5", "1:1", "3:4", "4:3"], + video: { min: 3, max: 60, preferred: 8 }, + guidance: "Composed for a mobile feed: the product large and centered, clean edges, no text baked into the image.", + tag: "ig-feed", + }, + { + id: "instagram_story", + label: "Instagram Story / Reel", + network: "Instagram", + surfaces: ["image", "video"], + aspects: ["9:16", "3:4", "1:1"], + video: { min: 3, max: 15, preferred: 8 }, + guidance: "Vertical full-screen framing; keep the top 15% and bottom 20% free of key detail so captions and stickers do not cover the product.", + tag: "ig-story", + }, + { + id: "tiktok", + label: "TikTok", + network: "TikTok", + surfaces: ["video"], + aspects: ["9:16", "3:4", "1:1"], + video: { min: 5, max: 15, preferred: 10 }, + guidance: "Vertical, energetic and native-looking; the first second must already show the product; leave the right edge free for the interface icons.", + tag: "tiktok", + }, + { + id: "amazon_listing", + label: "Amazon main image", + network: "Amazon", + surfaces: ["image"], + aspects: ["1:1", "4:3", "3:4"], + guidance: "Marketplace main-image rules: pure white seamless background, the product filling about 85% of the frame, no props, no text, no watermark, realistic colors.", + tag: "amazon", + }, + { + id: "shopify_hero", + label: "Storefront hero", + network: "Shopify / web", + surfaces: ["image", "video"], + aspects: ["16:9", "21:9", "4:3"], + video: { min: 4, max: 12, preferred: 6 }, + guidance: "Wide banner composition with the product on one third of the frame and calm negative space on the other side for a headline and a button.", + tag: "hero", + }, + { + id: "youtube_ad", + label: "YouTube ad", + network: "YouTube", + surfaces: ["video"], + aspects: ["16:9", "4:3"], + video: { min: 6, max: 15, preferred: 10 }, + guidance: "Landscape, cinematic pacing with a clear product reveal in the first three seconds and a steady closing frame for an end card.", + tag: "youtube", + }, + { + id: "pinterest", + label: "Pinterest pin", + network: "Pinterest", + surfaces: ["image"], + aspects: ["2:3", "3:4", "9:16", "1:1"], + guidance: "Tall editorial pin with soft natural light and an aspirational, magazine-like styling.", + tag: "pinterest", + }, + { + id: "email_banner", + label: "Email banner", + network: "Email", + surfaces: ["image"], + aspects: ["16:9", "21:9", "4:3"], + guidance: "Wide, bright and legible at small sizes; product on one side, uncluttered space for a headline on the other.", + tag: "email", + }, +]; + +export function getChannel(id: string): Channel { + const channel = CHANNELS.find((entry) => entry.id === id); + if (!channel) throw new Error(`Unknown channel: ${id}`); + return channel; +} + +export function isChannelId(id: string): id is ChannelId { + return CHANNELS.some((entry) => entry.id === id); +} + +export function channelsFor(surface: Surface): Channel[] { + return CHANNELS.filter((channel) => channel.surfaces.includes(surface)); +} + +/** The ratio this model can actually make for this channel, or null when the + model declares no aspect setting at all (the platform decides). */ +export function resolveAspect(channel: Channel, model: ModelEntry): string | null { + const field = model.settings.aspectRatio; + if (!field || field.type !== "enum") return null; + for (const candidate of channel.aspects) { + if (field.values.includes(candidate)) return candidate; + } + return field.default; +} + +/** The channel's preferred length, clamped into what the model allows. */ +export function resolveDuration(channel: Channel, model: ModelEntry): number | null { + const field = model.settings.duration; + if (!field || field.type !== "range" || !channel.video) return null; + const wanted = Math.min(channel.video.max, Math.max(channel.video.min, channel.video.preferred)); + return Math.min(field.max, Math.max(field.min, wanted)); +} diff --git a/src/domain/export.ts b/src/domain/export.ts new file mode 100644 index 0000000..4ff515f --- /dev/null +++ b/src/domain/export.ts @@ -0,0 +1,26 @@ +import { slugify } from "./brand"; + +export type ExportItem = { + brand: string; + product: string; + channelTag: string | null; + presetId: string | null; + url: string; + kind: "image" | "video"; + index: number; +}; + +/** The path a run takes inside a campaign export. Folders by product and + channel, because that is how a marketing team hands assets over: "the + Amazon set", "the Story set". */ +export function exportPath(item: ExportItem): string { + const ext = extensionOf(item.url) ?? (item.kind === "video" ? "mp4" : "png"); + const folder = [slugify(item.brand, "brand"), slugify(item.product, "product"), item.channelTag ?? "studio"].join("/"); + const name = `${item.presetId ?? "run"}-${String(item.index + 1).padStart(2, "0")}.${ext}`; + return `${folder}/${name}`; +} + +export function extensionOf(url: string): string | null { + const match = /\.([a-z0-9]{2,4})(?:[?#]|$)/i.exec(url); + return match ? match[1]!.toLowerCase() : null; +} diff --git a/src/domain/index.ts b/src/domain/index.ts new file mode 100644 index 0000000..8578a02 --- /dev/null +++ b/src/domain/index.ts @@ -0,0 +1,20 @@ +/** The vertical, as data. Swapping the industry this product serves means + replacing the presets and channels and the words below — the studio, the + billing and the generation engine do not change. See docs/VERTICAL.md. */ +export const VERTICAL = { + id: "ecommerce", + /** What the product calls itself. */ + name: "Vitrina", + descriptor: "AI product visuals for e-commerce brands", + /** What a "product" is called in this industry's UI. */ + subjectNoun: "product", + subjectNounPlural: "products", +} as const; + +export * from "./brand"; +export * from "./channels"; +export * from "./export"; +export * from "./plans"; +export * from "./presets"; +export * from "./pricing"; +export * from "./prompt"; diff --git a/src/domain/plans.ts b/src/domain/plans.ts new file mode 100644 index 0000000..b2df256 --- /dev/null +++ b/src/domain/plans.ts @@ -0,0 +1,71 @@ +/** What the studio sells. Prices are in cents of the currency Stripe is set up + in. The Stripe price ids live in the environment, not here, so the same + plan table serves test and live mode. */ +export type PlanId = "free" | "starter" | "pro" | "studio"; + +export type Plan = { + id: PlanId; + label: string; + /** Credits granted on activation and on every renewal. */ + credits: number; + /** Monthly price in cents; 0 for the free tier. */ + priceCents: number; + seats: number; + blurb: string; + features: readonly string[]; +}; + +export const CURRENCY = "eur"; + +export const PLANS: readonly Plan[] = [ + { + id: "free", + label: "Trial", + credits: 30, + priceCents: 0, + seats: 1, + blurb: "Try the whole studio on a real product.", + features: ["30 credits once", "Every preset and channel", "1 seat"], + }, + { + id: "starter", + label: "Starter", + credits: 500, + priceCents: 2900, + seats: 2, + blurb: "A small catalog, refreshed every month.", + features: ["500 credits / month", "Campaign exports", "2 seats"], + }, + { + id: "pro", + label: "Pro", + credits: 2000, + priceCents: 9900, + seats: 5, + blurb: "Content for a growing store and its ads.", + features: ["2,000 credits / month", "Priority generation", "5 seats", "Bring your own key"], + }, + { + id: "studio", + label: "Studio", + credits: 6000, + priceCents: 24900, + seats: 15, + blurb: "Agencies and multi-brand teams.", + features: ["6,000 credits / month", "Multiple brands", "15 seats", "Bring your own key"], + }, +]; + +export function getPlan(id: string | null | undefined): Plan { + return PLANS.find((plan) => plan.id === id) ?? PLANS[0]!; +} + +export function isPlanId(id: string): id is PlanId { + return PLANS.some((plan) => plan.id === id); +} + +export function formatPrice(cents: number, currency = CURRENCY, locale = "en"): string { + return new Intl.NumberFormat(locale, { style: "currency", currency, maximumFractionDigits: 0 }).format( + cents / 100, + ); +} diff --git a/src/domain/presets.ts b/src/domain/presets.ts new file mode 100644 index 0000000..880b20c --- /dev/null +++ b/src/domain/presets.ts @@ -0,0 +1,285 @@ +import type { Surface } from "@/generation/catalog/types"; + +import type { ChannelId } from "./channels"; + +export type PresetCategory = "studio" | "lifestyle" | "social" | "video"; + +/** A preset is one job an e-commerce team asks for by name — "pack shot", + "lifestyle scene", "product spin". It carries the prompt skeleton, the + models known to do it well and the settings that job needs. The catalog + still decides what each model can take; a preset only chooses. */ +export type Preset = { + id: string; + label: string; + description: string; + surface: Surface; + category: PresetCategory; + /** Models in order of preference. The first one present in the catalog is + used, so a catalog that loses a model degrades to the next. */ + models: readonly string[]; + /** Settings the job insists on, applied over the model's defaults and under + what the channel resolves. Keys the model does not declare are dropped. */ + settings: Record; + /** Prompt skeleton. Lines whose tokens all resolve empty are dropped. */ + template: string; + /** Media role the product photo is attached under, when the preset is + built on a real photo of the product rather than a description. */ + productRole: "reference" | "start" | null; + /** What the scene field asks for, and an example. */ + scenePlaceholder: string; + sceneExamples: readonly string[]; + channels: readonly ChannelId[]; +}; + +const IMAGE_MODELS = ["flux-2", "qwen-image-3", "z-image-turbo", "grok-imagine-2", "ideogram-4", "recraft-4.1"]; +const VIDEO_MODELS = ["seedance-2.5", "seedance-2", "kling-3-std", "kling-3-turbo", "wan-3", "minimax-hailuo-2.3"]; + +export const PRESETS: readonly Preset[] = [ + { + id: "pack-shot", + label: "Pack shot", + description: "The product alone on seamless white — the marketplace main image.", + surface: "image", + category: "studio", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Professional e-commerce product photograph of {{product.name}}{{product.categoryPhrase}}, {{scene}}.", + "{{product.appearance}}", + "Seamless pure white studio background, soft even lighting from a large softbox, subtle natural contact shadow, sharp focus across the whole product, true-to-life colors.", + "Product exactly as in the reference photo, centered, filling most of the frame, no props, no text, no logo overlays.", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Optional angle or detail — front three-quarter view, lid open…", + sceneExamples: ["front three-quarter view", "top-down flat view", "close on the label"], + channels: ["amazon_listing", "shopify_hero", "instagram_feed"], + }, + { + id: "lifestyle-scene", + label: "Lifestyle scene", + description: "The product in use, in a real place your customer recognises.", + surface: "image", + category: "lifestyle", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Lifestyle product photograph of {{product.name}}{{product.categoryPhrase}} in {{scene}}.", + "{{product.appearance}}", + "{{brand.styleLine}}", + "Natural, believable environment, the product is the hero and clearly recognisable from the reference photo, shallow depth of field, editorial photography, no text.", + "{{brand.audienceLine}}", + "{{channel.guidance}}", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Where is it? — a sunlit kitchen counter at breakfast, a trail at golden hour…", + sceneExamples: [ + "a bright scandinavian kitchen at breakfast, morning light", + "a rooftop terrace at golden hour, city behind", + "a rainy café window seat, warm interior light", + ], + channels: ["instagram_feed", "instagram_story", "pinterest", "shopify_hero", "email_banner"], + }, + { + id: "flat-lay", + label: "Flat lay", + description: "Top-down arrangement with a few props that tell the product's story.", + surface: "image", + category: "studio", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Top-down flat lay photograph featuring {{product.name}}{{product.categoryPhrase}}, styled with {{scene}}.", + "{{product.appearance}}", + "{{brand.styleLine}}", + "{{brand.paletteLine}}", + "Clean composition on a single surface, balanced negative space, soft directional daylight, crisp detail, no text.", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Props and surface — linen cloth, dried flowers, a ceramic cup…", + sceneExamples: ["linen cloth, dried eucalyptus and a ceramic cup", "raw oak board, sea salt and lemon halves"], + channels: ["instagram_feed", "pinterest", "email_banner"], + }, + { + id: "hero-banner", + label: "Hero banner", + description: "Wide storefront banner with room for a headline.", + surface: "image", + category: "studio", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Wide hero banner photograph of {{product.name}}{{product.categoryPhrase}}, {{scene}}.", + "{{product.appearance}}", + "{{brand.styleLine}}", + "{{brand.paletteLine}}", + "Product placed on one third of the frame, calm uncluttered negative space on the other side for a headline, premium commercial lighting, no text in the image.", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Backdrop and mood — on a marble ledge with soft shadows, floating on a gradient…", + sceneExamples: ["on a marble ledge with long soft shadows", "on a smooth color gradient backdrop, studio rim light"], + channels: ["shopify_hero", "email_banner"], + }, + { + id: "seasonal-campaign", + label: "Seasonal campaign", + description: "The product dressed for a moment — Christmas, summer sale, back to school.", + surface: "image", + category: "social", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Seasonal campaign photograph of {{product.name}}{{product.categoryPhrase}} for {{scene}}.", + "{{product.appearance}}", + "{{brand.styleLine}}", + "{{brand.paletteLine}}", + "Festive but tasteful set dressing that never hides the product, cohesive color story, commercial photography quality, no text.", + "{{channel.guidance}}", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "The occasion — Christmas gifting, summer beach season, Valentine's…", + sceneExamples: ["Christmas gifting, warm fairy lights and fir branches", "summer beach season, bright sun and sand"], + channels: ["instagram_feed", "instagram_story", "email_banner", "pinterest"], + }, + { + id: "on-model", + label: "On model", + description: "Apparel and accessories worn by a person, editorial style.", + surface: "image", + category: "lifestyle", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Editorial fashion photograph of a person wearing {{product.name}}{{product.categoryPhrase}}, {{scene}}.", + "{{product.appearance}}", + "The garment matches the reference photo exactly in color, cut and detail.", + "{{brand.styleLine}}", + "{{brand.audienceLine}}", + "Natural pose, confident expression, realistic skin and fabric, professional lighting, no text.", + "{{channel.guidance}}", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Who and where — a woman in her thirties on a city street at dusk…", + sceneExamples: ["a man in his thirties walking a city street at dusk", "a woman on a windy coastal path, overcast light"], + channels: ["instagram_feed", "instagram_story", "pinterest", "shopify_hero"], + }, + { + id: "macro-detail", + label: "Macro detail", + description: "Extreme close-up on texture, stitching, finish.", + surface: "image", + category: "studio", + models: IMAGE_MODELS, + settings: { resolution: "2k" }, + template: [ + "Macro product photograph of {{product.name}}{{product.categoryPhrase}}, extreme close-up on {{scene}}.", + "{{product.appearance}}", + "Razor-sharp texture, shallow depth of field, controlled studio light with a soft highlight, premium material feel, no text.", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Which detail — the stitching, the brushed metal cap, the woven strap…", + sceneExamples: ["the stitching and leather grain", "the brushed metal cap and embossed logo"], + channels: ["instagram_feed", "shopify_hero"], + }, + { + id: "product-spin", + label: "Product reveal", + description: "A slow orbit or push-in on the product from your photo.", + surface: "video", + category: "video", + models: VIDEO_MODELS, + settings: { resolution: "1080p", generateAudio: false, sound: false }, + template: [ + "Slow cinematic camera orbit around {{product.name}}{{product.categoryPhrase}}, starting exactly from the reference frame.", + "{{scene}}", + "The product stays perfectly still and unchanged; only the camera moves. Soft studio lighting with gentle highlights travelling across the surface, clean background, no text.", + "{{brand.avoid}}", + ].join("\n"), + productRole: "start", + scenePlaceholder: "Camera and light — a 180° orbit with a rim light, a slow push-in…", + sceneExamples: ["a slow 180° orbit with a travelling rim light", "a slow push-in ending on the label"], + channels: ["instagram_feed", "instagram_story", "shopify_hero", "tiktok"], + }, + { + id: "lifestyle-broll", + label: "Lifestyle b-roll", + description: "Short atmospheric clip of the product in a scene, for ads and Reels.", + surface: "video", + category: "video", + models: VIDEO_MODELS, + settings: { resolution: "1080p", generateAudio: true }, + template: [ + "Cinematic lifestyle video of {{product.name}}{{product.categoryPhrase}} in {{scene}}.", + "{{product.appearance}}", + "{{brand.styleLine}}", + "Starts from the reference frame, subtle handheld motion, natural light, the product is always recognisable and in focus, no text or captions.", + "{{channel.guidance}}", + "{{brand.avoid}}", + ].join("\n"), + productRole: "start", + scenePlaceholder: "The moment — being unpacked on a kitchen table, picked up on a morning run…", + sceneExamples: ["being picked up from a sunlit kitchen table", "carried on a morning run through a park"], + channels: ["instagram_story", "tiktok", "youtube_ad", "shopify_hero"], + }, + { + id: "ugc-style", + label: "UGC-style clip", + description: "Looks like a customer filmed it on a phone — the ad format that performs.", + surface: "video", + category: "social", + models: VIDEO_MODELS, + settings: { resolution: "720p", generateAudio: true }, + template: [ + "Authentic user-generated style phone video of a person showing {{product.name}}{{product.categoryPhrase}}, {{scene}}.", + "{{product.appearance}}", + "{{brand.audienceLine}}", + "Handheld smartphone look, natural indoor light, casual and genuine energy, the product held up to camera and clearly visible, no on-screen text.", + "{{channel.guidance}}", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "The setting — unboxing on a bed, a quick review at a desk…", + sceneExamples: ["unboxing it on a bed and holding it up to camera", "a quick excited review at a desk"], + channels: ["tiktok", "instagram_story"], + }, + { + id: "unboxing", + label: "Unboxing", + description: "Hands opening the packaging and revealing the product.", + surface: "video", + category: "video", + models: VIDEO_MODELS, + settings: { resolution: "1080p", generateAudio: true }, + template: [ + "Close-up unboxing video: hands open the packaging and reveal {{product.name}}{{product.categoryPhrase}}, {{scene}}.", + "{{product.appearance}}", + "{{brand.styleLine}}", + "Satisfying slow reveal, soft window light, tactile detail, the product matches the reference photo, no text.", + "{{brand.avoid}}", + ].join("\n"), + productRole: "reference", + scenePlaceholder: "Surface and mood — on a wooden desk with morning light…", + sceneExamples: ["on a wooden desk with soft morning light", "on a white marble counter, overhead view"], + channels: ["tiktok", "instagram_story", "youtube_ad"], + }, +]; + +export function getPreset(id: string): Preset { + const preset = PRESETS.find((entry) => entry.id === id); + if (!preset) throw new Error(`Unknown preset: ${id}`); + return preset; +} + +export function findPreset(id: string | null | undefined): Preset | null { + return (id && PRESETS.find((entry) => entry.id === id)) || null; +} + +export function presetsFor(surface: Surface): Preset[] { + return PRESETS.filter((preset) => preset.surface === surface); +} diff --git a/src/domain/pricing.ts b/src/domain/pricing.ts new file mode 100644 index 0000000..ade33fe --- /dev/null +++ b/src/domain/pricing.ts @@ -0,0 +1,45 @@ +import type { ModelEntry } from "@/generation/catalog/types"; + +/** Credits are the unit customers buy. One credit is roughly one standard + image; video is priced per second and by resolution, so a 10s 1080p clip + costs what a small batch of images does. Integers only — a ledger of + fractions is a ledger nobody can audit. */ + +const IMAGE_BASE = 1; +const IMAGE_RESOLUTION: Record = { "1k": 1, "2k": 2, "4k": 4, "720p": 1, "1080p": 2 }; + +/** Credits per second of video, by resolution. */ +const VIDEO_PER_SECOND: Record = { "480p": 0.5, "720p": 1, "1080p": 2, "4k": 4 }; +const VIDEO_DEFAULT_SECONDS = 5; +const VIDEO_MIN = 4; + +/** Premium tiers cost more on the platform and therefore here. Matched by + model id fragment so a new "pro" variant is priced without a code change. */ +const TIER_MULTIPLIER: ReadonlyArray<[RegExp, number]> = [ + [/-4k$/, 2], + [/-pro$/, 1.5], + [/cinema$/, 1.5], + [/-(fast|mini|turbo)$/, 0.75], +]; + +export function creditsFor(model: ModelEntry, settings: Record): number { + const tier = TIER_MULTIPLIER.find(([pattern]) => pattern.test(model.id))?.[1] ?? 1; + if (model.surface === "image") { + const resolution = String(settings.resolution ?? ""); + const perImage = IMAGE_BASE * (IMAGE_RESOLUTION[resolution] ?? 1) * tier; + const count = countOf(settings); + return Math.max(1, Math.ceil(perImage * count)); + } + const seconds = typeof settings.duration === "number" ? settings.duration : VIDEO_DEFAULT_SECONDS; + const resolution = String(settings.resolution ?? "720p"); + const rate = VIDEO_PER_SECOND[resolution] ?? 1; + return Math.max(VIDEO_MIN, Math.ceil(seconds * rate * tier)); +} + +function countOf(settings: Record): number { + for (const key of ["batchSize", "numImages"]) { + const value = Number(settings[key]); + if (Number.isInteger(value) && value > 0) return value; + } + return 1; +} diff --git a/src/domain/prompt.ts b/src/domain/prompt.ts new file mode 100644 index 0000000..8a2539f --- /dev/null +++ b/src/domain/prompt.ts @@ -0,0 +1,143 @@ +import type { ModelEntry } from "@/generation/catalog/types"; + +import type { BrandKit } from "./brand"; +import type { ProductFields } from "./brand"; +import { resolveAspect, resolveDuration, type Channel } from "./channels"; +import type { Preset } from "./presets"; + +export type PromptContext = { + product: ProductFields | null; + brand: BrandKit | null; + channel: Channel | null; + scene: string; +}; + +const TOKEN = /\{\{\s*([a-zA-Z.]+)\s*\}\}/g; + +function clean(text: string | undefined | null): string { + return (text ?? "").replace(/\s+/g, " ").trim(); +} + +function endsWithPunctuation(text: string): boolean { + return /[.!?…]$/.test(text); +} + +function sentence(text: string | undefined | null): string { + const value = clean(text); + if (!value) return ""; + return endsWithPunctuation(value) ? value : `${value}.`; +} + +/** Every token a template can name, resolved from the context. Tokens that + have nothing to say resolve to "" and the line they live on is dropped when + nothing else on it resolved either. */ +export function tokenValues(context: PromptContext): Record { + const { product, brand, channel } = context; + const category = product?.category && product.category !== "Other" ? product.category.toLowerCase() : ""; + const features = product?.features?.length ? product.features.map(clean).filter(Boolean) : []; + return { + "product.name": clean(product?.name), + "product.category": category, + "product.categoryPhrase": category ? ` (${category})` : "", + "product.description": sentence(product?.description), + "product.appearance": product?.appearance ? sentence(`Appearance: ${clean(product.appearance)}`) : "", + "product.features": features.length ? sentence(`Key features: ${features.join(", ")}`) : "", + "brand.name": clean(brand?.name), + "brand.tone": clean(brand?.tone), + "brand.style": clean(brand?.style), + "brand.styleLine": brand?.style ? sentence(`Visual style: ${clean(brand.style)}`) : "", + "brand.paletteLine": brand?.palette?.length + ? sentence(`Color palette leaning on ${brand.palette.join(", ")}`) + : "", + "brand.audienceLine": audienceLine(product, brand), + "brand.avoid": brand?.avoid ? sentence(`Avoid: ${clean(brand.avoid)}`) : "", + "channel.guidance": channel ? sentence(channel.guidance) : "", + scene: clean(context.scene), + }; +} + +function audienceLine(product: ProductFields | null, brand: BrandKit | null): string { + const audience = clean(product?.audience) || clean(brand?.audience); + return audience ? sentence(`Made to appeal to ${audience}`) : ""; +} + +/** Fold the context into the template. A line is kept when at least one of + its tokens resolved or it carries no tokens; then whitespace is tidied and + dangling connectors that a missing scene would leave behind are removed. */ +export function compilePrompt(template: string, context: PromptContext): string { + const values = tokenValues(context); + const lines: string[] = []; + for (const raw of template.split("\n")) { + let resolvedAny = false; + let hasToken = false; + const line = raw.replace(TOKEN, (_match, key: string) => { + hasToken = true; + const value = values[key] ?? ""; + if (value) resolvedAny = true; + return value; + }); + if (hasToken && !resolvedAny) continue; + const tidy = line + .replace(/\s+([.,])/g, "$1") + .replace(/\b(in|for|on|styled with|with)\s*[.,]/g, ".") + .replace(/,\s*\./g, ".") + .replace(/\.\s*\./g, ".") + .replace(/\s+([.,])/g, "$1") + .replace(/\s+/g, " ") + .trim(); + if (tidy) lines.push(tidy); + } + return lines.join(" ").trim(); +} + +/** The settings a job resolves to for a given model: the model's defaults, + the preset's insistences, then the channel's ratio and length on top. Keys + the catalog does not declare for this model are left out, so the result is + always something parseSettings accepts. */ +export function resolveSettings( + model: ModelEntry, + preset: Preset | null, + channel: Channel | null, + overrides: Record = {}, +): Record { + const out: Record = {}; + const declared = model.settings; + const apply = (patch: Record) => { + for (const [key, value] of Object.entries(patch)) { + const field = declared[key]; + if (!field) continue; + if (field.type === "enum" && (typeof value !== "string" || !field.values.includes(value))) continue; + if (field.type === "range" && (typeof value !== "number" || value < field.min || value > field.max)) continue; + if (field.type === "boolean" && typeof value !== "boolean") continue; + out[key] = value; + } + }; + apply(overrides); + if (preset) apply(withoutKeys(preset.settings, Object.keys(overrides))); + if (channel) { + const aspect = resolveAspect(channel, model); + if (aspect && overrides.aspectRatio === undefined) out.aspectRatio = aspect; + const duration = resolveDuration(channel, model); + if (duration !== null && overrides.duration === undefined) out.duration = duration; + } + return out; +} + +function withoutKeys(record: Record, keys: string[]): Record { + const out: Record = {}; + for (const [key, value] of Object.entries(record)) { + if (!keys.includes(key)) out[key] = value; + } + return out; +} + +/** First model of the preset's preference list that the catalog carries. */ +export function pickModel(preset: Preset, catalog: readonly ModelEntry[]): ModelEntry { + for (const id of preset.models) { + const model = catalog.find((entry) => entry.id === id && entry.surface === preset.surface); + if (model) return model; + } + const fallback = catalog.find((entry) => entry.surface === preset.surface); + if (!fallback) throw new Error(`No ${preset.surface} model in the catalog`); + return fallback; +} diff --git a/src/generation/actions.ts b/src/generation/actions.ts new file mode 100644 index 0000000..7f2a97c --- /dev/null +++ b/src/generation/actions.ts @@ -0,0 +1,212 @@ +"use server"; + +import { eq } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; +import { creditsFor } from "@/domain/pricing"; +import { InsufficientCreditsError, refundCredits, reserveCredits } from "@/server/credits"; +import { creditsOfRequest, insertRunningRows, runningRequestIds, settleRequest, type RunContext, type RunRecord } from "@/server/runs"; +import { seal, unseal } from "@/server/seal"; +import { requireViewer, type Viewer } from "@/server/session"; + +import { getModel, parseSettings } from "./catalog"; +import type { GenerationPlane } from "./catalog/types"; +import { + MissingCredentialsError, + operatorKey, + parseCredentialInput, + platformBaseUrl, + type KeyMode, +} from "./credentials"; +import { createPlatformClient } from "./platform"; +import type { GenerationStatus } from "./platform"; +import { toPlatform } from "./to-platform"; + +/* ---------- keys ---------- */ + +export type KeyStatus = { mode: KeyMode; byok: boolean; canEdit: boolean }; + +/** Where this workspace's runs are billed: its own key when it brought one, + the operator's otherwise — or nowhere, which the studio reports up front. */ +export async function platformKeyStatus(): Promise { + const viewer = await requireViewer(); + const byok = Boolean(viewer.workspace.platformKeySealed); + const mode: KeyMode = byok ? "byok" : operatorKey() ? "operator" : "none"; + return { mode, byok, canEdit: viewer.role === "owner" }; +} + +export async function savePlatformCredentials(data: unknown): Promise { + const viewer = await requireOwner(); + const { apiKey } = parseCredentialInput(data); + await db + .update(schema.workspace) + .set({ platformKeySealed: seal(apiKey), updatedAt: new Date() }) + .where(eq(schema.workspace.id, viewer.workspace.id)); + return { mode: "byok", byok: true, canEdit: true }; +} + +export async function clearPlatformCredentials(): Promise { + const viewer = await requireOwner(); + await db + .update(schema.workspace) + .set({ platformKeySealed: null, updatedAt: new Date() }) + .where(eq(schema.workspace.id, viewer.workspace.id)); + return { mode: operatorKey() ? "operator" : "none", byok: false, canEdit: true }; +} + +async function requireOwner(): Promise { + const viewer = await requireViewer(); + if (viewer.role !== "owner") throw new Error("Only the workspace owner can change the platform key"); + return viewer; +} + +function resolveCredentials(viewer: Viewer): { apiKey: string; baseUrl: string; mode: KeyMode } { + const baseUrl = platformBaseUrl(); + const sealed = viewer.workspace.platformKeySealed; + if (sealed) { + const apiKey = unseal(sealed); + if (apiKey) return { apiKey, baseUrl, mode: "byok" }; + } + const key = operatorKey(); + if (key) return { apiKey: key, baseUrl, mode: "operator" }; + throw new MissingCredentialsError(); +} + +/* ---------- generate ---------- */ + +export type SubmitInput = { + plane: GenerationPlane; + context: RunContext; + /** Tiles the studio opened for this request; the platform decides the real + count when it answers. */ + expected: number; + /** Presentation the studio computed for the tile and viewer. */ + view: { ratio: string; meta: string; badge?: string }; +}; + +export type SubmitResult = { + requestId: string; + credits: number; + mode: KeyMode; + runs: RunRecord[]; +}; + +/** Submit one generation. The credit debit happens before the platform is + asked and is refunded if the ask itself fails; a run the platform accepts + and later fails is refunded when its status settles. */ +export async function submitGeneration(input: SubmitInput): Promise { + const viewer = await requireViewer(); + const model = getModel(input.plane.model); + const parsed: GenerationPlane = { ...input.plane, settings: parseSettings(model, input.plane.settings) }; + if (!parsed.prompt.text.trim()) throw new Error("Write a prompt first"); + + const credentials = resolveCredentials(viewer); + const credits = credentials.mode === "byok" ? 0 : creditsFor(model, parsed.settings); + const reservationKey = `reserve:${crypto.randomUUID()}`; + if (credits > 0) await reserveCredits(viewer.workspace.id, credits, reservationKey); + + let queued; + try { + const { path, body } = toPlatform(parsed); + queued = await createPlatformClient(credentials).submit(path, body); + } catch (caught) { + if (credits > 0) await refundCredits(viewer.workspace.id, credits, reservationKey, "submit failed"); + throw caught; + } + + const runs = await insertRunningRows(viewer.workspace.id, viewer.user.id, queued.requestId, input.expected, { + ...input.context, + surface: model.surface, + modelId: model.id, + modelLabel: model.label, + prompt: parsed.prompt.text.trim(), + ratio: input.view.ratio, + meta: input.view.meta, + badge: input.view.badge, + settings: parsed.settings, + credits, + }); + if (credits > 0) { + /* Re-key the ledger entry to the platform's request id so a later refund + can find it by the same handle the status poll carries. */ + await db + .update(schema.creditLedger) + .set({ requestId: queued.requestId }) + .where(eq(schema.creditLedger.requestId, reservationKey)); + } + return { requestId: queued.requestId, credits, mode: credentials.mode, runs }; +} + +/* ---------- poll ---------- */ + +export type StatusResult = + | { requestId: string; status: GenerationStatus; runs: RunRecord[] | null } + | { requestId: string; error: string; runs: RunRecord[] | null }; + +const TERMINAL = new Set(["completed", "failed", "nsfw", "canceled"]); + +/** Every request in flight, answered in one round trip. Terminal answers are + written to the run log here — the server, not the browser, settles a run, + so a tab that closes mid-poll loses nothing and the refund cannot be + skipped by a client that never came back. */ +export async function getGenerationStatuses(data: unknown): Promise { + const viewer = await requireViewer(); + const requestIds = parseRequestIds(data); + const owned = await runningRequestIds(viewer.workspace.id, requestIds); + const credentials = resolveCredentials(viewer); + const client = createPlatformClient(credentials); + return Promise.all( + [...owned].map(async (requestId): Promise => { + try { + const status = await client.status(requestId); + if (!TERMINAL.has(status.status)) return { requestId, status, runs: null }; + const runs = await settleRequest(viewer.workspace.id, requestId, { status }); + if (runs && runs.some((run) => run.status === "failed")) { + await refundForRequest(viewer.workspace.id, requestId, runs[0]?.error); + } + return { requestId, status, runs }; + } catch (caught) { + const error = caught instanceof Error ? caught.message : String(caught); + return { requestId, error, runs: null }; + } + }), + ); +} + +/** The studio gave up on a request (deadline, or the poll itself kept + failing). Record the failure so the tiles do not stay open forever, and + give the credits back. */ +export async function abandonRequest(data: unknown): Promise { + const viewer = await requireViewer(); + const payload = asObject(data, "Invalid payload"); + const requestId = typeof payload.requestId === "string" ? payload.requestId : ""; + const reason = typeof payload.reason === "string" && payload.reason ? payload.reason : "the run did not finish"; + if (!requestId) throw new Error("Invalid request id"); + const runs = await settleRequest(viewer.workspace.id, requestId, { error: reason }); + if (runs) await refundForRequest(viewer.workspace.id, requestId, reason); + return runs; +} + +async function refundForRequest(workspaceId: string, requestId: string, note?: string): Promise { + const credits = await creditsOfRequest(workspaceId, requestId); + if (credits > 0) await refundCredits(workspaceId, credits, requestId, note); +} + +export { InsufficientCreditsError }; + +function parseRequestIds(data: unknown): string[] { + const payload = asObject(data, "Invalid status payload"); + const requestIds = payload.requestIds; + if (!Array.isArray(requestIds) || requestIds.length === 0) { + throw new Error("Invalid request ids"); + } + return requestIds.map((requestId) => { + if (typeof requestId !== "string" || !requestId) throw new Error("Invalid request id"); + return requestId; + }); +} + +function asObject(data: unknown, message: string): Record { + if (data === null || typeof data !== "object" || Array.isArray(data)) throw new Error(message); + return data as Record; +} diff --git a/src/generation/catalog/defaults.ts b/src/generation/catalog/defaults.ts new file mode 100644 index 0000000..ea89500 --- /dev/null +++ b/src/generation/catalog/defaults.ts @@ -0,0 +1,43 @@ +import type { MediaRole, ModelEntry, PlatformPaths } from "./types"; + +const IMAGE_ASPECT = ["auto", "1:1", "4:3", "3:4", "16:9", "9:16"] as const; +const VIDEO_ASPECT = ["16:9", "9:16", "1:1"] as const; + +export function t2v(path: string): PlatformPaths { + if (!path.endsWith("/text-to-video")) return { text: path }; + return { text: path, image: path.replace(/\/text-to-video$/, "/image-to-video") }; +} + +export function imageModel(id: string, label: string, paths: PlatformPaths): ModelEntry { + return { + id, + surface: "image", + label, + roles: { reference: 8 }, + settings: { + aspectRatio: { type: "enum", values: IMAGE_ASPECT, default: "1:1" }, + resolution: { type: "enum", values: ["1k", "2k", "4k"], default: "1k" }, + }, + paths, + }; +} + +export function videoModel( + id: string, + label: string, + roles: Partial>, + paths: PlatformPaths, +): ModelEntry { + return { + id, + surface: "video", + label, + roles, + settings: { + aspectRatio: { type: "enum", values: VIDEO_ASPECT, default: "16:9" }, + resolution: { type: "enum", values: ["720p", "1080p"], default: "720p" }, + duration: { type: "range", min: 4, max: 10, default: 5 }, + }, + paths, + }; +} diff --git a/src/generation/catalog/dop.ts b/src/generation/catalog/dop.ts new file mode 100644 index 0000000..b224b89 --- /dev/null +++ b/src/generation/catalog/dop.ts @@ -0,0 +1,3 @@ +import { videoModel } from "./defaults"; + +export const dop = videoModel("dop", "DoP", { start: 1 }, { image: "higgsfield-ai/dop/lite" }); diff --git a/src/generation/catalog/flux-2.ts b/src/generation/catalog/flux-2.ts new file mode 100644 index 0000000..c87f024 --- /dev/null +++ b/src/generation/catalog/flux-2.ts @@ -0,0 +1,3 @@ +import { imageModel } from "./defaults"; + +export const flux2 = imageModel("flux-2", "Flux 2", { text: "flux-2-pro" }); diff --git a/src/generation/catalog/flux-3.ts b/src/generation/catalog/flux-3.ts new file mode 100644 index 0000000..ea3d8af --- /dev/null +++ b/src/generation/catalog/flux-3.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const flux3 = videoModel( + "flux-3", + "Flux 3", + { start: 1 }, + t2v("blackforestlabs/flux-3/text-to-video"), +); diff --git a/src/generation/catalog/grok-imagine-2.ts b/src/generation/catalog/grok-imagine-2.ts new file mode 100644 index 0000000..7c8d1ac --- /dev/null +++ b/src/generation/catalog/grok-imagine-2.ts @@ -0,0 +1,5 @@ +import { imageModel } from "./defaults"; + +export const grokImagine2 = imageModel("grok-imagine-2", "Grok Imagine 2.0", { + text: "xai/grok-imagine-image-2.0", +}); diff --git a/src/generation/catalog/grok-imagine-video-1.5.ts b/src/generation/catalog/grok-imagine-video-1.5.ts new file mode 100644 index 0000000..1388698 --- /dev/null +++ b/src/generation/catalog/grok-imagine-video-1.5.ts @@ -0,0 +1,8 @@ +import { videoModel } from "./defaults"; + +export const grokImagineVideo15 = videoModel( + "grok-imagine-video-1.5", + "Grok Imagine Video 1.5", + { reference: 8, video: 3 }, + { reference: "xai/grok-imagine-video/v1.5/reference-to-video" }, +); diff --git a/src/generation/catalog/happy-horse-1.1.ts b/src/generation/catalog/happy-horse-1.1.ts new file mode 100644 index 0000000..f5ee1c5 --- /dev/null +++ b/src/generation/catalog/happy-horse-1.1.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const happyHorse11 = videoModel( + "happy-horse-1.1", + "Happy Horse 1.1", + { start: 1 }, + t2v("alibaba/happy-horse/v1.1/text-to-video"), +); diff --git a/src/generation/catalog/happy-horse-1.ts b/src/generation/catalog/happy-horse-1.ts new file mode 100644 index 0000000..23138fc --- /dev/null +++ b/src/generation/catalog/happy-horse-1.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const happyHorse1 = videoModel( + "happy-horse-1", + "Happy Horse 1.0", + { start: 1 }, + t2v("alibaba/happy-horse/text-to-video"), +); diff --git a/src/generation/catalog/ideogram-4.ts b/src/generation/catalog/ideogram-4.ts new file mode 100644 index 0000000..e28b189 --- /dev/null +++ b/src/generation/catalog/ideogram-4.ts @@ -0,0 +1,5 @@ +import { imageModel } from "./defaults"; + +export const ideogram4 = imageModel("ideogram-4", "Ideogram 4.0", { + text: "ideogram/v4.0", +}); diff --git a/src/generation/catalog/index.ts b/src/generation/catalog/index.ts new file mode 100644 index 0000000..1d7db73 --- /dev/null +++ b/src/generation/catalog/index.ts @@ -0,0 +1,87 @@ +import { dop } from "./dop"; +import { flux2 } from "./flux-2"; +import { flux3 } from "./flux-3"; +import { grokImagine2 } from "./grok-imagine-2"; +import { grokImagineVideo15 } from "./grok-imagine-video-1.5"; +import { happyHorse1 } from "./happy-horse-1"; +import { happyHorse11 } from "./happy-horse-1.1"; +import { ideogram4 } from "./ideogram-4"; +import { kling25 } from "./kling-2.5"; +import { kling26 } from "./kling-2.6"; +import { + kling34k, + kling3MotionPro, + kling3MotionStd, + kling3Pro, + kling3Std, + kling3Turbo, +} from "./kling-3"; +import { klingO1 } from "./kling-o1"; +import { klingO3 } from "./kling-o3"; +import { ltx25Fast } from "./ltx-2.5-fast"; +import { ltx25Pro } from "./ltx-2.5-pro"; +import { minimaxH3 } from "./minimax-h3"; +import { minimaxHailuo23 } from "./minimax-hailuo-2.3"; +import { parseSettings } from "./parse-settings"; +import { pixverse6 } from "./pixverse-6"; +import { qwenImage3 } from "./qwen-image-3"; +import { recraft41 } from "./recraft-4.1"; +import { seedance2, seedance2Fast, seedance2Mini } from "./seedance-2"; +import { seedance25, seedance25Edit, seedance25Extend } from "./seedance-2.5"; +import { soul2, soulCinema } from "./soul"; +import type { ModelEntry } from "./types"; +import { wan26 } from "./wan-2.6"; +import { wan27 } from "./wan-2.7"; +import { wan3 } from "./wan-3"; +import { wan3Prime } from "./wan-3-prime"; +import { zImageTurbo } from "./z-image-turbo"; + +export const MODELS: readonly ModelEntry[] = [ + soul2, + soulCinema, + seedance25, + seedance25Edit, + seedance25Extend, + seedance2, + seedance2Fast, + seedance2Mini, + kling3Turbo, + kling3Std, + kling3Pro, + kling34k, + kling3MotionStd, + kling3MotionPro, + flux2, + grokImagine2, + ideogram4, + recraft41, + qwenImage3, + zImageTurbo, + wan3, + wan3Prime, + wan27, + wan26, + flux3, + minimaxH3, + minimaxHailuo23, + happyHorse1, + happyHorse11, + kling26, + kling25, + klingO3, + klingO1, + ltx25Fast, + ltx25Pro, + grokImagineVideo15, + pixverse6, + dop, +]; + +export function getModel(id: string): ModelEntry { + const model = MODELS.find((entry) => entry.id === id); + if (!model) throw new Error(`Unknown model: ${id}`); + return model; +} + +export type { GenerationPlane, MediaItem, MediaRole, ModelEntry, PlatformPaths, Surface } from "./types"; +export { parseSettings }; diff --git a/src/generation/catalog/kling-2.5.ts b/src/generation/catalog/kling-2.5.ts new file mode 100644 index 0000000..e35925d --- /dev/null +++ b/src/generation/catalog/kling-2.5.ts @@ -0,0 +1,5 @@ +import { videoModel } from "./defaults"; + +export const kling25 = videoModel("kling-2.5", "Kling 2.5", { start: 1 }, { + image: "kling-video/v2.5-turbo/standard/image-to-video", +}); diff --git a/src/generation/catalog/kling-2.6.ts b/src/generation/catalog/kling-2.6.ts new file mode 100644 index 0000000..9524a4f --- /dev/null +++ b/src/generation/catalog/kling-2.6.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const kling26 = videoModel( + "kling-2.6", + "Kling 2.6", + { start: 1 }, + t2v("kling-video/v2.6/pro/text-to-video"), +); diff --git a/src/generation/catalog/kling-3.ts b/src/generation/catalog/kling-3.ts new file mode 100644 index 0000000..e9d995a --- /dev/null +++ b/src/generation/catalog/kling-3.ts @@ -0,0 +1,68 @@ +import type { ModelEntry } from "./types"; + +const klingTurboSettings = { + aspectRatio: { type: "enum", values: ["16:9", "9:16", "1:1"], default: "16:9" }, + resolution: { type: "enum", values: ["720p", "1080p"], default: "720p" }, + duration: { type: "range", min: 3, max: 15, default: 5 }, +} as const satisfies ModelEntry["settings"]; + +const kling3Settings = { + aspectRatio: { type: "enum", values: ["16:9", "9:16", "1:1"], default: "16:9" }, + duration: { type: "range", min: 3, max: 15, default: 5 }, + sound: { type: "boolean", default: true }, + cfgScale: { type: "range", min: 0, max: 1, default: 0.5, step: 0.01 }, + multiShots: { type: "boolean", default: false }, +} as const satisfies ModelEntry["settings"]; + +const klingMotionSettings = { + keepOriginalSound: { type: "boolean", default: true }, + characterOrientation: { type: "enum", values: ["video", "image"], default: "video" }, +} as const satisfies ModelEntry["settings"]; + +export const kling3Turbo: ModelEntry = { + id: "kling-3-turbo", + surface: "video", + label: "Kling 3.0 Turbo", + roles: { start: 1 }, + settings: klingTurboSettings, +}; + +export const kling3Std: ModelEntry = { + id: "kling-3-std", + surface: "video", + label: "Kling 3.0 Standard", + roles: { start: 1, end: 1 }, + settings: kling3Settings, +}; + +export const kling3Pro: ModelEntry = { + id: "kling-3-pro", + surface: "video", + label: "Kling 3.0 Pro", + roles: { start: 1, end: 1 }, + settings: kling3Settings, +}; + +export const kling34k: ModelEntry = { + id: "kling-3-4k", + surface: "video", + label: "Kling 3.0 4K", + roles: { start: 1, end: 1 }, + settings: kling3Settings, +}; + +export const kling3MotionStd: ModelEntry = { + id: "kling-3-motion-std", + surface: "video", + label: "Kling 3.0 Motion Control", + roles: { start: 1, video: 1 }, + settings: klingMotionSettings, +}; + +export const kling3MotionPro: ModelEntry = { + id: "kling-3-motion-pro", + surface: "video", + label: "Kling 3.0 Motion Control Pro", + roles: { start: 1, video: 1 }, + settings: klingMotionSettings, +}; \ No newline at end of file diff --git a/src/generation/catalog/kling-o1.ts b/src/generation/catalog/kling-o1.ts new file mode 100644 index 0000000..ef5cd40 --- /dev/null +++ b/src/generation/catalog/kling-o1.ts @@ -0,0 +1,5 @@ +import { videoModel } from "./defaults"; + +export const klingO1 = videoModel("kling-o1", "Kling O1 (Omni)", { start: 1, end: 1 }, { + firstLast: "kling-video/omni/first-last-frame", +}); diff --git a/src/generation/catalog/kling-o3.ts b/src/generation/catalog/kling-o3.ts new file mode 100644 index 0000000..006df63 --- /dev/null +++ b/src/generation/catalog/kling-o3.ts @@ -0,0 +1,5 @@ +import { videoModel } from "./defaults"; + +export const klingO3 = videoModel("kling-o3", "Kling O3", { start: 1, end: 1 }, { + firstLast: "kling-video/o3/first-last-frame", +}); diff --git a/src/generation/catalog/ltx-2.5-fast.ts b/src/generation/catalog/ltx-2.5-fast.ts new file mode 100644 index 0000000..9b19ba7 --- /dev/null +++ b/src/generation/catalog/ltx-2.5-fast.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const ltx25Fast = videoModel( + "ltx-2.5-fast", + "LTX 2.5 Fast", + { start: 1 }, + t2v("lightricks/ltx-2.5/text-to-video/fast"), +); diff --git a/src/generation/catalog/ltx-2.5-pro.ts b/src/generation/catalog/ltx-2.5-pro.ts new file mode 100644 index 0000000..5a15521 --- /dev/null +++ b/src/generation/catalog/ltx-2.5-pro.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const ltx25Pro = videoModel( + "ltx-2.5-pro", + "LTX 2.5 Pro", + { start: 1 }, + t2v("lightricks/ltx-2.5/text-to-video/pro"), +); diff --git a/src/generation/catalog/minimax-h3.ts b/src/generation/catalog/minimax-h3.ts new file mode 100644 index 0000000..760e6bd --- /dev/null +++ b/src/generation/catalog/minimax-h3.ts @@ -0,0 +1,3 @@ +import { t2v, videoModel } from "./defaults"; + +export const minimaxH3 = videoModel("minimax-h3", "MiniMax H3", { start: 1 }, t2v("minimax/h3/text-to-video")); diff --git a/src/generation/catalog/minimax-hailuo-2.3.ts b/src/generation/catalog/minimax-hailuo-2.3.ts new file mode 100644 index 0000000..4102953 --- /dev/null +++ b/src/generation/catalog/minimax-hailuo-2.3.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const minimaxHailuo23 = videoModel( + "minimax-hailuo-2.3", + "MiniMax Hailuo 2.3", + { start: 1 }, + t2v("minimax/hailuo-2.3/standard/text-to-video"), +); diff --git a/src/generation/catalog/parse-settings.ts b/src/generation/catalog/parse-settings.ts new file mode 100644 index 0000000..8f86060 --- /dev/null +++ b/src/generation/catalog/parse-settings.ts @@ -0,0 +1,25 @@ +import type { ModelEntry } from "./types"; + +export function parseSettings( + model: ModelEntry, + raw: Record, +): Record { + const out: Record = {}; + for (const [key, field] of Object.entries(model.settings)) { + const value = raw[key]; + if (field.type === "enum") { + const picked = typeof value === "string" ? value : field.default; + if (!field.values.includes(picked)) throw new Error(`Invalid ${key}`); + out[key] = picked; + continue; + } + if (field.type === "range") { + const picked = typeof value === "number" ? value : field.default; + if (picked < field.min || picked > field.max) throw new Error(`Invalid ${key}`); + out[key] = picked; + continue; + } + out[key] = typeof value === "boolean" ? value : field.default; + } + return out; +} diff --git a/src/generation/catalog/pixverse-6.ts b/src/generation/catalog/pixverse-6.ts new file mode 100644 index 0000000..e5aff7b --- /dev/null +++ b/src/generation/catalog/pixverse-6.ts @@ -0,0 +1,3 @@ +import { t2v, videoModel } from "./defaults"; + +export const pixverse6 = videoModel("pixverse-6", "PixVerse 6", { start: 1 }, t2v("pixverse/v6/text-to-video")); diff --git a/src/generation/catalog/qwen-image-3.ts b/src/generation/catalog/qwen-image-3.ts new file mode 100644 index 0000000..dee65a5 --- /dev/null +++ b/src/generation/catalog/qwen-image-3.ts @@ -0,0 +1,5 @@ +import { imageModel } from "./defaults"; + +export const qwenImage3 = imageModel("qwen-image-3", "Qwen Image 3", { + text: "alibaba/qwen-image-3/text-to-image", +}); diff --git a/src/generation/catalog/recraft-4.1.ts b/src/generation/catalog/recraft-4.1.ts new file mode 100644 index 0000000..4824475 --- /dev/null +++ b/src/generation/catalog/recraft-4.1.ts @@ -0,0 +1,5 @@ +import { imageModel } from "./defaults"; + +export const recraft41 = imageModel("recraft-4.1", "Recraft 4.1", { + text: "recraft/v4.1/text-to-image", +}); diff --git a/src/generation/catalog/seedance-2.5.ts b/src/generation/catalog/seedance-2.5.ts new file mode 100644 index 0000000..d9c46a2 --- /dev/null +++ b/src/generation/catalog/seedance-2.5.ts @@ -0,0 +1,39 @@ +import type { ModelEntry } from "./types"; +import { SEEDANCE_ASPECT } from "./tokens"; + +const seedance25Settings = { + resolution: { type: "enum", values: ["480p", "720p"], default: "720p" }, + generateAudio: { type: "boolean", default: true }, + outputFormat: { type: "enum", values: ["mp4", "mov"], default: "mp4" }, +} as const satisfies ModelEntry["settings"]; + +export const seedance25: ModelEntry = { + id: "seedance-2.5", + surface: "video", + label: "Seedance 2.5", + roles: { start: 1, end: 1, reference: 30, video: 10, audio: 10 }, + settings: { + aspectRatio: { type: "enum", values: SEEDANCE_ASPECT, default: "16:9" }, + duration: { type: "range", min: 4, max: 30, default: 5 }, + ...seedance25Settings, + }, +}; + +export const seedance25Edit: ModelEntry = { + id: "seedance-2.5-edit", + surface: "video", + label: "Seedance 2.5 Edit", + roles: { video: 1, reference: 30, audio: 10 }, + settings: seedance25Settings, +}; + +export const seedance25Extend: ModelEntry = { + id: "seedance-2.5-extend", + surface: "video", + label: "Seedance 2.5 Extend", + roles: { video: 1, reference: 30, audio: 10 }, + settings: { + duration: { type: "range", min: 4, max: 30, default: 5 }, + ...seedance25Settings, + }, +}; diff --git a/src/generation/catalog/seedance-2.ts b/src/generation/catalog/seedance-2.ts new file mode 100644 index 0000000..05bb5c8 --- /dev/null +++ b/src/generation/catalog/seedance-2.ts @@ -0,0 +1,43 @@ +import type { ModelEntry } from "./types"; +import { SEEDANCE_ASPECT } from "./tokens"; + +const seedanceRoles = { start: 1, end: 1, reference: 9, video: 3, audio: 3 } as const; + +const seedanceSettings = { + aspectRatio: { type: "enum", values: SEEDANCE_ASPECT, default: "16:9" }, + duration: { type: "range", min: 4, max: 15, default: 5 }, + generateAudio: { type: "boolean", default: true }, +} as const satisfies ModelEntry["settings"]; + +export const seedance2: ModelEntry = { + id: "seedance-2", + surface: "video", + label: "Seedance 2.0", + roles: seedanceRoles, + settings: { + ...seedanceSettings, + resolution: { type: "enum", values: ["480p", "720p", "1080p", "4k"], default: "720p" }, + }, +}; + +export const seedance2Fast: ModelEntry = { + id: "seedance-2-fast", + surface: "video", + label: "Seedance 2.0 Fast", + roles: seedanceRoles, + settings: { + ...seedanceSettings, + resolution: { type: "enum", values: ["480p", "720p"], default: "720p" }, + }, +}; + +export const seedance2Mini: ModelEntry = { + id: "seedance-2-mini", + surface: "video", + label: "Seedance 2.0 Mini", + roles: seedanceRoles, + settings: { + ...seedanceSettings, + resolution: { type: "enum", values: ["480p", "720p"], default: "720p" }, + }, +}; diff --git a/src/generation/catalog/soul.ts b/src/generation/catalog/soul.ts new file mode 100644 index 0000000..6796713 --- /dev/null +++ b/src/generation/catalog/soul.ts @@ -0,0 +1,25 @@ +import type { ModelEntry } from "./types"; +import { SOUL_ASPECT } from "./tokens"; + +const soulSettings = { + aspectRatio: { type: "enum", values: SOUL_ASPECT, default: "1:1" }, + resolution: { type: "enum", values: ["720p", "1080p"], default: "720p" }, + batchSize: { type: "enum", values: ["1", "4"], default: "1" }, + enhancePrompt: { type: "boolean", default: false }, +} as const satisfies ModelEntry["settings"]; + +export const soulCinema: ModelEntry = { + id: "soul-cinema", + surface: "image", + label: "Soul Cinema", + roles: {}, + settings: soulSettings, +}; + +export const soul2: ModelEntry = { + id: "soul-2", + surface: "image", + label: "Soul 2", + roles: {}, + settings: soulSettings, +}; \ No newline at end of file diff --git a/src/generation/catalog/tokens.ts b/src/generation/catalog/tokens.ts new file mode 100644 index 0000000..9874809 --- /dev/null +++ b/src/generation/catalog/tokens.ts @@ -0,0 +1,3 @@ +export const SOUL_ASPECT = ["9:16", "16:9", "4:3", "3:4", "1:1", "2:3", "3:2"] as const; + +export const SEEDANCE_ASPECT = ["16:9", "4:3", "1:1", "3:4", "9:16", "21:9"] as const; \ No newline at end of file diff --git a/src/generation/catalog/types.ts b/src/generation/catalog/types.ts new file mode 100644 index 0000000..ff71339 --- /dev/null +++ b/src/generation/catalog/types.ts @@ -0,0 +1,37 @@ +export type Surface = "image" | "video"; +export type MediaRole = "start" | "end" | "reference" | "video" | "audio"; + +export type MediaItem = { + id: string; + url: string; + role: MediaRole; +}; + +export type SettingField = + | { type: "enum"; values: readonly string[]; default: string } + | { type: "range"; min: number; max: number; default: number; step?: number } + | { type: "boolean"; default: boolean }; + +export type PlatformPaths = { + text?: string; + image?: string; + firstLast?: string; + reference?: string; +}; + +export type ModelEntry = { + id: string; + surface: Surface; + label: string; + roles: Partial>; + settings: Record; + /** Submit paths when the shared mapper is enough. Soul, Kling 3, and Seedance keep custom maps. */ + paths?: PlatformPaths; +}; + +export type GenerationPlane = { + model: string; + prompt: { text: string }; + media: Partial>; + settings: Record; +}; diff --git a/src/generation/catalog/wan-2.6.ts b/src/generation/catalog/wan-2.6.ts new file mode 100644 index 0000000..ac4edd3 --- /dev/null +++ b/src/generation/catalog/wan-2.6.ts @@ -0,0 +1,3 @@ +import { t2v, videoModel } from "./defaults"; + +export const wan26 = videoModel("wan-2.6", "Wan 2.6", { start: 1 }, t2v("wan/v2.6/text-to-video")); diff --git a/src/generation/catalog/wan-2.7.ts b/src/generation/catalog/wan-2.7.ts new file mode 100644 index 0000000..c8d7028 --- /dev/null +++ b/src/generation/catalog/wan-2.7.ts @@ -0,0 +1,3 @@ +import { t2v, videoModel } from "./defaults"; + +export const wan27 = videoModel("wan-2.7", "Wan 2.7", { start: 1 }, t2v("wan/v2.7/text-to-video")); diff --git a/src/generation/catalog/wan-3-prime.ts b/src/generation/catalog/wan-3-prime.ts new file mode 100644 index 0000000..66ab94c --- /dev/null +++ b/src/generation/catalog/wan-3-prime.ts @@ -0,0 +1,8 @@ +import { t2v, videoModel } from "./defaults"; + +export const wan3Prime = videoModel( + "wan-3-prime", + "Wan 3.0 Prime", + { start: 1 }, + t2v("alibaba/wan-3.0-prime/text-to-video"), +); diff --git a/src/generation/catalog/wan-3.ts b/src/generation/catalog/wan-3.ts new file mode 100644 index 0000000..f7adf0d --- /dev/null +++ b/src/generation/catalog/wan-3.ts @@ -0,0 +1,3 @@ +import { t2v, videoModel } from "./defaults"; + +export const wan3 = videoModel("wan-3", "Wan 3.0", { start: 1 }, t2v("alibaba/wan-3.0/text-to-video")); diff --git a/src/generation/catalog/z-image-turbo.ts b/src/generation/catalog/z-image-turbo.ts new file mode 100644 index 0000000..d0c0704 --- /dev/null +++ b/src/generation/catalog/z-image-turbo.ts @@ -0,0 +1,5 @@ +import { imageModel } from "./defaults"; + +export const zImageTurbo = imageModel("z-image-turbo", "Z-Image Turbo", { + text: "z-image/turbo", +}); diff --git a/src/generation/credentials.ts b/src/generation/credentials.ts new file mode 100644 index 0000000..c5a681b --- /dev/null +++ b/src/generation/credentials.ts @@ -0,0 +1,50 @@ +export class MissingCredentialsError extends Error { + constructor() { + super("Missing platform key"); + this.name = "MissingCredentialsError"; + } +} + +/** Who pays the platform for a run. "operator" is the SaaS default — the + installation's own key, metered in credits. "byok" is a workspace that + brought its own key and is not charged credits. */ +export type KeyMode = "operator" | "byok" | "none"; + +export function parseCredentialInput(data: unknown): { apiKey: string } { + if (data === null || typeof data !== "object" || Array.isArray(data)) { + throw new Error("Enter an API key"); + } + const record = data as { apiKey?: unknown; api_key?: unknown }; + const apiKey = record.apiKey ?? record.api_key; + if (typeof apiKey !== "string" || !apiKey.trim()) throw new Error("Enter an API key"); + return { apiKey: requireIdAndSecret(apiKey.trim()) }; +} + +export function toAuthorizationHeader(apiKey: string): string { + return `Key ${requireIdAndSecret(apiKey)}`; +} + +export function requireIdAndSecret(apiKey: string): string { + const colon = apiKey.indexOf(":"); + if (colon <= 0 || colon === apiKey.length - 1) { + throw new Error("API key must be id:secret"); + } + return apiKey; +} + +/** The installation's own platform key, if the operator set one. */ +export function operatorKey(): string | null { + const key = process.env.PLATFORM_API_KEY?.trim(); + if (!key) return null; + try { + return requireIdAndSecret(key); + } catch { + return null; + } +} + +export function platformBaseUrl(): string { + const baseUrl = process.env.HF_API_BASE_URL?.trim(); + if (!baseUrl) throw new Error("Missing HF_API_BASE_URL"); + return baseUrl; +} diff --git a/src/generation/plane.ts b/src/generation/plane.ts new file mode 100644 index 0000000..e23042a --- /dev/null +++ b/src/generation/plane.ts @@ -0,0 +1,28 @@ +import { getModel, parseSettings } from "./catalog"; +import type { GenerationPlane } from "./catalog/types"; +import { useActive } from "./stores/active"; +import { useImageMedia, useVideoMedia } from "./stores/media"; +import { useImagePrompt, useVideoPrompt } from "./stores/prompt"; +import { useSettings } from "./stores/settings"; + +export function assemblePlane(): GenerationPlane { + const { model: modelId, surface } = useActive.getState(); + const model = getModel(modelId); + const text = (surface === "image" ? useImagePrompt : useVideoPrompt).getState().text; + const items = (surface === "image" ? useImageMedia : useVideoMedia).getState().items; + const media: GenerationPlane["media"] = {}; + for (const item of items) { + const max = model.roles[item.role]; + if (!max) continue; + const list = media[item.role] ?? []; + if (list.length >= max) continue; + list.push(item); + media[item.role] = list; + } + return { + model: model.id, + prompt: { text }, + media, + settings: parseSettings(model, useSettings.getState().byModel[model.id] ?? {}), + }; +} diff --git a/src/generation/platform.ts b/src/generation/platform.ts new file mode 100644 index 0000000..13523d7 --- /dev/null +++ b/src/generation/platform.ts @@ -0,0 +1,140 @@ +import { toAuthorizationHeader } from "./credentials"; + +const MODEL_ID = /^[a-z0-9][a-z0-9._/-]*$/i; + +export class PlatformError extends Error { + readonly status: number; + readonly body: unknown; + + constructor(status: number, body: unknown) { + super(messageFromBody(status, body)); + this.name = "PlatformError"; + this.status = status; + this.body = body; + } +} + +export type QueuedGeneration = { + status: string; + requestId: string; + statusUrl: string; + cancelUrl: string; +}; + +export type GenerationStatus = { + status: string; + requestId: string; + images?: Array<{ url: string }>; + video?: { url: string }; + error?: unknown; +}; + +/** One request's answer inside a batched status poll. A request that errors + carries its reason alone, so it cannot lose the answers standing beside it. */ +export type StatusResult = + | { requestId: string; status: GenerationStatus } + | { requestId: string; error: string }; + +export type PlatformClientOptions = { + apiKey: string; + baseUrl: string; + fetch?: typeof fetch; +}; + +export function isModelId(model: string): boolean { + return MODEL_ID.test(model) && !model.includes(".."); +} + +export function createPlatformClient(options: PlatformClientOptions) { + const baseUrl = options.baseUrl.replace(/\/$/, ""); + const fetchImpl = options.fetch ?? fetch; + const auth = toAuthorizationHeader(options.apiKey); + + async function send(method: "GET" | "POST", path: string, body?: Record) { + const url = `${baseUrl}${path}`; + console.info("[platform] request", { method, url, body: body ?? null }); + const response = await fetchImpl(url, { + method, + headers: { + Authorization: auth, + ...(body ? { "Content-Type": "application/json" } : {}), + }, + ...(body ? { body: JSON.stringify(body) } : {}), + }); + + const payload = await readJson(response); + console.info("[platform] response", { method, url, status: response.status, body: payload }); + if (!response.ok) throw new PlatformError(response.status, payload); + return payload; + } + + return { + async submit(model: string, input: Record): Promise { + if (!isModelId(model)) throw new PlatformError(400, { detail: "Invalid model" }); + return mapQueued(await send("POST", `/${model}`, input)); + }, + async status(requestId: string): Promise { + if (!requestId) throw new PlatformError(400, { detail: "Missing request id" }); + return mapStatus(await send("GET", `/requests/${encodeURIComponent(requestId)}/status`)); + }, + }; +} + +function mapQueued(payload: unknown): QueuedGeneration { + const data = asRecord(payload); + const requestId = stringField(data, "request_id"); + if (!requestId) throw new PlatformError(502, { detail: "Platform response missing request_id" }); + return { + status: stringField(data, "status") ?? "queued", + requestId, + statusUrl: stringField(data, "status_url") ?? "", + cancelUrl: stringField(data, "cancel_url") ?? "", + }; +} + +function mapStatus(payload: unknown): GenerationStatus { + const data = asRecord(payload); + const requestId = stringField(data, "request_id") ?? ""; + const images = Array.isArray(data.images) + ? data.images.flatMap((item) => { + const url = asRecord(item).url; + return typeof url === "string" ? [{ url }] : []; + }) + : undefined; + const videoUrl = asRecord(data.video).url; + + return { + status: stringField(data, "status") ?? "unknown", + requestId, + ...(images?.length ? { images } : {}), + ...(typeof videoUrl === "string" ? { video: { url: videoUrl } } : {}), + ...(data.error !== undefined ? { error: data.error } : {}), + }; +} + +function asRecord(value: unknown): Record { + return value !== null && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : {}; +} + +function stringField(value: Record, key: string): string | undefined { + const field = value[key]; + return typeof field === "string" ? field : undefined; +} + +async function readJson(response: Response): Promise { + const text = await response.text(); + if (!text) return null; + try { + return JSON.parse(text) as unknown; + } catch { + return text; + } +} + +function messageFromBody(status: number, body: unknown): string { + const detail = asRecord(body).detail; + if (typeof detail === "string" && detail) return detail; + return `Platform request failed (${status})`; +} diff --git a/src/generation/poll.ts b/src/generation/poll.ts new file mode 100644 index 0000000..4e8f282 --- /dev/null +++ b/src/generation/poll.ts @@ -0,0 +1,135 @@ +import { abandonRequest, getGenerationStatuses } from "./actions"; +import type { StatusResult } from "./actions"; +import type { RunRecord } from "@/server/runs"; + +/** Statuses the platform never moves off again. */ +const TERMINAL = new Set(["completed", "failed", "nsfw", "canceled"]); + +export const POLL_INTERVAL_MS = 4000; +export const POLL_DEADLINE_MS = 10 * 60_000; +/** Rounds allowed to fail back to back before the watches are given up on. One + dropped round must not end every generation in flight. */ +const MAX_MISSES = 3; + +/** What a watch resolves with: the rows the server wrote for the request, or + null when nothing was running for it any more (deleted while in flight, + or settled by another tab). */ +export type Settled = RunRecord[] | null; + +type Waiter = { + deadline: number; + resolve: (settled: Settled) => void; + reject: (reason: Error) => void; +}; + +const waiting = new Map(); +const inflight = new Map>(); +let timer: ReturnType | null = null; +let polling = false; +let misses = 0; + +/** Resolves when the platform reports a terminal status for this request and + the server has settled its rows. Every request in flight is asked for + together, in one server action per interval: Next dispatches server + actions one at a time per client, so a poll per run would queue ahead of + the next submit and the composer would stall. */ +export function watchRequest(requestId: string, opts?: { deadline?: number }): Promise { + const existing = inflight.get(requestId); + if (existing) return existing; + const promise = new Promise((resolve, reject) => { + waiting.set(requestId, { + deadline: opts?.deadline ?? Date.now() + POLL_DEADLINE_MS, + resolve: (settled) => { + inflight.delete(requestId); + resolve(settled); + }, + reject: (reason) => { + inflight.delete(requestId); + reject(reason); + }, + }); + schedule(); + }); + inflight.set(requestId, promise); + return promise; +} + +/** Drops every watch without settling it: the studio unmounted and there is + nobody left to hand a result to. In-flight jobs stay in the run log and + the next mount starts a fresh watch. */ +export function stopWatching(): void { + if (timer !== null) clearTimeout(timer); + timer = null; + misses = 0; + waiting.clear(); + inflight.clear(); +} + +function schedule(): void { + if (timer !== null || polling || waiting.size === 0) return; + timer = setTimeout(() => void round(), POLL_INTERVAL_MS); +} + +async function round(): Promise { + timer = null; + polling = true; + try { + const ids = [...waiting.keys()]; + const results = await getGenerationStatuses({ requestIds: ids }); + misses = 0; + const answered = new Set(results.map((result) => result.requestId)); + for (const result of results) deliver(result); + /* A request the server no longer holds as running has been settled or + deleted elsewhere; there is nothing left to wait for. */ + for (const id of ids) { + if (answered.has(id)) continue; + const waiter = waiting.get(id); + if (!waiter) continue; + waiting.delete(id); + waiter.resolve(null); + } + await sweep(); + } catch (caught) { + if (++misses < MAX_MISSES) return; + settleAll(caught instanceof Error ? caught : new Error(String(caught))); + } finally { + polling = false; + schedule(); + } +} + +function deliver(result: StatusResult): void { + const waiter = waiting.get(result.requestId); + if (!waiter) return; + if ("error" in result) { + /* One request's poll failing is not the run failing: the platform may be + slow on that id alone. Keep waiting until the deadline. */ + return; + } + if (!TERMINAL.has(result.status.status)) return; + waiting.delete(result.requestId); + waiter.resolve(result.runs); +} + +/* A run the platform never finishes would otherwise hold its skeleton open for + the rest of the session — and its credits. The server records the timeout + and refunds. */ +async function sweep(): Promise { + const now = Date.now(); + for (const [requestId, waiter] of [...waiting]) { + if (now <= waiter.deadline) continue; + waiting.delete(requestId); + try { + waiter.resolve(await abandonRequest({ requestId, reason: "timed out waiting for the platform" })); + } catch (caught) { + waiter.reject(caught instanceof Error ? caught : new Error(String(caught))); + } + } +} + +function settleAll(reason: Error): void { + const waiters = [...waiting.values()]; + waiting.clear(); + misses = 0; + for (const waiter of waiters) waiter.reject(reason); +} diff --git a/src/generation/stores/active.ts b/src/generation/stores/active.ts new file mode 100644 index 0000000..125ce32 --- /dev/null +++ b/src/generation/stores/active.ts @@ -0,0 +1,57 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +import { getModel } from "../catalog"; +import type { Surface } from "../catalog/types"; +import { browserStorage } from "./browser-storage"; + +/** Results one press of Generate produces. Models that do not carry a count of + their own are submitted once per result — every unit is a real platform + request — so the ceiling is deliberately small. */ +export const MAX_BATCH = 4; + +type ActiveState = { + surface: Surface; + model: string; + batch: number; + setModel: (id: string) => void; + setBatch: (count: number) => void; +}; + +export const useActive = create()( + persist( + (set) => ({ + /* A product studio opens on images: the pack shot is the job asked for + most, and the video presets are one tab away. */ + surface: "image", + model: "flux-2", + batch: 1, + setModel: (id) => { + const model = getModel(id); + set((state) => + state.model === model.id && state.surface === model.surface + ? state + : { model: model.id, surface: model.surface }, + ); + }, + setBatch: (count) => + set((state) => { + const batch = Math.min(MAX_BATCH, Math.max(1, Math.round(count))); + return state.batch === batch ? state : { batch }; + }), + }), + { + name: "vitrina.active.v3", + storage: browserStorage(), + partialize: (state) => ({ surface: state.surface, model: state.model, batch: state.batch }), + onRehydrateStorage: () => (state) => { + if (!state) return; + try { + getModel(state.model); + } catch { + state.setModel("flux-2"); + } + }, + }, + ), +); diff --git a/src/generation/stores/browser-storage.ts b/src/generation/stores/browser-storage.ts new file mode 100644 index 0000000..85e7d8d --- /dev/null +++ b/src/generation/stores/browser-storage.ts @@ -0,0 +1,42 @@ +import { type PersistStorage, type StorageValue } from "zustand/middleware"; + +/** Per-call localStorage so persist still works after SSR, where the store + module evaluates before a Storage exists. */ +export function browserStorage(): PersistStorage { + return { + getItem: (name) => { + const raw = read(name); + if (raw === null) return null; + try { + return JSON.parse(raw) as StorageValue; + } catch { + return null; + } + }, + setItem: (name, value) => { + write(name, JSON.stringify(value)); + }, + removeItem: (name) => { + write(name, null); + }, + }; +} + +function read(name: string): string | null { + try { + if (typeof localStorage === "undefined") return null; + return localStorage.getItem(name); + } catch { + return null; + } +} + +function write(name: string, value: string | null): void { + try { + if (typeof localStorage === "undefined") return; + if (value === null) localStorage.removeItem(name); + else localStorage.setItem(name, value); + } catch { + /* quota, private mode, or a denied store */ + } +} diff --git a/src/generation/stores/media.ts b/src/generation/stores/media.ts new file mode 100644 index 0000000..738167d --- /dev/null +++ b/src/generation/stores/media.ts @@ -0,0 +1,33 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +import type { MediaItem } from "../catalog/types"; +import { browserStorage } from "./browser-storage"; + +type MediaState = { + items: MediaItem[]; + add: (item: MediaItem) => void; + remove: (id: string) => void; +}; + +function createMediaStore(name: string) { + return create()( + persist( + (set) => ({ + items: [], + add: (item) => set((state) => ({ items: [...state.items, item] })), + remove: (id) => set((state) => ({ items: state.items.filter((item) => item.id !== id) })), + }), + { + name, + storage: browserStorage(), + partialize: (state) => ({ + items: state.items.filter((item) => !item.url.startsWith("blob:")), + }), + }, + ), + ); +} + +export const useImageMedia = createMediaStore("vitrina.imageMedia.v1"); +export const useVideoMedia = createMediaStore("vitrina.videoMedia.v1"); diff --git a/src/generation/stores/prompt.ts b/src/generation/stores/prompt.ts new file mode 100644 index 0000000..1182944 --- /dev/null +++ b/src/generation/stores/prompt.ts @@ -0,0 +1,24 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +import { browserStorage } from "./browser-storage"; + +type PromptState = { + text: string; + setText: (text: string) => void; +}; + +function createPromptStore(name: string) { + return create()( + persist( + (set) => ({ + text: "", + setText: (text) => set((state) => (state.text === text ? state : { text })), + }), + { name, storage: browserStorage(), partialize: (state) => ({ text: state.text }) }, + ), + ); +} + +export const useImagePrompt = createPromptStore("vitrina.imagePrompt.v1"); +export const useVideoPrompt = createPromptStore("vitrina.videoPrompt.v1"); diff --git a/src/generation/stores/settings.ts b/src/generation/stores/settings.ts new file mode 100644 index 0000000..1ca36d2 --- /dev/null +++ b/src/generation/stores/settings.ts @@ -0,0 +1,31 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +import { browserStorage } from "./browser-storage"; + +type SettingsState = { + byModel: Record>; + set: (modelId: string, patch: Record) => void; +}; + +export const useSettings = create()( + persist( + (set) => ({ + byModel: {}, + set: (modelId, patch) => + set((state) => { + const current = state.byModel[modelId]; + if (current && Object.entries(patch).every(([key, value]) => Object.is(current[key], value))) { + return state; + } + return { + byModel: { + ...state.byModel, + [modelId]: { ...current, ...patch }, + }, + }; + }), + }), + { name: "vitrina.settings.v1", storage: browserStorage(), partialize: (state) => ({ byModel: state.byModel }) }, + ), +); diff --git a/src/generation/to-platform.ts b/src/generation/to-platform.ts new file mode 100644 index 0000000..111edc4 --- /dev/null +++ b/src/generation/to-platform.ts @@ -0,0 +1,201 @@ +import { getModel } from "./catalog"; +import type { GenerationPlane, PlatformPaths } from "./catalog/types"; + +type Mapped = { path: string; body: Record }; +type Mapper = (plane: GenerationPlane) => Mapped; + +const MAP: Record = { + "soul-cinema": (plane) => mapSoul(plane, "higgsfield-ai/soul/cinema"), + "soul-2": (plane) => mapSoul(plane, "higgsfield-ai/soul/v2/standard"), + "kling-3-turbo": mapKlingTurbo, + "kling-3-std": (plane) => mapKling3(plane, "kling-video/v3.0/std"), + "kling-3-pro": (plane) => mapKling3(plane, "kling-video/v3.0/pro"), + "kling-3-4k": (plane) => mapKling3(plane, "kling-video/v3.0/4k"), + "kling-3-motion-std": (plane) => mapKlingMotion(plane, "kling-video/v3/motion-control/std"), + "kling-3-motion-pro": (plane) => mapKlingMotion(plane, "kling-video/v3/motion-control/pro"), + "seedance-2": (plane) => mapSeedance(plane, "bytedance/seedance-2.0"), + "seedance-2-fast": (plane) => mapSeedance(plane, "bytedance/seedance-2.0/fast"), + "seedance-2-mini": (plane) => mapSeedance(plane, "bytedance/seedance-2.0/mini"), + "seedance-2.5": (plane) => mapSeedance(plane, "bytedance/seedance-2.5"), + "seedance-2.5-edit": (plane) => mapSeedanceSource(plane, "bytedance/seedance-2.5/video-edit", false), + "seedance-2.5-extend": (plane) => mapSeedanceSource(plane, "bytedance/seedance-2.5/video-extend", true), +}; + +export function toPlatform(plane: GenerationPlane): Mapped { + const model = getModel(plane.model); + const map = MAP[model.id] ?? (model.paths ? (next) => mapByPaths(next, model.paths!) : undefined); + if (!map) throw new Error(`No platform map for ${plane.model}`); + return map(plane); +} + +function urls(plane: GenerationPlane, role: "start" | "end" | "reference" | "video" | "audio") { + return (plane.media[role] ?? []).map((item) => item.url); +} + +function mapSoul(plane: GenerationPlane, path: string): Mapped { + return { + path, + body: { + prompt: plane.prompt.text, + batch_size: Number(plane.settings.batchSize), + resolution: plane.settings.resolution, + aspect_ratio: plane.settings.aspectRatio, + enhance_prompt: plane.settings.enhancePrompt, + }, + }; +} + +function mapKlingTurbo(plane: GenerationPlane): Mapped { + const start = urls(plane, "start")[0]; + return { + path: start + ? "kling-video/v3.0-turbo/image-to-video" + : "kling-video/v3.0-turbo/text-to-video", + body: { + prompt: plane.prompt.text, + duration: plane.settings.duration, + resolution: plane.settings.resolution, + ...(start ? { image_url: start } : { aspect_ratio: plane.settings.aspectRatio }), + }, + }; +} + +function mapKling3(plane: GenerationPlane, prefix: string): Mapped { + const start = urls(plane, "start")[0]; + const end = urls(plane, "end")[0]; + const body: Record = { + prompt: plane.prompt.text, + sound: plane.settings.sound ? "on" : "off", + duration: plane.settings.duration, + cfg_scale: plane.settings.cfgScale, + multi_shots: plane.settings.multiShots, + }; + if (start) { + body.image_url = start; + if (end) body.last_image_url = end; + return { path: `${prefix}/image-to-video`, body }; + } + body.aspect_ratio = plane.settings.aspectRatio; + return { path: `${prefix}/text-to-video`, body }; +} + +function mapKlingMotion(plane: GenerationPlane, path: string): Mapped { + const start = urls(plane, "start")[0]; + const video = urls(plane, "video")[0]; + return { + path, + body: { + prompt: plane.prompt.text, + ...(start ? { image_url: start } : {}), + ...(video ? { video_url: video } : {}), + keep_original_sound: plane.settings.keepOriginalSound ? "yes" : "no", + character_orientation: plane.settings.characterOrientation, + }, + }; +} + +function mapByPaths(plane: GenerationPlane, spec: PlatformPaths): Mapped { + const start = urls(plane, "start")[0]; + const end = urls(plane, "end")[0]; + const refs = urls(plane, "reference"); + const videos = urls(plane, "video"); + const body: Record = { + prompt: plane.prompt.text, + ...(plane.settings.aspectRatio ? { aspect_ratio: plane.settings.aspectRatio } : {}), + ...(plane.settings.resolution ? { resolution: plane.settings.resolution } : {}), + ...(typeof plane.settings.duration === "number" ? { duration: plane.settings.duration } : {}), + }; + if (spec.firstLast && (start || end)) { + return { + path: spec.firstLast, + body: { + ...body, + ...(start ? { first_frame_url: start } : {}), + ...(end ? { last_frame_url: end } : {}), + }, + }; + } + if (spec.image && start) { + return { + path: spec.image, + body: { ...body, image_url: start, ...(end ? { last_image_url: end } : {}) }, + }; + } + if (spec.reference && (refs.length || videos.length)) { + return { + path: spec.reference, + body: { + ...body, + ...(refs.length ? { image_urls: refs } : {}), + ...(videos.length ? { video_urls: videos } : {}), + }, + }; + } + if (spec.text) { + return { + path: spec.text, + body: refs.length ? { ...body, image_urls: refs } : body, + }; + } + if (spec.image) return { path: spec.image, body }; + if (spec.reference) return { path: spec.reference, body }; + if (spec.firstLast) return { path: spec.firstLast, body }; + throw new Error("Model has no platform path"); +} + +function seedanceBody(plane: GenerationPlane, withDuration: boolean) { + return { + prompt: plane.prompt.text, + resolution: plane.settings.resolution, + generate_audio: plane.settings.generateAudio, + ...(withDuration ? { duration: plane.settings.duration } : {}), + ...(plane.settings.outputFormat ? { output_format: plane.settings.outputFormat } : {}), + }; +} + +function mapSeedance(plane: GenerationPlane, prefix: string): Mapped { + const start = urls(plane, "start")[0]; + const end = urls(plane, "end")[0]; + const refs = urls(plane, "reference"); + const videos = urls(plane, "video"); + const audios = urls(plane, "audio"); + const shared = seedanceBody(plane, true); + if (start) { + return { + path: `${prefix}/image-to-video`, + body: { ...shared, image_url: start, ...(end ? { end_image_url: end } : {}) }, + }; + } + if (refs.length || videos.length || audios.length) { + return { + path: `${prefix}/reference-to-video`, + body: { + ...shared, + aspect_ratio: plane.settings.aspectRatio, + ...(refs.length ? { image_urls: refs } : {}), + ...(videos.length ? { video_urls: videos } : {}), + ...(audios.length ? { audio_urls: audios } : {}), + }, + }; + } + return { + path: `${prefix}/text-to-video`, + body: { ...shared, aspect_ratio: plane.settings.aspectRatio }, + }; +} + +function mapSeedanceSource(plane: GenerationPlane, path: string, withDuration: boolean): Mapped { + const [video, ...extraVideos] = urls(plane, "video"); + const refs = urls(plane, "reference"); + const audios = urls(plane, "audio"); + return { + path, + body: { + ...seedanceBody(plane, withDuration), + ...(video ? { video_url: video } : {}), + ...(refs.length ? { image_urls: refs } : {}), + ...(extraVideos.length ? { video_urls: extraVideos } : {}), + ...(audios.length ? { audio_urls: audios } : {}), + }, + }; +} diff --git a/src/generation/upload.ts b/src/generation/upload.ts new file mode 100644 index 0000000..bc936f7 --- /dev/null +++ b/src/generation/upload.ts @@ -0,0 +1,39 @@ +import { put } from "@vercel/blob/client"; + +/** Which store the browser sends files to. Set at build time from the same + variable the server reads, so the two never disagree. */ +export function storageDriver(): "blob" | "local" { + return process.env.NEXT_PUBLIC_STORAGE_DRIVER === "blob" ? "blob" : "local"; +} + +export async function uploadMedia(file: File): Promise<{ url: string }> { + return storageDriver() === "blob" ? uploadToBlob(file) : uploadToLocal(file); +} + +async function uploadToLocal(file: File): Promise<{ url: string }> { + const form = new FormData(); + form.set("file", file, file.name); + const res = await fetch("/api/upload", { method: "POST", body: form }); + const body = (await res.json().catch(() => ({}))) as { url?: unknown; error?: unknown }; + if (!res.ok || typeof body.url !== "string") { + throw new Error(typeof body.error === "string" ? body.error : "Upload failed"); + } + return { url: body.url }; +} + +async function uploadToBlob(file: File): Promise<{ url: string }> { + const res = await fetch("/api/blob", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + type: "blob.generate-client-token", + payload: { pathname: file.name, clientPayload: null, multipart: false }, + }), + }); + const body = (await res.json().catch(() => ({}))) as { clientToken?: unknown; pathname?: unknown; error?: unknown }; + if (!res.ok || typeof body.clientToken !== "string" || typeof body.pathname !== "string") { + throw new Error(typeof body.error === "string" ? body.error : "Failed to retrieve the client token"); + } + const blob = await put(body.pathname, file, { access: "public", token: body.clientToken }); + return { url: blob.url }; +} diff --git a/src/instrumentation.ts b/src/instrumentation.ts new file mode 100644 index 0000000..9658b76 --- /dev/null +++ b/src/instrumentation.ts @@ -0,0 +1,16 @@ +/** Runs once when the Node server starts: the database is brought up to the + current schema before the first request, so a fresh container or a + freshly cloned checkout needs no separate migrate step. */ +export async function register(): Promise { + if (process.env.NEXT_RUNTIME !== "nodejs") return; + if (process.env.NEXT_PHASE === "phase-production-build") return; + const { mkdir } = await import("node:fs/promises"); + const url = process.env.DATABASE_URL?.trim() || "file:./data/vitrina.db"; + if (url.startsWith("file:")) { + const dir = url.slice(5).replace(/\/[^/]*$/, ""); + if (dir && dir !== url.slice(5)) await mkdir(dir, { recursive: true }); + } + const { runMigrations } = await import("@/db/migrate"); + await runMigrations(); + console.info("[vitrina] database ready"); +} diff --git a/src/server/__tests__/billing.test.ts b/src/server/__tests__/billing.test.ts new file mode 100644 index 0000000..6279e12 --- /dev/null +++ b/src/server/__tests__/billing.test.ts @@ -0,0 +1,101 @@ +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import type Stripe from "stripe"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +const dir = mkdtempSync(join(tmpdir(), "vitrina-billing-")); +process.env.DATABASE_URL = `file:${join(dir, "test.db")}`; +process.env.STRIPE_PRICE_STARTER = "price_starter"; +process.env.STRIPE_PRICE_PRO = "price_pro"; + +const { db, schema, dbClient } = await import("@/db/client"); +const { runMigrations } = await import("@/db/migrate"); +const { bootstrapWorkspace } = await import("@/server/workspaces"); +const { creditBalance } = await import("@/server/credits"); +const { applyStripeEvent, planForPrice } = await import("@/server/billing"); +const { eq } = await import("drizzle-orm"); + +let workspaceId = ""; + +function event(id: string, type: string, object: Record): Stripe.Event { + return { id, type, data: { object } } as unknown as Stripe.Event; +} + +beforeAll(async () => { + await runMigrations(); + const now = new Date(); + await db.insert(schema.user).values({ id: "u1", name: "Ada", email: "ada@example.com", createdAt: now, updatedAt: now }); + workspaceId = await bootstrapWorkspace({ id: "u1", name: "Ada", email: "ada@example.com" }); +}); + +afterAll(() => { + dbClient.close(); + rmSync(dir, { recursive: true, force: true }); +}); + +async function workspace() { + const [row] = await db.select().from(schema.workspace).where(eq(schema.workspace.id, workspaceId)); + return row!; +} + +describe("applyStripeEvent", () => { + it("activates a plan on checkout and grants its credits once", async () => { + const checkout = event("evt_1", "checkout.session.completed", { + customer: "cus_1", + subscription: "sub_1", + metadata: { workspaceId, planId: "starter" }, + }); + expect(await applyStripeEvent(checkout)).toContain("activated starter"); + expect(await applyStripeEvent(checkout)).toBe("duplicate"); + const row = await workspace(); + expect(row.planId).toBe("starter"); + expect(row.stripeCustomerId).toBe("cus_1"); + expect(row.stripeSubscriptionId).toBe("sub_1"); + expect(await creditBalance(workspaceId)).toBe(30 + 500); + }); + + it("grants credits on a renewal invoice but not on the first one", async () => { + const first = event("evt_2", "invoice.paid", { customer: "cus_1", billing_reason: "subscription_create", lines: { data: [] } }); + expect(await applyStripeEvent(first)).toContain("not a renewal"); + const renewal = event("evt_3", "invoice.paid", { + customer: "cus_1", + billing_reason: "subscription_cycle", + lines: { data: [{ period: { end: 1_900_000_000 } }] }, + }); + expect(await applyStripeEvent(renewal)).toContain("renewed starter"); + expect(await creditBalance(workspaceId)).toBe(1030); + expect((await workspace()).planRenewsAt?.getTime()).toBe(1_900_000_000 * 1000); + }); + + it("follows a plan change on the subscription and downgrades on deletion", async () => { + const updated = event("evt_4", "customer.subscription.updated", { + id: "sub_1", + customer: "cus_1", + status: "active", + items: { data: [{ price: { id: "price_pro" }, current_period_end: 1_900_100_000 }] }, + }); + expect(await applyStripeEvent(updated)).toContain("updated subscription"); + expect((await workspace()).planId).toBe("pro"); + + const deleted = event("evt_5", "customer.subscription.deleted", { id: "sub_1", customer: "cus_1" }); + expect(await applyStripeEvent(deleted)).toContain("downgraded"); + const row = await workspace(); + expect(row.planId).toBe("free"); + expect(row.stripeSubscriptionId).toBeNull(); + /* Credits already granted are kept; only the renewal stops. */ + expect(await creditBalance(workspaceId)).toBe(1030); + }); + + it("ignores events for customers it does not know", async () => { + const stray = event("evt_6", "invoice.paid", { customer: "cus_nobody", billing_reason: "subscription_cycle", lines: { data: [] } }); + expect(await applyStripeEvent(stray)).toContain("unknown customer"); + }); + + it("maps configured price ids back to plans", () => { + expect(planForPrice("price_pro")).toBe("pro"); + expect(planForPrice("price_unknown")).toBeNull(); + expect(planForPrice(undefined)).toBeNull(); + }); +}); diff --git a/src/server/__tests__/credits.test.ts b/src/server/__tests__/credits.test.ts new file mode 100644 index 0000000..e42b6e8 --- /dev/null +++ b/src/server/__tests__/credits.test.ts @@ -0,0 +1,79 @@ +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +const dir = mkdtempSync(join(tmpdir(), "vitrina-test-")); +process.env.DATABASE_URL = `file:${join(dir, "test.db")}`; + +const { db, schema, dbClient } = await import("@/db/client"); +const { runMigrations } = await import("@/db/migrate"); +const { bootstrapWorkspace } = await import("@/server/workspaces"); +const { InsufficientCreditsError, creditBalance, grantCredits, refundCredits, reserveCredits } = await import( + "@/server/credits" +); +const { seal, unseal } = await import("@/server/seal"); + +let workspaceId = ""; + +beforeAll(async () => { + await runMigrations(); + const now = new Date(); + await db.insert(schema.user).values({ id: "u1", name: "Ada", email: "ada@example.com", createdAt: now, updatedAt: now }); + workspaceId = await bootstrapWorkspace({ id: "u1", name: "Ada", email: "ada@example.com" }); +}); + +afterAll(() => { + dbClient.close(); + rmSync(dir, { recursive: true, force: true }); +}); + +describe("credits ledger", () => { + it("starts with the trial grant and reserves atomically", async () => { + expect(await creditBalance(workspaceId)).toBe(30); + await reserveCredits(workspaceId, 10, "req-1"); + expect(await creditBalance(workspaceId)).toBe(20); + }); + + it("refuses a reservation the balance cannot cover and leaves the balance alone", async () => { + await expect(reserveCredits(workspaceId, 25, "req-2")).rejects.toBeInstanceOf(InsufficientCreditsError); + expect(await creditBalance(workspaceId)).toBe(20); + }); + + it("refunds a request once and only once", async () => { + expect(await refundCredits(workspaceId, 10, "req-1", "failed")).toBe(true); + expect(await refundCredits(workspaceId, 10, "req-1", "failed again")).toBe(false); + expect(await creditBalance(workspaceId)).toBe(30); + }); + + it("grants plan credits and records the ledger", async () => { + await grantCredits(workspaceId, 500, "plan_grant", { stripeEventId: "evt_1", note: "starter" }); + expect(await creditBalance(workspaceId)).toBe(530); + const rows = await db.select().from(schema.creditLedger); + const sum = rows.reduce((total, row) => total + row.delta, 0); + expect(sum).toBe(530); + }); + + it("races: only one of two concurrent reservations for the last credits wins", async () => { + await reserveCredits(workspaceId, 525, "req-drain"); + expect(await creditBalance(workspaceId)).toBe(5); + const results = await Promise.allSettled([ + reserveCredits(workspaceId, 5, "race-a"), + reserveCredits(workspaceId, 5, "race-b"), + ]); + const won = results.filter((result) => result.status === "fulfilled").length; + expect(won).toBe(1); + expect(await creditBalance(workspaceId)).toBe(0); + }); +}); + +describe("seal", () => { + it("round-trips and rejects tampering", () => { + const sealed = seal("abc:secret"); + expect(sealed.startsWith("v1.")).toBe(true); + expect(unseal(sealed)).toBe("abc:secret"); + expect(unseal(`${sealed.slice(0, -2)}xx`)).toBeNull(); + expect(unseal("garbage")).toBeNull(); + }); +}); diff --git a/src/server/__tests__/runs.test.ts b/src/server/__tests__/runs.test.ts new file mode 100644 index 0000000..af39817 --- /dev/null +++ b/src/server/__tests__/runs.test.ts @@ -0,0 +1,93 @@ +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +const dir = mkdtempSync(join(tmpdir(), "vitrina-runs-")); +process.env.DATABASE_URL = `file:${join(dir, "test.db")}`; + +const { db, schema, dbClient } = await import("@/db/client"); +const { runMigrations } = await import("@/db/migrate"); +const { bootstrapWorkspace } = await import("@/server/workspaces"); +const { creditsOfRequest, insertRunningRows, listRuns, runningRequestIds, settleRequest, updateRuns } = await import("@/server/runs"); + +let workspaceId = ""; + +const draft = { + productId: null, + campaignId: null, + presetId: "pack-shot", + channelId: "amazon_listing", + scene: "front", + surface: "image" as const, + modelId: "flux-2", + modelLabel: "Flux 2", + prompt: "a mug", + ratio: "1 / 1", + meta: "2K", + settings: { resolution: "2k" }, + credits: 2, +}; + +beforeAll(async () => { + await runMigrations(); + const now = new Date(); + await db.insert(schema.user).values({ id: "u1", name: "Ada", email: "ada@example.com", createdAt: now, updatedAt: now }); + workspaceId = await bootstrapWorkspace({ id: "u1", name: "Ada", email: "ada@example.com" }); +}); + +afterAll(() => { + dbClient.close(); + rmSync(dir, { recursive: true, force: true }); +}); + +describe("run log", () => { + it("opens one row per expected result, credits on the first only", async () => { + const rows = await insertRunningRows(workspaceId, "u1", "req-a", 2, draft); + expect(rows.map((row) => row.id)).toEqual(["req-a#0", "req-a#1"]); + expect(rows.map((row) => row.credits)).toEqual([2, 0]); + expect(await runningRequestIds(workspaceId, ["req-a", "req-zzz"])).toEqual(new Set(["req-a"])); + expect(await runningRequestIds("other-workspace", ["req-a"])).toEqual(new Set()); + }); + + it("re-cuts a completed request to one row per delivered URL, keeping the charge", async () => { + const settled = await settleRequest(workspaceId, "req-a", { + status: { status: "completed", requestId: "req-a", images: [{ url: "https://cdn/a.png" }, { url: "https://cdn/b.png" }, { url: "https://cdn/c.png" }] }, + }); + expect(settled?.map((row) => row.urls[0])).toEqual(["https://cdn/a.png", "https://cdn/b.png", "https://cdn/c.png"]); + expect(settled?.every((row) => row.status === "completed")).toBe(true); + expect(await creditsOfRequest(workspaceId, "req-a")).toBe(2); + /* Already settled: nothing running, nothing to do. */ + expect(await settleRequest(workspaceId, "req-a", { error: "late" })).toBeNull(); + }); + + it("records a failure with its reason", async () => { + await insertRunningRows(workspaceId, "u1", "req-b", 1, draft); + const settled = await settleRequest(workspaceId, "req-b", { status: { status: "nsfw", requestId: "req-b" } }); + expect(settled?.[0]?.status).toBe("failed"); + expect(settled?.[0]?.error).toMatch(/NSFW/); + const abandoned = await insertRunningRows(workspaceId, "u1", "req-c", 1, draft); + expect(abandoned).toHaveLength(1); + expect((await settleRequest(workspaceId, "req-c", { error: "timed out" }))?.[0]?.error).toBe("timed out"); + }); + + it("pages newest first and hides deleted rows", async () => { + for (let i = 0; i < 5; i++) { + await insertRunningRows(workspaceId, "u1", `req-p${i}`, 1, draft); + await new Promise((resolve) => setTimeout(resolve, 2)); + } + const first = await listRuns(workspaceId, { limit: 4 }); + expect(first.runs).toHaveLength(4); + expect(first.hasMore).toBe(true); + expect(first.runs[0]!.requestId).toBe("req-p4"); + const oldest = first.runs[first.runs.length - 1]!.createdAt; + const second = await listRuns(workspaceId, { limit: 4, before: oldest }); + expect(second.runs.every((run) => run.createdAt < oldest)).toBe(true); + expect(second.runs.length + first.runs.length).toBeLessThanOrEqual(10); + + await updateRuns(workspaceId, ["req-p4"], { deletedAt: new Date() }); + const after = await listRuns(workspaceId, { limit: 4 }); + expect(after.runs[0]!.requestId).toBe("req-p3"); + }); +}); diff --git a/src/server/__tests__/team.test.ts b/src/server/__tests__/team.test.ts new file mode 100644 index 0000000..011b1e5 --- /dev/null +++ b/src/server/__tests__/team.test.ts @@ -0,0 +1,78 @@ +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +const dir = mkdtempSync(join(tmpdir(), "vitrina-team-")); +process.env.DATABASE_URL = `file:${join(dir, "test.db")}`; + +const { db, schema, dbClient } = await import("@/db/client"); +const { runMigrations } = await import("@/db/migrate"); +const { eq } = await import("drizzle-orm"); +const { acceptInvite, bootstrapWorkspace, createInvite, InviteError, listMembers, removeMember, seatsUsed, workspaceForUser, workspacesForUser } = + await import("@/server/workspaces"); + +let workspaceId = ""; + +beforeAll(async () => { + await runMigrations(); + const now = new Date(); + for (const id of ["owner", "bob", "cara"]) { + await db.insert(schema.user).values({ id, name: id, email: `${id}@example.com`, createdAt: now, updatedAt: now }); + } + workspaceId = await bootstrapWorkspace({ id: "owner", name: "Owner", email: "owner@example.com" }); + /* Bob has his own workspace too, so joining another must not lose it. */ + await bootstrapWorkspace({ id: "bob", name: "Bob", email: "bob@example.com" }); +}); + +afterAll(() => { + dbClient.close(); + rmSync(dir, { recursive: true, force: true }); +}); + +describe("invites and seats", () => { + it("refuses unknown tokens", async () => { + await expect(acceptInvite("nope", "bob")).rejects.toBeInstanceOf(InviteError); + }); + + it("seats a member on the free plan's single seat only after an upgrade", async () => { + const invite = await createInvite(workspaceId, "owner", "editor"); + await expect(acceptInvite(invite.token, "bob")).rejects.toMatchObject({ code: "full" }); + await db.update(schema.workspace).set({ planId: "starter" }).where(eq(schema.workspace.id, workspaceId)); + expect(await acceptInvite(invite.token, "bob")).toBe(workspaceId); + expect(await seatsUsed(workspaceId)).toBe(2); + const members = await listMembers(workspaceId); + expect(members.map((member) => member.userId)).toEqual(["owner", "bob"]); + }); + + it("spends a token once", async () => { + const [row] = await db.select().from(schema.invite).where(eq(schema.invite.workspaceId, workspaceId)); + await expect(acceptInvite(row!.token, "cara")).rejects.toMatchObject({ code: "expired" }); + }); + + it("keeps a member's own workspace first and honours a preferred one", async () => { + const mine = await workspaceForUser("bob"); + expect(mine.role).toBe("owner"); + expect(mine.workspace.id).not.toBe(workspaceId); + const preferred = await workspaceForUser("bob", workspaceId); + expect(preferred.workspace.id).toBe(workspaceId); + expect(preferred.role).toBe("editor"); + expect((await workspacesForUser("bob")).length).toBe(2); + /* A preference for a workspace bob is not in falls back quietly. */ + expect((await workspaceForUser("bob", "not-a-workspace")).workspace.id).toBe(mine.workspace.id); + }); + + it("never removes the owner", async () => { + await expect(removeMember(workspaceId, "owner")).rejects.toThrow(/owner/); + await removeMember(workspaceId, "bob"); + expect(await seatsUsed(workspaceId)).toBe(1); + }); + + it("opens a second workspace without a second trial", async () => { + const id = await bootstrapWorkspace({ id: "owner", name: "Owner", email: "owner@example.com" }, { name: "Second brand", trial: false }); + const [row] = await db.select().from(schema.workspace).where(eq(schema.workspace.id, id)); + expect(row!.name).toBe("Second brand"); + expect(row!.creditBalance).toBe(0); + }); +}); diff --git a/src/server/actions/billing.ts b/src/server/actions/billing.ts new file mode 100644 index 0000000..0a65d92 --- /dev/null +++ b/src/server/actions/billing.ts @@ -0,0 +1,84 @@ +"use server"; + +import { desc, eq } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; +import { isPlanId } from "@/domain/plans"; +import { billingConfigured, ensureCustomer, priceIdFor, stripeClient } from "@/server/billing"; +import { requireViewer } from "@/server/session"; + +export type WorkspaceSummary = { + credits: number; + planId: string; + planRenewsAt: number | null; + byok: boolean; + billing: boolean; + hasSubscription: boolean; +}; + +export async function getWorkspaceSummary(): Promise { + const viewer = await requireViewer(); + return { + credits: viewer.workspace.creditBalance, + planId: viewer.workspace.planId, + planRenewsAt: viewer.workspace.planRenewsAt?.getTime() ?? null, + byok: Boolean(viewer.workspace.platformKeySealed), + billing: billingConfigured(), + hasSubscription: Boolean(viewer.workspace.stripeSubscriptionId), + }; +} + +export type LedgerEntry = { id: string; delta: number; reason: string; note: string | null; createdAt: number }; + +export async function listLedger(limit = 30): Promise { + const viewer = await requireViewer(); + const rows = await db + .select() + .from(schema.creditLedger) + .where(eq(schema.creditLedger.workspaceId, viewer.workspace.id)) + .orderBy(desc(schema.creditLedger.createdAt)) + .limit(limit); + return rows.map((row) => ({ id: row.id, delta: row.delta, reason: row.reason, note: row.note, createdAt: row.createdAt.getTime() })); +} + +function appUrl(): string { + return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/+$/, ""); +} + +/** Sends the owner to Stripe Checkout for a plan. The workspace and plan ride + in the session metadata, which is what the webhook keys on. */ +export async function startCheckout(planId: string): Promise<{ url: string }> { + const viewer = await requireViewer(); + if (viewer.role !== "owner") throw new Error("Only the workspace owner can change the plan"); + if (!isPlanId(planId) || planId === "free") throw new Error("Unknown plan"); + const stripe = stripeClient(); + if (!stripe) throw new Error("Billing is not configured on this studio"); + const price = priceIdFor(planId); + if (!price) throw new Error(`No Stripe price is configured for the ${planId} plan`); + const customer = await ensureCustomer(stripe, viewer.workspace, viewer.user.email); + const session = await stripe.checkout.sessions.create({ + mode: "subscription", + customer, + line_items: [{ price, quantity: 1 }], + success_url: `${appUrl()}/app/settings?checkout=success`, + cancel_url: `${appUrl()}/app/settings?checkout=canceled`, + metadata: { workspaceId: viewer.workspace.id, planId }, + subscription_data: { metadata: { workspaceId: viewer.workspace.id, planId } }, + allow_promotion_codes: true, + }); + if (!session.url) throw new Error("Stripe did not return a checkout URL"); + return { url: session.url }; +} + +export async function openBillingPortal(): Promise<{ url: string }> { + const viewer = await requireViewer(); + if (viewer.role !== "owner") throw new Error("Only the workspace owner can manage billing"); + const stripe = stripeClient(); + if (!stripe) throw new Error("Billing is not configured on this studio"); + if (!viewer.workspace.stripeCustomerId) throw new Error("No billing account yet — start a plan first"); + const session = await stripe.billingPortal.sessions.create({ + customer: viewer.workspace.stripeCustomerId, + return_url: `${appUrl()}/app/settings`, + }); + return { url: session.url }; +} diff --git a/src/server/actions/brand.ts b/src/server/actions/brand.ts new file mode 100644 index 0000000..408f92c --- /dev/null +++ b/src/server/actions/brand.ts @@ -0,0 +1,44 @@ +"use server"; + +import { eq } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; +import { brandKitSchema, EMPTY_BRAND, type BrandKit } from "@/domain/brand"; +import { requireViewer } from "@/server/session"; +import { flatten, type SaveResult } from "@/server/validation"; + +export async function getBrandKit(): Promise { + const viewer = await requireViewer(); + return readBrandKit(viewer.workspace.id); +} + +export async function readBrandKit(workspaceId: string): Promise { + const [row] = await db.select().from(schema.brandKit).where(eq(schema.brandKit.workspaceId, workspaceId)); + if (!row) return EMPTY_BRAND; + return { + name: row.name, + tagline: row.tagline, + tone: row.tone, + style: row.style, + palette: row.palette, + audience: row.audience, + avoid: row.avoid, + logoUrl: row.logoUrl, + }; +} + +export async function saveBrandKit(input: unknown): Promise> { + const viewer = await requireViewer(); + const parsed = brandKitSchema.safeParse(input); + if (!parsed.success) return { ok: false, errors: flatten(parsed.error) }; + const kit = parsed.data; + const now = new Date(); + await db + .insert(schema.brandKit) + .values({ workspaceId: viewer.workspace.id, ...kit, updatedAt: now }) + .onConflictDoUpdate({ target: schema.brandKit.workspaceId, set: { ...kit, updatedAt: now } }); + if (kit.name && viewer.workspace.name !== kit.name) { + await db.update(schema.workspace).set({ name: kit.name, updatedAt: now }).where(eq(schema.workspace.id, viewer.workspace.id)); + } + return { ok: true, value: kit }; +} diff --git a/src/server/actions/campaigns.ts b/src/server/actions/campaigns.ts new file mode 100644 index 0000000..bd1b47f --- /dev/null +++ b/src/server/actions/campaigns.ts @@ -0,0 +1,89 @@ +"use server"; + +import { and, desc, eq } from "drizzle-orm"; +import { z } from "zod"; + +import { db, schema } from "@/db/client"; +import { isChannelId } from "@/domain/channels"; +import { PRESETS } from "@/domain/presets"; +import { newId } from "@/server/ids"; +import { requireViewer } from "@/server/session"; + +import { flatten, type SaveResult } from "@/server/validation"; + +export type Campaign = { + id: string; + productId: string; + name: string; + scene: string; + presetIds: string[]; + channelIds: string[]; + createdAt: number; +}; + +const campaignSchema = z.object({ + productId: z.string().min(1, "Pick a product"), + name: z.string().trim().min(1, "Give the campaign a name").max(120), + scene: z.string().trim().max(400).default(""), + presetIds: z + .array(z.string()) + .min(1, "Pick at least one preset") + .refine((ids) => ids.every((id) => PRESETS.some((preset) => preset.id === id)), "Unknown preset"), + channelIds: z + .array(z.string()) + .min(1, "Pick at least one channel") + .refine((ids) => ids.every(isChannelId), "Unknown channel"), +}); + +export type CampaignInput = z.input; + +function toCampaign(row: typeof schema.campaign.$inferSelect): Campaign { + return { + id: row.id, + productId: row.productId, + name: row.name, + scene: row.scene, + presetIds: row.presetIds, + channelIds: row.channelIds, + createdAt: row.createdAt.getTime(), + }; +} + +export async function listCampaigns(): Promise { + const viewer = await requireViewer(); + const rows = await db + .select() + .from(schema.campaign) + .where(eq(schema.campaign.workspaceId, viewer.workspace.id)) + .orderBy(desc(schema.campaign.createdAt)); + return rows.map(toCampaign); +} + +export async function getCampaign(id: string): Promise { + const viewer = await requireViewer(); + const [row] = await db + .select() + .from(schema.campaign) + .where(and(eq(schema.campaign.workspaceId, viewer.workspace.id), eq(schema.campaign.id, id))); + return row ? toCampaign(row) : null; +} + +export async function createCampaign(input: unknown): Promise> { + const viewer = await requireViewer(); + const parsed = campaignSchema.safeParse(input); + if (!parsed.success) return { ok: false, errors: flatten(parsed.error) }; + const [product] = await db + .select({ id: schema.product.id }) + .from(schema.product) + .where(and(eq(schema.product.workspaceId, viewer.workspace.id), eq(schema.product.id, parsed.data.productId))); + if (!product) return { ok: false, errors: { productId: "Product not found" } }; + const now = new Date(); + const row = { id: newId(), workspaceId: viewer.workspace.id, ...parsed.data, createdAt: now, updatedAt: now }; + await db.insert(schema.campaign).values(row); + return { ok: true, value: toCampaign(row) }; +} + +export async function deleteCampaign(id: string): Promise { + const viewer = await requireViewer(); + await db.delete(schema.campaign).where(and(eq(schema.campaign.workspaceId, viewer.workspace.id), eq(schema.campaign.id, id))); +} diff --git a/src/server/actions/products.ts b/src/server/actions/products.ts new file mode 100644 index 0000000..fc6deb7 --- /dev/null +++ b/src/server/actions/products.ts @@ -0,0 +1,142 @@ +"use server"; + +import { and, asc, desc, eq, isNull } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; +import { productSchema, slugify, type Product, type ProductAsset } from "@/domain/brand"; +import { newId } from "@/server/ids"; +import { requireViewer } from "@/server/session"; + +import { flatten, type SaveResult } from "@/server/validation"; + +function toAsset(row: typeof schema.productAsset.$inferSelect): ProductAsset { + return { id: row.id, url: row.url, kind: row.kind, name: row.name }; +} + +async function assetsFor(productIds: string[]): Promise> { + const out = new Map(); + if (productIds.length === 0) return out; + const rows = await db + .select() + .from(schema.productAsset) + .orderBy(asc(schema.productAsset.position), asc(schema.productAsset.createdAt)); + for (const row of rows) { + if (!productIds.includes(row.productId)) continue; + const list = out.get(row.productId) ?? []; + list.push(toAsset(row)); + out.set(row.productId, list); + } + return out; +} + +function toProduct(row: typeof schema.product.$inferSelect, assets: ProductAsset[]): Product { + return { + id: row.id, + slug: row.slug, + name: row.name, + category: row.category as Product["category"], + description: row.description, + features: row.features, + appearance: row.appearance, + audience: row.audience, + priceLabel: row.priceLabel, + assets, + }; +} + +export async function listProducts(): Promise { + const viewer = await requireViewer(); + const rows = await db + .select() + .from(schema.product) + .where(and(eq(schema.product.workspaceId, viewer.workspace.id), isNull(schema.product.archivedAt))) + .orderBy(desc(schema.product.updatedAt)); + const assets = await assetsFor(rows.map((row) => row.id)); + return rows.map((row) => toProduct(row, assets.get(row.id) ?? [])); +} + +export async function getProduct(id: string): Promise { + const viewer = await requireViewer(); + const [row] = await db + .select() + .from(schema.product) + .where(and(eq(schema.product.workspaceId, viewer.workspace.id), eq(schema.product.id, id))); + if (!row) return null; + const assets = await assetsFor([row.id]); + return toProduct(row, assets.get(row.id) ?? []); +} + +async function uniqueSlug(workspaceId: string, name: string, exceptId?: string): Promise { + const base = slugify(name, "product"); + const rows = await db + .select({ id: schema.product.id, slug: schema.product.slug }) + .from(schema.product) + .where(eq(schema.product.workspaceId, workspaceId)); + const taken = new Set(rows.filter((row) => row.id !== exceptId).map((row) => row.slug)); + if (!taken.has(base)) return base; + for (let n = 2; ; n++) { + const candidate = `${base}-${n}`; + if (!taken.has(candidate)) return candidate; + } +} + +export async function createProduct(input: unknown): Promise> { + const viewer = await requireViewer(); + const parsed = productSchema.safeParse(input); + if (!parsed.success) return { ok: false, errors: flatten(parsed.error) }; + const now = new Date(); + const id = newId(); + const slug = await uniqueSlug(viewer.workspace.id, parsed.data.name); + await db.insert(schema.product).values({ id, workspaceId: viewer.workspace.id, slug, ...parsed.data, createdAt: now, updatedAt: now }); + return { ok: true, value: { id, slug, ...parsed.data, assets: [] } }; +} + +export async function updateProduct(id: string, input: unknown): Promise> { + const viewer = await requireViewer(); + const parsed = productSchema.safeParse(input); + if (!parsed.success) return { ok: false, errors: flatten(parsed.error) }; + const current = await getProduct(id); + if (!current) return { ok: false, errors: { form: "Product not found" } }; + const slug = current.name === parsed.data.name ? current.slug : await uniqueSlug(viewer.workspace.id, parsed.data.name, id); + await db + .update(schema.product) + .set({ ...parsed.data, slug, updatedAt: new Date() }) + .where(and(eq(schema.product.workspaceId, viewer.workspace.id), eq(schema.product.id, id))); + return { ok: true, value: { ...current, ...parsed.data, slug } }; +} + +export async function archiveProduct(id: string): Promise { + const viewer = await requireViewer(); + await db + .update(schema.product) + .set({ archivedAt: new Date(), updatedAt: new Date() }) + .where(and(eq(schema.product.workspaceId, viewer.workspace.id), eq(schema.product.id, id))); +} + +export async function addProductAsset(productId: string, input: { url: string; kind: string; name: string }): Promise { + const viewer = await requireViewer(); + const product = await getProduct(productId); + if (!product) throw new Error("Product not found"); + const url = String(input.url ?? "").trim(); + if (!/^https?:\/\//.test(url)) throw new Error("Invalid asset URL"); + if (product.assets.length >= 12) throw new Error("A product holds up to 12 photos"); + const asset = { + id: newId(), + productId, + url, + kind: input.kind === "video" ? ("video" as const) : ("image" as const), + name: String(input.name ?? "").slice(0, 180), + position: product.assets.length, + createdAt: new Date(), + }; + await db.insert(schema.productAsset).values(asset); + await db.update(schema.product).set({ updatedAt: new Date() }).where(eq(schema.product.id, productId)); + void viewer; + return toAsset(asset); +} + +export async function removeProductAsset(productId: string, assetId: string): Promise { + const product = await getProduct(productId); + if (!product) throw new Error("Product not found"); + await db.delete(schema.productAsset).where(and(eq(schema.productAsset.productId, productId), eq(schema.productAsset.id, assetId))); +} diff --git a/src/server/actions/runs.ts b/src/server/actions/runs.ts new file mode 100644 index 0000000..e760fe5 --- /dev/null +++ b/src/server/actions/runs.ts @@ -0,0 +1,32 @@ +"use server"; + +import { requireViewer } from "@/server/session"; +import { listRuns as listRunRows, runsByIds, updateRuns, type RunRecord } from "@/server/runs"; + +export async function listRuns(before?: number | null): Promise<{ runs: RunRecord[]; hasMore: boolean }> { + const viewer = await requireViewer(); + return listRunRows(viewer.workspace.id, { before: before ?? null }); +} + +export async function setFavorite(ids: string[], favorite: boolean): Promise { + const viewer = await requireViewer(); + await updateRuns(viewer.workspace.id, ids, { favorite }); +} + +export async function setReview(ids: string[], review: "pending" | "approved" | "rejected"): Promise { + const viewer = await requireViewer(); + await updateRuns(viewer.workspace.id, ids, { review }); +} + +/** Soft: the platform's result URLs are not re-derivable, and the studio + offers an undo. Hard deletion is the workspace owner's call elsewhere. */ +export async function deleteRuns(ids: string[]): Promise { + const viewer = await requireViewer(); + await updateRuns(viewer.workspace.id, ids, { deletedAt: new Date() }); +} + +export async function restoreRuns(ids: string[]): Promise { + const viewer = await requireViewer(); + await updateRuns(viewer.workspace.id, ids, { deletedAt: null }); + return runsByIds(viewer.workspace.id, ids); +} diff --git a/src/server/actions/team.ts b/src/server/actions/team.ts new file mode 100644 index 0000000..f9ca0a2 --- /dev/null +++ b/src/server/actions/team.ts @@ -0,0 +1,98 @@ +"use server"; + +import { cookies } from "next/headers"; + +import { getPlan } from "@/domain/plans"; +import { requireViewer, WORKSPACE_COOKIE, WORKSPACE_COOKIE_OPTIONS } from "@/server/session"; +import { + bootstrapWorkspace, + createInvite, + isMember, + listMembers, + listOpenInvites, + removeMember, + revokeInvite, + seatsUsed, + workspacesForUser, + type Invite, + type Member, +} from "@/server/workspaces"; + +export type TeamView = { + members: Member[]; + invites: Invite[]; + seats: number; + seatsUsed: number; + canManage: boolean; + ownerId: string; +}; + +export async function getTeam(): Promise { + const viewer = await requireViewer(); + const [members, invites, used] = await Promise.all([ + listMembers(viewer.workspace.id), + viewer.role === "owner" ? listOpenInvites(viewer.workspace.id) : Promise.resolve([]), + seatsUsed(viewer.workspace.id), + ]); + return { + members, + invites, + seats: getPlan(viewer.workspace.planId).seats, + seatsUsed: used, + canManage: viewer.role === "owner", + ownerId: viewer.workspace.ownerId, + }; +} + +async function requireOwner() { + const viewer = await requireViewer(); + if (viewer.role !== "owner") throw new Error("Only the workspace owner can manage the team"); + return viewer; +} + +export async function createInviteLink(role: "owner" | "editor" = "editor"): Promise { + const viewer = await requireOwner(); + const used = await seatsUsed(viewer.workspace.id); + if (used >= getPlan(viewer.workspace.planId).seats) { + throw new Error("Every seat on this plan is taken. Upgrade to invite more people."); + } + return createInvite(viewer.workspace.id, viewer.user.id, role); +} + +export async function revokeInviteLink(inviteId: string): Promise { + const viewer = await requireOwner(); + await revokeInvite(viewer.workspace.id, inviteId); +} + +export async function removeTeamMember(userId: string): Promise { + const viewer = await requireOwner(); + await removeMember(viewer.workspace.id, userId); +} + +export type WorkspaceOption = { id: string; name: string; role: "owner" | "editor" }; + +export async function listMyWorkspaces(): Promise { + const viewer = await requireViewer(); + const rows = await workspacesForUser(viewer.user.id); + return rows.map((row) => ({ id: row.workspace.id, name: row.workspace.name, role: row.role })); +} + +/** Makes another of the member's workspaces the one on screen. */ +export async function switchWorkspace(workspaceId: string): Promise { + const viewer = await requireViewer(); + if (!(await isMember(workspaceId, viewer.user.id))) throw new Error("Not a member of that workspace"); + const jar = await cookies(); + jar.set(WORKSPACE_COOKIE, workspaceId, WORKSPACE_COOKIE_OPTIONS); +} + +/** A second brand under the same account: its own kit, products, runs and + credits, no trial the second time around. */ +export async function createWorkspace(name: string): Promise<{ id: string }> { + const viewer = await requireViewer(); + const clean = String(name ?? "").trim(); + if (!clean) throw new Error("Give the workspace a name"); + const id = await bootstrapWorkspace(viewer.user, { name: clean.slice(0, 80), trial: false }); + const jar = await cookies(); + jar.set(WORKSPACE_COOKIE, id, WORKSPACE_COOKIE_OPTIONS); + return { id }; +} diff --git a/src/server/actions/uploads.ts b/src/server/actions/uploads.ts new file mode 100644 index 0000000..dfd7814 --- /dev/null +++ b/src/server/actions/uploads.ts @@ -0,0 +1,55 @@ +"use server"; + +import { and, desc, eq } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; +import { newId } from "@/server/ids"; +import { requireViewer } from "@/server/session"; + +export interface UploadRecord { + id: string; + url: string; + kind: "image" | "video" | "audio"; + name: string; + createdAt: number; +} + +const MAX_UPLOADS = 120; + +export async function listUploads(): Promise { + const viewer = await requireViewer(); + const rows = await db + .select() + .from(schema.upload) + .where(eq(schema.upload.workspaceId, viewer.workspace.id)) + .orderBy(desc(schema.upload.createdAt)) + .limit(MAX_UPLOADS); + return rows.map((row) => ({ id: row.id, url: row.url, kind: row.kind, name: row.name, createdAt: row.createdAt.getTime() })); +} + +/** A file that reached storage joins the workspace shelf, one entry per URL. */ +export async function rememberUpload(input: { url: string; kind: string; name: string }): Promise { + const viewer = await requireViewer(); + const url = String(input.url ?? "").trim(); + if (!/^https?:\/\//.test(url)) throw new Error("Invalid upload URL"); + const kind = input.kind === "video" || input.kind === "audio" ? input.kind : "image"; + const name = String(input.name ?? "").slice(0, 180) || "file"; + const now = new Date(); + const existing = await db + .select() + .from(schema.upload) + .where(and(eq(schema.upload.workspaceId, viewer.workspace.id), eq(schema.upload.url, url))) + .limit(1); + if (existing[0]) { + await db.update(schema.upload).set({ createdAt: now, name }).where(eq(schema.upload.id, existing[0].id)); + return { id: existing[0].id, url, kind: existing[0].kind, name, createdAt: now.getTime() }; + } + const id = newId(); + await db.insert(schema.upload).values({ id, workspaceId: viewer.workspace.id, url, kind, name, createdAt: now }); + return { id, url, kind, name, createdAt: now.getTime() }; +} + +export async function forgetUpload(id: string): Promise { + const viewer = await requireViewer(); + await db.delete(schema.upload).where(and(eq(schema.upload.workspaceId, viewer.workspace.id), eq(schema.upload.id, id))); +} diff --git a/src/server/billing.ts b/src/server/billing.ts new file mode 100644 index 0000000..f09c79e --- /dev/null +++ b/src/server/billing.ts @@ -0,0 +1,142 @@ +import { eq } from "drizzle-orm"; +import Stripe from "stripe"; + +import { db, schema } from "@/db/client"; +import { getPlan, isPlanId, type PlanId } from "@/domain/plans"; + +import { grantCredits } from "./credits"; + +/** Stripe is optional: a self-hosted studio with no key simply shows no + checkout. Everything here degrades to "billing is not configured". */ +export function stripeClient(): Stripe | null { + const key = process.env.STRIPE_SECRET_KEY?.trim(); + if (!key) return null; + return new Stripe(key, { apiVersion: "2026-08-27.dahlia" as Stripe.LatestApiVersion }); +} + +export function billingConfigured(): boolean { + return Boolean(process.env.STRIPE_SECRET_KEY?.trim()); +} + +/** Price ids live in the environment so test and live mode share the plan + table: STRIPE_PRICE_STARTER, STRIPE_PRICE_PRO, STRIPE_PRICE_STUDIO. */ +export function priceIdFor(planId: PlanId): string | null { + const value = process.env[`STRIPE_PRICE_${planId.toUpperCase()}`]?.trim(); + return value || null; +} + +export function planForPrice(priceId: string | null | undefined): PlanId | null { + if (!priceId) return null; + for (const plan of ["starter", "pro", "studio"] as const) { + if (priceIdFor(plan) === priceId) return plan; + } + return null; +} + +export async function ensureCustomer(stripe: Stripe, workspace: schema.Workspace, email: string): Promise { + if (workspace.stripeCustomerId) return workspace.stripeCustomerId; + const customer = await stripe.customers.create({ + email, + name: workspace.name, + metadata: { workspaceId: workspace.id }, + }); + await db + .update(schema.workspace) + .set({ stripeCustomerId: customer.id, updatedAt: new Date() }) + .where(eq(schema.workspace.id, workspace.id)); + return customer.id; +} + +async function workspaceByCustomer(customerId: string): Promise { + const [row] = await db.select().from(schema.workspace).where(eq(schema.workspace.stripeCustomerId, customerId)); + return row ?? null; +} + +function customerIdOf(value: string | Stripe.Customer | Stripe.DeletedCustomer | null | undefined): string | null { + if (!value) return null; + return typeof value === "string" ? value : value.id; +} + +/** Applies one Stripe event to the workspace it concerns. Returns what it + did, for the log. Each event id is recorded first, so a redelivery is a + no-op even if the handler below throws halfway on the first try. */ +export async function applyStripeEvent(event: Stripe.Event): Promise { + const now = new Date(); + try { + await db.insert(schema.stripeEvent).values({ id: event.id, type: event.type, processedAt: now }); + } catch { + return "duplicate"; + } + + switch (event.type) { + case "checkout.session.completed": { + const session = event.data.object; + const workspaceId = session.metadata?.workspaceId; + const planId = session.metadata?.planId; + if (!workspaceId || !planId || !isPlanId(planId)) return "ignored: no workspace or plan in metadata"; + const subscriptionId = typeof session.subscription === "string" ? session.subscription : (session.subscription?.id ?? null); + await db + .update(schema.workspace) + .set({ + planId, + stripeSubscriptionId: subscriptionId, + stripeCustomerId: customerIdOf(session.customer) ?? undefined, + updatedAt: now, + }) + .where(eq(schema.workspace.id, workspaceId)); + await grantCredits(workspaceId, getPlan(planId).credits, "plan_grant", { + stripeEventId: event.id, + note: `${getPlan(planId).label} activated`, + }); + return `activated ${planId} for ${workspaceId}`; + } + case "invoice.paid": { + const invoice = event.data.object; + /* The first invoice is the checkout; credits for it come from the + session above. Only renewals grant here. */ + if (invoice.billing_reason !== "subscription_cycle") return "ignored: not a renewal"; + const customerId = customerIdOf(invoice.customer); + const workspace = customerId ? await workspaceByCustomer(customerId) : null; + if (!workspace) return "ignored: unknown customer"; + const plan = getPlan(workspace.planId); + const periodEnd = invoice.lines.data[0]?.period?.end; + await db + .update(schema.workspace) + .set({ planRenewsAt: periodEnd ? new Date(periodEnd * 1000) : null, updatedAt: now }) + .where(eq(schema.workspace.id, workspace.id)); + await grantCredits(workspace.id, plan.credits, "plan_grant", { stripeEventId: event.id, note: `${plan.label} renewal` }); + return `renewed ${plan.id} for ${workspace.id}`; + } + case "customer.subscription.updated": { + const subscription = event.data.object; + const customerId = customerIdOf(subscription.customer); + const workspace = customerId ? await workspaceByCustomer(customerId) : null; + if (!workspace) return "ignored: unknown customer"; + const planId = planForPrice(subscription.items.data[0]?.price?.id); + const renews = subscription.items.data[0]?.current_period_end; + await db + .update(schema.workspace) + .set({ + ...(planId && subscription.status === "active" ? { planId } : {}), + stripeSubscriptionId: subscription.id, + planRenewsAt: renews ? new Date(renews * 1000) : null, + updatedAt: now, + }) + .where(eq(schema.workspace.id, workspace.id)); + return `updated subscription for ${workspace.id}`; + } + case "customer.subscription.deleted": { + const subscription = event.data.object; + const customerId = customerIdOf(subscription.customer); + const workspace = customerId ? await workspaceByCustomer(customerId) : null; + if (!workspace) return "ignored: unknown customer"; + await db + .update(schema.workspace) + .set({ planId: "free", stripeSubscriptionId: null, planRenewsAt: null, updatedAt: now }) + .where(eq(schema.workspace.id, workspace.id)); + return `downgraded ${workspace.id} to free`; + } + default: + return `ignored: ${event.type}`; + } +} diff --git a/src/server/credits.ts b/src/server/credits.ts new file mode 100644 index 0000000..2db88ad --- /dev/null +++ b/src/server/credits.ts @@ -0,0 +1,106 @@ +import { and, eq, sql } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; + +import { newId } from "./ids"; + +export class InsufficientCreditsError extends Error { + readonly needed: number; + readonly balance: number; + constructor(needed: number, balance: number) { + super(`Not enough credits: this run needs ${needed}, the workspace has ${balance}`); + this.name = "InsufficientCreditsError"; + this.needed = needed; + this.balance = balance; + } +} + +type Reason = (typeof schema.creditLedger.$inferInsert)["reason"]; + +/** Takes credits for a run before it is submitted. The balance check and the + debit are one conditional UPDATE, so two submits racing for the last + credits cannot both win. */ +export async function reserveCredits(workspaceId: string, amount: number, requestKey: string): Promise { + if (amount <= 0) return; + const now = new Date(); + const result = await db + .update(schema.workspace) + .set({ creditBalance: sql`${schema.workspace.creditBalance} - ${amount}`, updatedAt: now }) + .where(and(eq(schema.workspace.id, workspaceId), sql`${schema.workspace.creditBalance} >= ${amount}`)); + if (result.rowsAffected === 0) { + const [row] = await db + .select({ balance: schema.workspace.creditBalance }) + .from(schema.workspace) + .where(eq(schema.workspace.id, workspaceId)); + throw new InsufficientCreditsError(amount, row?.balance ?? 0); + } + await db.insert(schema.creditLedger).values({ + id: newId(), + workspaceId, + delta: -amount, + reason: "generation", + requestId: requestKey, + createdAt: now, + }); +} + +/** Gives a failed run its credits back, once: the (requestId, reason) pair is + unique in the ledger, so a second refund of the same request is refused by + the database and the balance is left alone. */ +export async function refundCredits(workspaceId: string, amount: number, requestId: string, note?: string): Promise { + if (amount <= 0) return false; + const now = new Date(); + try { + await db.insert(schema.creditLedger).values({ + id: newId(), + workspaceId, + delta: amount, + reason: "refund", + requestId, + note, + createdAt: now, + }); + } catch { + return false; + } + await db + .update(schema.workspace) + .set({ creditBalance: sql`${schema.workspace.creditBalance} + ${amount}`, updatedAt: now }) + .where(eq(schema.workspace.id, workspaceId)); + return true; +} + +/** Credits arriving from a plan or a purchase. Keyed by the Stripe event so a + redelivered webhook grants nothing twice. */ +export async function grantCredits( + workspaceId: string, + amount: number, + reason: Extract, + options: { stripeEventId?: string; note?: string } = {}, +): Promise { + if (amount === 0) return; + const now = new Date(); + await db.batch([ + db.insert(schema.creditLedger).values({ + id: newId(), + workspaceId, + delta: amount, + reason, + stripeEventId: options.stripeEventId, + note: options.note, + createdAt: now, + }), + db + .update(schema.workspace) + .set({ creditBalance: sql`${schema.workspace.creditBalance} + ${amount}`, updatedAt: now }) + .where(eq(schema.workspace.id, workspaceId)), + ]); +} + +export async function creditBalance(workspaceId: string): Promise { + const [row] = await db + .select({ balance: schema.workspace.creditBalance }) + .from(schema.workspace) + .where(eq(schema.workspace.id, workspaceId)); + return row?.balance ?? 0; +} diff --git a/src/server/ids.ts b/src/server/ids.ts new file mode 100644 index 0000000..ba858ac --- /dev/null +++ b/src/server/ids.ts @@ -0,0 +1,5 @@ +import { randomUUID } from "node:crypto"; + +export function newId(): string { + return randomUUID(); +} diff --git a/src/server/mail.ts b/src/server/mail.ts new file mode 100644 index 0000000..4b1935a --- /dev/null +++ b/src/server/mail.ts @@ -0,0 +1,22 @@ +/** Outbound mail is optional. With RESEND_API_KEY and MAIL_FROM set, the + studio can send password resets; without them the feature is simply + absent from the UI rather than silently broken. Resend's HTTP API is a + single POST, so no SDK is needed. */ +export function mailConfigured(): boolean { + return Boolean(process.env.RESEND_API_KEY?.trim() && process.env.MAIL_FROM?.trim()); +} + +export async function sendMail(input: { to: string; subject: string; text: string; html?: string }): Promise { + const key = process.env.RESEND_API_KEY?.trim(); + const from = process.env.MAIL_FROM?.trim(); + if (!key || !from) throw new Error("Mail is not configured"); + const response = await fetch("https://api.resend.com/emails", { + method: "POST", + headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" }, + body: JSON.stringify({ from, to: [input.to], subject: input.subject, text: input.text, html: input.html }), + }); + if (!response.ok) { + const body = await response.text().catch(() => ""); + throw new Error(`Mail provider refused the message (${response.status}) ${body.slice(0, 200)}`); + } +} diff --git a/src/server/runs.ts b/src/server/runs.ts new file mode 100644 index 0000000..cee6e0d --- /dev/null +++ b/src/server/runs.ts @@ -0,0 +1,268 @@ +import { and, desc, eq, inArray, isNull, lt } from "drizzle-orm"; + +import { db, schema } from "@/db/client"; +import type { RunRow } from "@/db/schema"; +import type { Surface } from "@/generation/catalog/types"; +import type { GenerationStatus } from "@/generation/platform"; + +/** The row the studio renders. Mirrors RunRow with dates as numbers, since it + crosses the server-action boundary and lands in client state. */ +export interface RunRecord { + id: string; + requestId: string; + surface: Surface; + kind: Surface; + modelId: string; + modelLabel: string; + prompt: string; + scene: string; + ratio: string; + meta: string; + badge?: string; + urls: string[]; + status: "running" | "completed" | "failed"; + error?: string; + favorite: boolean; + review: "pending" | "approved" | "rejected"; + settings: Record; + credits: number; + productId: string | null; + campaignId: string | null; + presetId: string | null; + channelId: string | null; + createdAt: number; +} + +export type RunContext = { + productId: string | null; + campaignId: string | null; + presetId: string | null; + channelId: string | null; + scene: string; +}; + +export type RunDraft = RunContext & { + surface: Surface; + modelId: string; + modelLabel: string; + prompt: string; + ratio: string; + meta: string; + badge?: string; + settings: Record; + credits: number; +}; + +export const HISTORY_LIMIT = 120; + +export function toRunRecord(row: RunRow): RunRecord { + return { + id: row.id, + requestId: row.requestId, + surface: row.surface, + kind: row.surface, + modelId: row.modelId, + modelLabel: row.modelLabel, + prompt: row.prompt, + scene: row.scene, + ratio: row.ratio, + meta: row.meta, + badge: row.badge ?? undefined, + urls: row.urls, + status: row.status, + error: row.error ?? undefined, + favorite: row.favorite, + review: row.review, + settings: row.settings, + credits: row.credits, + productId: row.productId, + campaignId: row.campaignId, + presetId: row.presetId, + channelId: row.channelId, + createdAt: row.createdAt.getTime(), + }; +} + +function rowId(requestId: string, offset: number, count: number): string { + return count > 1 ? `${requestId}#${offset}` : requestId; +} + +/** One row per expected result, all waiting on the same platform request. */ +export async function insertRunningRows( + workspaceId: string, + userId: string, + requestId: string, + count: number, + draft: RunDraft, +): Promise { + const now = new Date(); + const rows = Array.from({ length: Math.max(1, count) }, (_, offset) => ({ + id: rowId(requestId, offset, count), + workspaceId, + userId, + requestId, + productId: draft.productId, + campaignId: draft.campaignId, + presetId: draft.presetId, + channelId: draft.channelId, + surface: draft.surface, + modelId: draft.modelId, + modelLabel: draft.modelLabel, + prompt: draft.prompt, + scene: draft.scene, + ratio: draft.ratio, + meta: draft.meta, + badge: draft.badge ?? null, + urls: [] as string[], + status: "running" as const, + error: null, + favorite: false, + review: "pending" as const, + settings: draft.settings, + /* The whole request's cost sits on its first row, so a batch is charged + once and refunded once. */ + credits: offset === 0 ? draft.credits : 0, + createdAt: now, + updatedAt: now, + deletedAt: null, + })); + await db.insert(schema.run).values(rows); + const stored = await db.select().from(schema.run).where(eq(schema.run.requestId, requestId)); + return stored.map(toRunRecord); +} + +export function failureText(status: GenerationStatus): string { + if (status.status === "nsfw") return "the platform flagged the result as NSFW"; + if (status.status === "canceled") return "the run was canceled"; + if (typeof status.error === "string" && status.error) return status.error; + return "the platform reported a failure"; +} + +/** Replace a request's running rows with its outcome. Returns the settled + rows, or null when nothing was running for it (already settled, or the + tiles were deleted while in flight). */ +export async function settleRequest( + workspaceId: string, + requestId: string, + outcome: { status: GenerationStatus } | { error: string }, +): Promise { + const running = await db + .select() + .from(schema.run) + .where( + and(eq(schema.run.workspaceId, workspaceId), eq(schema.run.requestId, requestId), eq(schema.run.status, "running")), + ); + if (running.length === 0) return null; + + const now = new Date(); + const first = running[0]!; + const urls = + "status" in outcome + ? (outcome.status.images?.map((image) => image.url) ?? (outcome.status.video ? [outcome.status.video.url] : [])) + : []; + const completed = "status" in outcome && outcome.status.status === "completed" && urls.length > 0; + const error = completed ? null : "status" in outcome ? failureText(outcome.status) : outcome.error; + + if (completed) { + /* The platform may answer with more or fewer media than tiles were opened + for. Rows are re-cut to one per delivered URL, keeping the first row's + credits and the request's identity. */ + const total = urls.length; + const rows = urls.map((url, offset) => ({ + ...first, + id: rowId(requestId, offset, total), + urls: [url], + status: "completed" as const, + error: null, + credits: offset === 0 ? first.credits : 0, + updatedAt: now, + })); + await db.batch([ + db.delete(schema.run).where(and(eq(schema.run.requestId, requestId), eq(schema.run.status, "running"))), + db.insert(schema.run).values(rows).onConflictDoNothing(), + ]); + } else { + await db + .update(schema.run) + .set({ status: "failed", error, urls: [], updatedAt: now }) + .where(and(eq(schema.run.requestId, requestId), eq(schema.run.status, "running"))); + } + const settled = await db.select().from(schema.run).where(eq(schema.run.requestId, requestId)); + return settled.map(toRunRecord); +} + +/** Newest first. `before` (a createdAt in ms) pages further back; the page + is one over the limit so the caller knows whether more exist. */ +export async function listRuns( + workspaceId: string, + options: { limit?: number; before?: number | null } = {}, +): Promise<{ runs: RunRecord[]; hasMore: boolean }> { + const limit = options.limit ?? HISTORY_LIMIT; + const rows = await db + .select() + .from(schema.run) + .where( + and( + eq(schema.run.workspaceId, workspaceId), + isNull(schema.run.deletedAt), + ...(options.before ? [lt(schema.run.createdAt, new Date(options.before))] : []), + ), + ) + .orderBy(desc(schema.run.createdAt)) + .limit(limit + 1); + return { runs: rows.slice(0, limit).map(toRunRecord), hasMore: rows.length > limit }; +} + +/** Every run of one campaign — campaigns are small and the page shows all. */ +export async function listCampaignRuns(workspaceId: string, campaignId: string): Promise { + const rows = await db + .select() + .from(schema.run) + .where(and(eq(schema.run.workspaceId, workspaceId), eq(schema.run.campaignId, campaignId), isNull(schema.run.deletedAt))) + .orderBy(desc(schema.run.createdAt)); + return rows.map(toRunRecord); +} + +export async function runningRequestIds(workspaceId: string, requestIds: string[]): Promise> { + if (requestIds.length === 0) return new Set(); + const rows = await db + .select({ requestId: schema.run.requestId }) + .from(schema.run) + .where( + and( + eq(schema.run.workspaceId, workspaceId), + eq(schema.run.status, "running"), + inArray(schema.run.requestId, requestIds), + ), + ); + return new Set(rows.map((row) => row.requestId)); +} + +export async function creditsOfRequest(workspaceId: string, requestId: string): Promise { + const rows = await db + .select({ credits: schema.run.credits }) + .from(schema.run) + .where(and(eq(schema.run.workspaceId, workspaceId), eq(schema.run.requestId, requestId))); + return rows.reduce((total, row) => total + row.credits, 0); +} + +export async function updateRuns( + workspaceId: string, + ids: string[], + patch: Partial>, +): Promise { + if (ids.length === 0) return; + await db + .update(schema.run) + .set({ ...patch, updatedAt: new Date() }) + .where(and(eq(schema.run.workspaceId, workspaceId), inArray(schema.run.id, ids))); +} + +export async function runsByIds(workspaceId: string, ids: string[]): Promise { + if (ids.length === 0) return []; + const rows = await db + .select() + .from(schema.run) + .where(and(eq(schema.run.workspaceId, workspaceId), inArray(schema.run.id, ids))); + return rows.map(toRunRecord); +} diff --git a/src/server/seal.ts b/src/server/seal.ts new file mode 100644 index 0000000..6d15e5c --- /dev/null +++ b/src/server/seal.ts @@ -0,0 +1,28 @@ +import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto"; + +/** AES-256-GCM around a secret the operator holds. Sealed values are stored + in the database so a dump of it alone reveals no customer platform key. */ +function key(): Buffer { + const secret = process.env.APP_SECRET?.trim() || "vitrina-dev-secret-change-me"; + return createHash("sha256").update(secret).digest(); +} + +export function seal(plain: string): string { + const iv = randomBytes(12); + const cipher = createCipheriv("aes-256-gcm", key(), iv); + const body = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]); + const tag = cipher.getAuthTag(); + return `v1.${iv.toString("base64url")}.${tag.toString("base64url")}.${body.toString("base64url")}`; +} + +export function unseal(sealed: string): string | null { + const [version, ivText, tagText, bodyText] = sealed.split("."); + if (version !== "v1" || !ivText || !tagText || !bodyText) return null; + try { + const decipher = createDecipheriv("aes-256-gcm", key(), Buffer.from(ivText, "base64url")); + decipher.setAuthTag(Buffer.from(tagText, "base64url")); + return Buffer.concat([decipher.update(Buffer.from(bodyText, "base64url")), decipher.final()]).toString("utf8"); + } catch { + return null; + } +} diff --git a/src/server/session.ts b/src/server/session.ts new file mode 100644 index 0000000..a7415fb --- /dev/null +++ b/src/server/session.ts @@ -0,0 +1,62 @@ +import { cookies, headers } from "next/headers"; +import { redirect } from "next/navigation"; +import { cache } from "react"; + +import { auth } from "@/auth/auth"; +import type { Workspace } from "@/db/schema"; + +import { workspaceForUser } from "./workspaces"; + +export class UnauthorizedError extends Error { + constructor() { + super("Sign in to continue"); + this.name = "UnauthorizedError"; + } +} + +export type Viewer = { + user: { id: string; name: string; email: string }; + workspace: Workspace; + role: "owner" | "editor"; +}; + +/** Remembers which of a member's workspaces is on screen. */ +export const WORKSPACE_COOKIE = "vt_workspace"; + +export const WORKSPACE_COOKIE_OPTIONS = { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax" as const, + path: "/", + maxAge: 60 * 60 * 24 * 365, +}; + +/** The signed-in user and the workspace they act in, read once per request. + Every server action and page goes through here, so a request without a + session cannot reach a query. */ +export const getViewer = cache(async (): Promise => { + const session = await auth.api.getSession({ headers: await headers() }); + if (!session) return null; + const jar = await cookies(); + const { workspace, role } = await workspaceForUser(session.user.id, jar.get(WORKSPACE_COOKIE)?.value); + return { + user: { id: session.user.id, name: session.user.name, email: session.user.email }, + workspace, + role, + }; +}); + +export async function requireViewer(): Promise { + const viewer = await getViewer(); + if (!viewer) throw new UnauthorizedError(); + return viewer; +} + +/** For pages: a missing session goes to sign-in instead of an error. Layouts + and pages render concurrently, so every page under /app asks for itself + rather than relying on the layout's redirect landing first. */ +export async function viewerOrRedirect(): Promise { + const viewer = await getViewer(); + if (!viewer) redirect("/login"); + return viewer; +} diff --git a/src/server/storage.ts b/src/server/storage.ts new file mode 100644 index 0000000..81a131f --- /dev/null +++ b/src/server/storage.ts @@ -0,0 +1,93 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { join, resolve } from "node:path"; + +import { newId } from "./ids"; + +export type StorageDriver = "blob" | "local"; + +/** Uploads go client-direct to Vercel Blob when a token is present, else to + the local disk under DATA_DIR, served back by /api/files. The local driver + exists so a laptop or a single Docker host works without a Blob store — + the platform still has to be able to fetch the resulting URL. */ +export function storageDriver(): StorageDriver { + const forced = process.env.STORAGE_DRIVER?.trim(); + if (forced === "local" || forced === "blob") return forced; + return process.env.BLOB_READ_WRITE_TOKEN?.trim() ? "blob" : "local"; +} + +export const ALLOWED_UPLOAD_TYPES = [ + "image/jpeg", + "image/png", + "image/webp", + "image/gif", + "video/mp4", + "audio/wav", + "audio/x-wav", +] as const; + +export const MAX_UPLOAD_BYTES = 64 * 1024 * 1024; + +export function dataDir(): string { + return resolve(/*turbopackIgnore: true*/ process.env.DATA_DIR?.trim() || "./data"); +} + +export function uploadsDir(): string { + return join(dataDir(), "uploads"); +} + +export function sanitizeFilename(filename: string): string { + const base = filename.replaceAll("\\", "/").split("/").pop() ?? ""; + const cleaned = base.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^\.+/, ""); + return cleaned.slice(0, 120) || "file"; +} + +/** Writes bytes under uploads//- and returns the path + the files route serves it at. The id makes the URL unguessable, which is + the only protection a URL the platform fetches anonymously can have. */ +export async function storeLocal(workspaceId: string, filename: string, bytes: Uint8Array): Promise { + const name = `${newId()}-${sanitizeFilename(filename)}`; + const dir = join(uploadsDir(), workspaceId); + await mkdir(dir, { recursive: true }); + await writeFile(join(dir, name), bytes); + return `${workspaceId}/${name}`; +} + +const SAFE_SEGMENT = /^[A-Za-z0-9._-]+$/; + +/** Resolves a served path back to disk, refusing anything that could step + outside the uploads directory. */ +export function localPath(segments: string[]): string | null { + if (segments.length !== 2 || !segments.every((segment) => SAFE_SEGMENT.test(segment) && !segment.startsWith("."))) { + return null; + } + return join(uploadsDir(), ...segments); +} + +export function contentTypeFor(name: string): string { + const ext = name.split(".").pop()?.toLowerCase(); + switch (ext) { + case "jpg": + case "jpeg": + return "image/jpeg"; + case "png": + return "image/png"; + case "webp": + return "image/webp"; + case "gif": + return "image/gif"; + case "mp4": + return "video/mp4"; + case "wav": + return "audio/wav"; + default: + return "application/octet-stream"; + } +} + +/** The origin the platform can reach files at. APP_URL in production; the + request's own origin in development. */ +export function publicOrigin(request: Request): string { + const explicit = process.env.APP_URL?.trim(); + if (explicit) return explicit.replace(/\/+$/, ""); + return new URL(request.url).origin; +} diff --git a/src/server/studio-data.ts b/src/server/studio-data.ts new file mode 100644 index 0000000..1aa1b09 --- /dev/null +++ b/src/server/studio-data.ts @@ -0,0 +1,30 @@ +import { operatorKey } from "@/generation/credentials"; +import type { StudioData } from "@/studio/types"; + +import { readBrandKit } from "./actions/brand"; +import { listProducts } from "./actions/products"; +import { listUploads } from "./actions/uploads"; +import { listRuns } from "./runs"; +import type { Viewer } from "./session"; + +/** One read per studio page load, on the server, so the grid opens full. */ +export async function loadStudioData(viewer: Viewer): Promise { + const [page, uploads, products, brand] = await Promise.all([ + listRuns(viewer.workspace.id), + listUploads(), + listProducts(), + readBrandKit(viewer.workspace.id), + ]); + const byok = Boolean(viewer.workspace.platformKeySealed); + return { + runs: page.runs, + hasMore: page.hasMore, + uploads, + products, + brand, + key: { mode: byok ? "byok" : operatorKey() ? "operator" : "none", byok, canEdit: viewer.role === "owner" }, + credits: viewer.workspace.creditBalance, + planId: viewer.workspace.planId, + workspaceName: viewer.workspace.name, + }; +} diff --git a/src/server/validation.ts b/src/server/validation.ts new file mode 100644 index 0000000..67d568d --- /dev/null +++ b/src/server/validation.ts @@ -0,0 +1,12 @@ +export type SaveResult = { ok: true; value: T } | { ok: false; errors: Record }; + +/** One message per field, the first issue winning, keyed the way a form + names its inputs. */ +export function flatten(error: { issues: Array<{ path: PropertyKey[]; message: string }> }): Record { + const out: Record = {}; + for (const issue of error.issues) { + const key = issue.path.map(String).join(".") || "form"; + out[key] ??= issue.message; + } + return out; +} diff --git a/src/server/workspaces.ts b/src/server/workspaces.ts new file mode 100644 index 0000000..aed74b0 --- /dev/null +++ b/src/server/workspaces.ts @@ -0,0 +1,178 @@ +import { and, asc, eq, isNull } from "drizzle-orm"; +import { randomBytes } from "node:crypto"; + +import { db, schema } from "@/db/client"; +import { slugify } from "@/domain/brand"; +import { getPlan } from "@/domain/plans"; + +import { newId } from "./ids"; + +export type Role = "owner" | "editor"; + +export type WorkspaceMembership = { workspace: schema.Workspace; role: Role }; + +const INVITE_TTL_MS = 14 * 24 * 60 * 60 * 1000; + +/** Everything a new account needs to start working: a workspace, its brand + kit and the trial credits. Runs inside the sign-up hook, and again when + an owner opens a second workspace (without the trial). */ +export async function bootstrapWorkspace( + owner: { id: string; name: string; email: string }, + options: { name?: string; trial?: boolean } = {}, +): Promise { + const now = new Date(); + const id = newId(); + const base = slugify(options.name || owner.name || owner.email.split("@")[0] || "workspace", "workspace"); + const slug = `${base}-${id.slice(0, 6)}`; + const trial = options.trial === false ? 0 : getPlan("free").credits; + const name = options.name?.trim() || (owner.name ? `${owner.name}'s brand` : "My brand"); + await db.batch([ + db.insert(schema.workspace).values({ + id, + name, + slug, + ownerId: owner.id, + planId: "free", + creditBalance: trial, + createdAt: now, + updatedAt: now, + }), + db.insert(schema.membership).values({ workspaceId: id, userId: owner.id, role: "owner", createdAt: now }), + db.insert(schema.brandKit).values({ workspaceId: id, name: options.name?.trim() ?? "", updatedAt: now }), + ...(trial > 0 + ? [ + db.insert(schema.creditLedger).values({ + id: newId(), + workspaceId: id, + delta: trial, + reason: "trial" as const, + note: "Welcome credits", + createdAt: now, + }), + ] + : []), + ]); + return id; +} + +export async function workspacesForUser(userId: string): Promise { + const rows = await db + .select({ workspace: schema.workspace, role: schema.membership.role }) + .from(schema.membership) + .innerJoin(schema.workspace, eq(schema.membership.workspaceId, schema.workspace.id)) + .where(eq(schema.membership.userId, userId)) + .orderBy(asc(schema.membership.createdAt)); + return rows; +} + +/** The workspace a user works in: the one they last switched to when they + are still a member of it, else their first. An account without any + (predating the sign-up hook, or whose hook failed) gets one made. */ +export async function workspaceForUser(userId: string, preferredId?: string | null): Promise { + const memberships = await workspacesForUser(userId); + const preferred = preferredId ? memberships.find((entry) => entry.workspace.id === preferredId) : undefined; + if (preferred) return preferred; + if (memberships[0]) return memberships[0]; + const id = await bootstrapWorkspace({ id: userId, name: "", email: "" }); + const [created] = await db.select().from(schema.workspace).where(eq(schema.workspace.id, id)); + return { workspace: created!, role: "owner" }; +} + +export async function isMember(workspaceId: string, userId: string): Promise { + const rows = await db + .select({ userId: schema.membership.userId }) + .from(schema.membership) + .where(and(eq(schema.membership.workspaceId, workspaceId), eq(schema.membership.userId, userId))) + .limit(1); + return rows.length > 0; +} + +export type Member = { userId: string; name: string; email: string; role: Role; joinedAt: number }; + +export async function listMembers(workspaceId: string): Promise { + const rows = await db + .select({ userId: schema.user.id, name: schema.user.name, email: schema.user.email, role: schema.membership.role, joinedAt: schema.membership.createdAt }) + .from(schema.membership) + .innerJoin(schema.user, eq(schema.membership.userId, schema.user.id)) + .where(eq(schema.membership.workspaceId, workspaceId)) + .orderBy(asc(schema.membership.createdAt)); + return rows.map((row) => ({ ...row, joinedAt: row.joinedAt.getTime() })); +} + +export async function seatsUsed(workspaceId: string): Promise { + const rows = await db.select({ userId: schema.membership.userId }).from(schema.membership).where(eq(schema.membership.workspaceId, workspaceId)); + return rows.length; +} + +export type Invite = { id: string; token: string; role: Role; expiresAt: number; createdAt: number }; + +export async function listOpenInvites(workspaceId: string): Promise { + const rows = await db + .select() + .from(schema.invite) + .where(and(eq(schema.invite.workspaceId, workspaceId), isNull(schema.invite.acceptedAt))) + .orderBy(asc(schema.invite.createdAt)); + const now = Date.now(); + return rows + .filter((row) => row.expiresAt.getTime() > now) + .map((row) => ({ id: row.id, token: row.token, role: row.role, expiresAt: row.expiresAt.getTime(), createdAt: row.createdAt.getTime() })); +} + +export async function createInvite(workspaceId: string, createdBy: string, role: Role): Promise { + const now = new Date(); + const row = { + id: newId(), + workspaceId, + token: randomBytes(24).toString("base64url"), + role, + createdBy, + expiresAt: new Date(now.getTime() + INVITE_TTL_MS), + acceptedBy: null, + acceptedAt: null, + createdAt: now, + }; + await db.insert(schema.invite).values(row); + return { id: row.id, token: row.token, role, expiresAt: row.expiresAt.getTime(), createdAt: now.getTime() }; +} + +export async function revokeInvite(workspaceId: string, inviteId: string): Promise { + await db.delete(schema.invite).where(and(eq(schema.invite.workspaceId, workspaceId), eq(schema.invite.id, inviteId))); +} + +export class InviteError extends Error { + readonly code: "unknown" | "expired" | "full"; + constructor(code: "unknown" | "expired" | "full") { + super( + code === "unknown" + ? "This invitation does not exist" + : code === "expired" + ? "This invitation has expired or was already used" + : "This workspace has no seats left on its plan", + ); + this.name = "InviteError"; + this.code = code; + } +} + +/** Joins the holder of a token to its workspace. A member already inside is + simply switched there; the token is spent either way. */ +export async function acceptInvite(token: string, userId: string): Promise { + const [row] = await db.select().from(schema.invite).where(eq(schema.invite.token, token)); + if (!row) throw new InviteError("unknown"); + if (row.acceptedAt || row.expiresAt.getTime() < Date.now()) throw new InviteError("expired"); + const now = new Date(); + if (!(await isMember(row.workspaceId, userId))) { + const [ws] = await db.select().from(schema.workspace).where(eq(schema.workspace.id, row.workspaceId)); + if (!ws) throw new InviteError("unknown"); + if ((await seatsUsed(ws.id)) >= getPlan(ws.planId).seats) throw new InviteError("full"); + await db.insert(schema.membership).values({ workspaceId: ws.id, userId, role: row.role, createdAt: now }); + } + await db.update(schema.invite).set({ acceptedBy: userId, acceptedAt: now }).where(eq(schema.invite.id, row.id)); + return row.workspaceId; +} + +export async function removeMember(workspaceId: string, userId: string): Promise { + const [ws] = await db.select({ ownerId: schema.workspace.ownerId }).from(schema.workspace).where(eq(schema.workspace.id, workspaceId)); + if (!ws || ws.ownerId === userId) throw new Error("The workspace owner cannot be removed"); + await db.delete(schema.membership).where(and(eq(schema.membership.workspaceId, workspaceId), eq(schema.membership.userId, userId))); +} diff --git a/src/site.ts b/src/site.ts new file mode 100644 index 0000000..7be12c7 --- /dev/null +++ b/src/site.ts @@ -0,0 +1,75 @@ +/* One source of truth for the site's identity and its canonical origin. + Server-only by intent: VERCEL_PROJECT_PRODUCTION_URL is not exposed to the + browser, so importing SITE_URL into a client component would resolve + differently on each side. Keep this module out of "use client" files. */ + +function resolveOrigin(): string { + const explicit = process.env.NEXT_PUBLIC_SITE_URL?.trim(); + if (explicit) return explicit.replace(/\/+$/, ""); + + const vercel = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim(); + if (vercel) return `https://${vercel.replace(/^https?:\/\//, "").replace(/\/+$/, "")}`; + + return "http://localhost:3000"; +} + +export const SITE_URL = resolveOrigin(); + +export const SITE_NAME = "Vitrina"; +export const SITE_DESCRIPTOR = "AI product visuals for e-commerce"; +export const SITE_TITLE = `${SITE_NAME} — ${SITE_DESCRIPTOR}`; + +export const SITE_DESCRIPTION = + "Product photos and clips for every channel, from the photo you already have. Presets for pack shots, lifestyle scenes, reveals and UGC clips; the brand kit writes the prompt, the channel fixes the ratio."; + +/** Near-black studio ground; also the installed-app and browser-chrome color. */ +export const STUDIO_BG = "#0a0a0b"; + +/* The card built by scripts/build-brand-assets.mjs. It lives in public/ rather + than as an app/opengraph-image file on purpose: the file convention outranks + an explicit declaration in its own segment, so the two would disagree about + the alt text — the root would take it from an opengraph-image.alt.txt while + every route that overrides `openGraph` took it from here. One asset, one + declaration, one alt. */ +export const OG_IMAGE = { + url: "/og.png", + width: 1200, + height: 630, + type: "image/png", + alt: "The Vitrina mark on a near-black field, above the Vitrina wordmark, the words AI product visuals for e-commerce, and a line describing presets and channels for product photos and clips.", +}; + +/* Next replaces the whole `openGraph` (and `twitter`) object when a route + defines one, so a route that only wants its own url would silently drop + og:type, og:site_name, og:locale and the card. Overrides go through here. */ +export function openGraphFor({ + path, + title = SITE_TITLE, + description = SITE_DESCRIPTION, +}: { + path: string; + title?: string; + description?: string; +}) { + return { + type: "website" as const, + siteName: SITE_NAME, + locale: "en_US", + url: path, + title, + description, + images: [OG_IMAGE], + }; +} + +export function twitterFor({ + title = SITE_TITLE, + description = SITE_DESCRIPTION, +}: { title?: string; description?: string } = {}) { + return { + card: "summary_large_image" as const, + title, + description, + images: [OG_IMAGE], + }; +} diff --git a/src/studio/artwork.ts b/src/studio/artwork.ts new file mode 100644 index 0000000..1cdc2d8 --- /dev/null +++ b/src/studio/artwork.ts @@ -0,0 +1,60 @@ +import type { Surface } from "@/generation/catalog"; + +/* Deterministic pseudo-random from a string seed, so compositions stay stable + across renders without storing coordinates. */ +function hash(str: string): number { + let h = 2166136261; + for (let i = 0; i < str.length; i++) { + h ^= str.charCodeAt(i); + h = Math.imul(h, 16777619); + } + return h >>> 0; +} + +function rng(seed: number): () => number { + let s = seed || 1; + return () => { + s = Math.imul(s ^ (s >>> 15), s | 1); + s ^= s + Math.imul(s ^ (s >>> 7), s | 61); + return ((s ^ (s >>> 14)) >>> 0) / 4294967296; + }; +} + +/** Layered-gradient artwork used for model swatches and as the ground behind + loading/failed runs. Seeded, so no two subjects share the exact same light. */ +export function artFor(surface: Surface, hue: number, seedKey: string): string { + const r = rng(hash(seedKey)); + const h2 = (hue + 36 + Math.round(r() * 44)) % 360; + const h3 = (hue + 14 + Math.round(r() * 22)) % 360; + const hc = (hue + 165 + Math.round(r() * 40)) % 360; + const x1 = 10 + Math.round(r() * 28); + const y1 = 4 + Math.round(r() * 22); + const x2 = 66 + Math.round(r() * 28); + const y2 = 70 + Math.round(r() * 26); + const xc = 15 + Math.round(r() * 70); + const yc = 30 + Math.round(r() * 55); + const angle = 138 + Math.round(r() * 44); + + const layers = [ + `radial-gradient(115% 88% at ${x1}% ${y1}%, oklch(0.76 0.16 ${hue} / 0.68), transparent 55%)`, + `radial-gradient(88% 78% at ${x2}% ${y2}%, oklch(0.52 0.14 ${h2} / 0.62), transparent 60%)`, + `radial-gradient(26% 24% at ${xc}% ${yc}%, oklch(0.5 0.15 ${hc} / 0.3), transparent 70%)`, + `radial-gradient(150% 120% at 50% 125%, oklch(0.1 0.02 ${h3} / 0.9), transparent 68%)`, + `linear-gradient(${angle}deg, oklch(0.38 0.09 ${hue}), oklch(0.17 0.04 ${h3}))`, + ]; + if (surface === "video") { + layers.unshift( + `linear-gradient(180deg, transparent 46%, oklch(0.8 0.1 ${hue} / 0.07) 50%, transparent 54%)`, + ); + } + return layers.join(", "); +} + +export function swatchFor(surface: Surface, seed: string): string { + return artFor(surface, hash(seed) % 360, seed); +} + +/* Film-grain overlay shared by every artwork, defined once as a CSS custom + property so the data URI lives in one place. */ +export const GRAIN_URI = + "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E\")"; diff --git a/src/studio/asset-picker.tsx b/src/studio/asset-picker.tsx new file mode 100644 index 0000000..cfc0e33 --- /dev/null +++ b/src/studio/asset-picker.tsx @@ -0,0 +1,437 @@ +"use client"; + +import { memo, useEffect, useMemo, useRef, useState } from "react"; + +import type { MediaItem, MediaRole, ModelEntry } from "@/generation/catalog"; + +import { ROLE_KINDS, ROLE_LABELS, defaultRole, roleNoun, rolesOf, type AssetKind } from "./data"; +import type { RunRecord } from "./history"; +import { + AssetsIcon, + AudioIcon, + CheckIcon, + CloseIcon, + PlayBadgeIcon, + UploadIcon, +} from "./icons"; +import type { UploadRecord } from "@/server/actions/uploads"; + +type Source = "uploads" | "generations"; + +const SOURCES: readonly Source[] = ["uploads", "generations"]; + +const SOURCE_LABELS: Record = { + uploads: "Uploads", + generations: "Generations", +}; + +interface Asset { + url: string; + kind: AssetKind; + /** The tile's name — a file name, or the prompt the run was made from. It is + the alt text and the pointer title: a grey plate has no other handle. */ + title: string; + /** The run's layered-gradient art, so a tile whose media is slow to arrive or + has aged off the platform's CDN is still a picture rather than a hole. */ + art?: string; +} + +/** Everything a role can be filled from: files this browser sent to Blob, and + the finished runs already in the history. Both are public URLs the plane can + carry, so the picker treats them as one library cut along two tabs. */ +export function AssetPicker({ + model, + items, + uploads, + history, + staged, + uploading, + onUpload, + onApply, + onClose, +}: { + model: ModelEntry; + /** The current surface's attachments. They are what the panel opens holding: + a role's inputs are edited here, not only added to. */ + items: MediaItem[]; + uploads: UploadRecord[]; + history: RunRecord[]; + staged: string | null; + uploading: boolean; + onUpload: (role: MediaRole) => void; + onApply: (role: MediaRole, urls: string[]) => void; + onClose: () => void; +}) { + const roles = rolesOf(model); + const [role, setRole] = useState(() => defaultRole(model)); + /* null until the visitor picks a shelf: the panel opens on whichever one + actually holds something, so someone who has generated all day and + uploaded nothing does not land on an empty tab. */ + const [source, setSource] = useState(null); + /* The role's inputs as this panel would leave them — seeded with what is + already on the plane, so an attached tile opens marked and can be pressed + off again. An array, not a Set: the order picked is the order attached, + and a start/end pair is not order-blind. */ + const [selected, setSelected] = useState(() => urlsOf(items, defaultRole(model))); + const tabsRef = useRef(null); + + const kind = ROLE_KINDS[role]; + const max = model.roles[role] ?? 0; + const current = useMemo(() => urlsOf(items, role), [items, role]); + const room = Math.max(0, max - selected.length); + + const uploadAssets = useMemo( + () => + uploads + .filter((record) => record.kind === kind) + .map((record) => ({ url: record.url, kind: record.kind, title: record.name })), + [uploads, kind], + ); + + /* One record can carry several media, and each is its own attachable frame. */ + const runAssets = useMemo(() => { + const out: Asset[] = []; + for (const record of history) { + if (record.status !== "completed" || record.kind !== kind) continue; + for (const url of record.urls) out.push({ url, kind, title: record.prompt, art: record.art }); + } + return out; + }, [history, kind]); + + const shelf: Source = + source ?? (uploadAssets.length === 0 && runAssets.length > 0 ? "generations" : "uploads"); + const assets = shelf === "uploads" ? uploadAssets : runAssets; + + // The dialog takes focus on the control that scopes it, not on the grid. + useEffect(() => { + tabsRef.current?.querySelector('[aria-selected="true"]')?.focus(); + }, []); + + /* A file uploaded from inside the panel joins the selection rather than the + plane: one press still applies the whole set. The ref holds whatever was + staged before this panel opened, so reopening does not re-select it. */ + const seen = useRef(staged); + + /* A new role is a new library and a new set — a start frame cannot follow the + switch to audio — so the shelf and the selection are both re-seeded. */ + function pickRole(next: MediaRole) { + setRole(next); + setSource(null); + setSelected(urlsOf(items, next)); + } + + /* Start and end are a pair: attaching the start should land on the empty end + slot instead of closing, so the second frame is one more click, not a + reopen plus a confirm. */ + function advanceOrClose(filled: MediaRole) { + if (filled === "start" && (model.roles.end ?? 0) > 0) { + const endUsed = items.filter((item) => item.role === "end").length; + if (endUsed < (model.roles.end ?? 0)) { + pickRole("end"); + return; + } + } + onClose(); + } + + function commit(urls: string[]) { + onApply(role, urls); + if (max > 0 && urls.length >= max) { + advanceOrClose(role); + return; + } + onClose(); + } + + /* A file that just finished uploading joins the selection — an external + event (the tray's upload) arriving through props, hence the effect. */ + useEffect(() => { + if (staged === null || staged === seen.current) return; + seen.current = staged; + if (max === 1) { + // eslint-disable-next-line react-hooks/set-state-in-effect -- reacts to the upload landing + setSelected([staged]); + commit([staged]); + return; + } + setSelected((prev) => (prev.includes(staged) ? prev : [...prev, staged])); + // eslint-disable-next-line react-hooks/exhaustive-deps -- only the arrival of a new upload should re-run this + }, [staged]); + + function toggle(url: string) { + if (max === 1) { + const next = selected.includes(url) ? [] : [url]; + setSelected(next); + onApply(role, next); + if (next.length === 1) advanceOrClose(role); + return; + } + setSelected((prev) => + prev.includes(url) ? prev.filter((entry) => entry !== url) : [...prev, url], + ); + } + + const onTabKeyDown = (event: React.KeyboardEvent) => { + const from = SOURCES.indexOf(shelf); + const to = + event.key === "ArrowRight" + ? (from + 1) % SOURCES.length + : event.key === "ArrowLeft" + ? (from - 1 + SOURCES.length) % SOURCES.length + : -1; + if (to < 0) return; + event.preventDefault(); + setSource(SOURCES[to]!); + tabsRef.current?.querySelectorAll('[role="tab"]')[to]?.focus(); + }; + + const empty = emptyCopy(shelf, kind); + const canUpload = room > 0 && !uploading; + const uploadTip = uploading + ? "Uploading…" + : room > 0 + ? `Upload a ${roleNoun(role, 1)} from this device` + : `Every ${roleNoun(role, 1)} slot is taken — press one off to free it`; + + /* The button states the difference it will make, so a set edited down reads + as a removal rather than as an "Add" that removes. */ + const picked = new Set(selected); + const added = selected.filter((url) => !current.includes(url)).length; + const dropped = current.filter((url) => !picked.has(url)).length; + const applyLabel = + added && dropped + ? `Replace ${roleNoun(role, Math.max(added, dropped))}` + : added + ? `Add ${added > 1 ? `${added} ` : ""}${roleNoun(role, added)}` + : dropped + ? `Remove ${dropped > 1 ? `${dropped} ` : ""}${roleNoun(role, dropped)}` + : "Done"; + + return ( +
+
+
+ {SOURCES.map((id) => { + const active = id === shelf; + const count = id === "uploads" ? uploadAssets.length : runAssets.length; + return ( + + ); + })} +
+ + +
+ + {/* Which slot this panel is editing. Models declaring one role need no + switch — the footer already names what is being changed. */} + {roles.length > 1 && ( +
+ {roles.map((entry) => { + const used = + entry === role ? selected.length : items.filter((item) => item.role === entry).length; + return ( + + ); + })} +
+ )} + +
+ {assets.length === 0 ? ( +
+ + {shelf === "uploads" ? : } + + {empty.title} + {empty.hint} + {shelf === "uploads" && ( + + )} +
+ ) : ( +
+ {shelf === "uploads" && ( + + )} + + {assets.map((asset) => { + const on = picked.has(asset.url); + return ( + + ); + })} +
+ )} +
+ +
+ + {selected.length} of {max} {roleNoun(role, max)} + + {/* Never disabled: with no change left to apply it is simply the way + out, and the panel always offers one press that ends the task. */} + +
+
+ ); +} + +function urlsOf(items: MediaItem[], role: MediaRole): string[] { + return items.filter((item) => item.role === role).map((item) => item.url); +} + +function emptyCopy(source: Source, kind: AssetKind): { title: string; hint: string } { + if (source === "uploads") { + return { + title: "Nothing uploaded yet", + hint: "Files you send from this device stay on this shelf for the next run too.", + }; + } + if (kind === "audio") { + return { + title: "Runs never return audio", + hint: "Attach an audio track from this device instead — it lands on the Uploads shelf.", + }; + } + return { + title: `No finished ${kind} runs yet`, + hint: `Every ${kind} you generate lands here, ready to feed the next run.`, + }; +} + +/* Video assets preview on hover, the same reflex the gallery teaches, so the + tile carries its own element and its own ref. */ +const AssetTile = memo(function AssetTile({ + asset, + picked, + blocked, + onToggle, +}: { + asset: Asset; + picked: boolean; + blocked: boolean; + onToggle: (url: string) => void; +}) { + const videoRef = useRef(null); + + return ( + + ); +}); diff --git a/src/studio/composer.tsx b/src/studio/composer.tsx new file mode 100644 index 0000000..0fec502 --- /dev/null +++ b/src/studio/composer.tsx @@ -0,0 +1,512 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; +import type { CSSProperties, ReactNode } from "react"; + +import type { BrandKit, Product } from "@/domain/brand"; +import { parseSettings } from "@/generation/catalog"; +import type { ModelEntry, Surface } from "@/generation/catalog"; +import type { UploadRecord } from "@/server/actions/uploads"; +import { MAX_BATCH, useActive } from "@/generation/stores/active"; +import { useImagePrompt, useVideoPrompt } from "@/generation/stores/prompt"; +import { useSettings } from "@/generation/stores/settings"; + +import { swatchFor } from "./artwork"; +import { AssetPicker } from "./asset-picker"; +import { PROMPT_PLACEHOLDERS, countSetting } from "./data"; +import type { RunRecord } from "./history"; +import { ArrowUpIcon, CaretDownIcon, CloseIcon, MinusIcon, PlusIcon, WarningIcon } from "./icons"; +import { finalPrompt, type Job } from "./job"; +import { JobPicker, JobRail } from "./job-rail"; +import { MediaStrip, useMediaTray } from "./media-tray"; +import { ModelIcon, modelIconSrc } from "./model-icon"; +import { ModelPicker } from "./model-picker"; +import { SettingPill, SettingPopover } from "./settings"; + +/* Overlay ids: the two fixed panels, or one setting addressed by its catalog + key — the rail renders whatever the model declares, so the ids cannot be a + closed union. */ +const PICKER = "picker"; +const ASSETS = "assets"; +const SETTING = "setting:"; + +const PROMPT_MAX_HEIGHT = 168; + +/* What the studio itself can deliver for a model that carries no count of its + own: one platform request per result. */ +const STUDIO_COUNTS = Array.from({ length: MAX_BATCH }, (_, index) => index + 1); + +/** Breathing room between a popover and the control it opened from. */ +const POPOVER_GAP = 8; + +/** Declared widths keep an opening popover inside the composer's own column. */ +function popoverWidth(id: string, model: ModelEntry): number { + if (id === PICKER || id === ASSETS || id.startsWith("job:")) return 560; + /* A list of an enum's values is the narrow panel; a slider needs its travel. */ + if (id.startsWith(SETTING) && model.settings[id.slice(SETTING.length)]?.type === "enum") { + return 216; + } + return 268; +} + +export function Composer({ + surface, + model, + generating, + error, + focusNonce, + history, + products, + brand, + job, + initialUploads, + notice, + selection, + selecting, + onError, + onGenerate, +}: { + surface: Surface; + model: ModelEntry; + generating: boolean; + error: string | null; + focusNonce: number; + /* Finished runs are attachable inputs, so the asset picker reads the same + log the gallery renders. */ + history: RunRecord[]; + products: Product[]; + brand: BrandKit; + /* The product, preset and channel the composer is set up for. */ + job: Job; + initialUploads: UploadRecord[]; + /* Transient receipts from the gallery share the composer's banner strip + rather than adding a floating layer of their own. */ + notice?: ReactNode; + /* The bulk toolbar takes the composer's slot while runs are picked; it is + always mounted so it can animate away, and states its own presence. */ + selection: ReactNode; + selecting: boolean; + onError: (message: string | null) => void; + onGenerate: () => void; +}) { + const setModel = useActive((state) => state.setModel); + const batch = useActive((state) => state.batch); + const setBatch = useActive((state) => state.setBatch); + const imagePrompt = useImagePrompt(); + const videoPrompt = useVideoPrompt(); + const prompt = surface === "image" ? imagePrompt : videoPrompt; + const settings = useSettings(); + const values = parseSettings(model, settings.byModel[model.id] ?? {}); + const tray = useMediaTray(model, onError, initialUploads); + const [preview, setPreview] = useState(false); + + const [overlay, setOverlay] = useState(null); + const [anchor, setAnchor] = useState({ x: 0, y: 0 }); + const [shortcut, setShortcut] = useState(null); + const dockRef = useRef(null); + const wrapRef = useRef(null); + const promptRef = useRef(null); + /* A run in flight is not a lock: it holds its own tile in the grid, so the + only thing that can stop a press is having nothing to say. */ + const compiled = finalPrompt(prompt.text, job, brand); + const disabled = compiled.trim().length === 0; + const placeholder = job.preset ? job.preset.scenePlaceholder : PROMPT_PLACEHOLDERS[surface]; + + /* One batch control, two mechanisms. A model that declares its own + results-per-request gets that setting written; the rest are submitted once + per result by the studio. Either way the control means "results per press", + so the model's own count key never also appears as a settings pill. */ + const native = countSetting(model); + const counts = native ? native.counts : STUDIO_COUNTS; + const batchValue = native ? Number(values[native.key]) || counts[0]! : batch; + const settingKeys = Object.keys(model.settings).filter((key) => key !== native?.key); + + function setBatchValue(next: number) { + if (!native) { + setBatch(next); + return; + } + settings.set(model.id, { [native.key]: native.kind === "enum" ? String(next) : next }); + } + + /* The dock floats over the gallery, so the gallery cannot reserve its height + from layout. It reads it from here instead, and the last row keeps clearing + a composer that grew — a long prompt, a media strip, the undo receipt. */ + useEffect(() => { + const dock = dockRef.current; + if (!dock) return; + const observer = new ResizeObserver(() => { + dock.parentElement?.style.setProperty("--vt-dock-h", `${dock.offsetHeight}px`); + }); + observer.observe(dock); + return () => observer.disconnect(); + }, []); + + useEffect(() => { + if (!overlay) return; + const onPointerDown = (event: PointerEvent) => { + if (wrapRef.current && !wrapRef.current.contains(event.target as Node)) setOverlay(null); + }; + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") setOverlay(null); + }; + document.addEventListener("pointerdown", onPointerDown); + document.addEventListener("keydown", onKeyDown); + return () => { + document.removeEventListener("pointerdown", onPointerDown); + document.removeEventListener("keydown", onKeyDown); + }; + }, [overlay]); + + /* Auto-grow the prompt, including when text is set programmatically — and + again when the column narrows, because the field shares its line with the + attachment now and the same text rewraps onto more of them. Only a width + change re-measures: reacting to the height we just set would loop. */ + useEffect(() => { + const el = promptRef.current; + if (!el) return; + const grow = () => { + el.style.height = "auto"; + el.style.height = `${Math.min(el.scrollHeight, PROMPT_MAX_HEIGHT)}px`; + }; + grow(); + let width = el.clientWidth; + const observer = new ResizeObserver(() => { + if (el.clientWidth === width) return; + width = el.clientWidth; + grow(); + }); + observer.observe(el); + return () => observer.disconnect(); + }, [prompt.text]); + + useEffect(() => { + if (focusNonce > 0) promptRef.current?.focus(); + }, [focusNonce]); + + /* A panel anchored to a control the visitor can no longer see is a stray + plate — the swap closes whatever the composer had open. */ + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect -- reacts to a parent-owned mode switch + if (selecting) setOverlay(null); + }, [selecting]); + + // Rendered only after mount: the modifier is the visitor's, not the server's. + useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect -- navigator is read after hydration on purpose + setShortcut(/Mac|iP(hone|ad|od)/.test(navigator.userAgent) ? "⌘↵" : "Ctrl↵"); + }, []); + + /* Popovers are positioned by the composer so the wide ones stay inside its + column, but they open off the control that summoned them: the anchor + travels to the trigger's left edge, clamped to the column, and rises from + just above the trigger's own top rather than from the whole dock. + One thing outranks the trigger: the attachments. A panel laid over the + frames already on the plane makes the visitor choose blind, so where the + strip is present every panel clears it. */ + function toggle(next: string, trigger: HTMLElement) { + if (overlay === next) { + setOverlay(null); + return; + } + const wrap = wrapRef.current; + if (!wrap) return; + const wrapBox = wrap.getBoundingClientRect(); + const triggerBox = trigger.getBoundingClientRect(); + const strip = wrap.querySelector(".vt-strip"); + const ceiling = strip + ? Math.min(triggerBox.top, strip.getBoundingClientRect().top) + : triggerBox.top; + const available = wrap.clientWidth; + const width = Math.min(popoverWidth(next, model), available); + setAnchor({ + x: Math.round(Math.max(0, Math.min(triggerBox.left - wrapBox.left, available - width))), + y: Math.round(wrapBox.bottom - ceiling + POPOVER_GAP), + }); + setOverlay(next); + } + + const attachLabel = tray.allFull ? "Change the inputs" : "Add an input"; + const settingKey = overlay?.startsWith(SETTING) ? overlay.slice(SETTING.length) : null; + const generateLabel = batchValue > 1 ? `Generate ${batchValue} results` : "Generate"; + const generateTip = disabled ? "Write a prompt first" : `${generateLabel} · ${shortcut ?? "⌘↵"}`; + const jobOverlay = overlay?.startsWith("job:") ? overlay : null; + + return ( +
+
+ {notice} + + {error && ( +
+ + + + {error} + +
+ )} + + {settingKey && } + {jobOverlay && ( + setOverlay(null)} /> + )} + {overlay === ASSETS && ( + setOverlay(null)} + /> + )} + {overlay === PICKER && ( + { + setModel(next.id); + setOverlay(null); + }} + onClose={() => setOverlay(null)} + /> + )} + + {/* One slot, two instruments. Both are laid on the same cell and + aligned to the same bottom edge, so the dock never changes height + and the toolbar rises exactly where the composer's rail was. */} +
+
+ + + + + {/* The attachment sits beside the words it belongs to, on the same + left rail the control row starts from. */} +
+ {tray.roles.length > 0 && ( + <> + {tray.input} + + + )} + + {/* The prompt lives inside the wrap the outside-pointer rule + guards, so it has to dismiss for itself: reaching for the words + puts the open control away. Pointer and focus both, because a + button click does not move focus on every platform — the field + can still hold it while a popover stands open. */} + -
- - -

Billing

-
-
- Address - -
-
- City - -
-
- State - -
-
- Country - -
-
- Email - -
-
-
-
- Zip code - -
-
- Phone number - -
-
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - \ No newline at end of file