01 · Getting Started
Installation
Place both
ark-meta-tags.js and your host HTML in the same directory. No npm, no bundler needed.HTML
<!-- Script tag (simplest) -->
<script src="ark-meta-tags.js"></script>
<!-- Or as ES module -->
<script type="module">
import ArkMetaTags from './ark-meta-tags.js';
</script>
// CommonJS / Node
const ArkMetaTags = require('./ark-meta-tags');
CSS is self-injected. The library writes its own <style> tag to <head> once per page. You don't need any external stylesheet. Override with .amt-widget { --amt-font: … }.
02 · Getting Started
Quick Start
Mount on any block element, collect structured JSON, restore from saved data.
Live demo — full widget with default tags
Interactive
collectData() output
Click "collectData()" after adding some entries…
JavaScript
// 1. Mount
const widget = new ArkMetaTags('#my-div', {
onChange: data => console.log(data),
});
// 2. Collect structured JSON
const meta = widget.collectData();
// → { court: [{value:'…',note:'…'}], phone: [{values:[…]}] }
// 3. Restore from saved data
widget.loadData(savedMeta);
// 4. Other methods
widget.addTag('court'); // programmatic add
widget.reset(); // clear all
widget.destroy(); // unmount
03 · Tag Types
All Field Types
Every tag has a
type property that controls what input is rendered and what shape the collectData() entry produces.text
Single-line text input. The most common type.
→ { value: "…" }
textarea
Resizable multiline text area.
→ { value: "…" }
select
Dropdown from a fixed
opts[] array.→ { value: "…" }
chiplist
Multiple values as chips. Press Enter or comma to add.
→ { values: ["a","b"] }
keyvalue
Side-by-side label : value pair. Good for custom fields.
→ { key:"…", value:"…" }
dateNote
Date picker with a mandatory note row always shown below.
→ { date:"YYYY-MM-DD",
note:"…" }
note:"…" }
Try each type — one widget per type
Interactive
text & textarea
select
chiplist
keyvalue
dateNote
All widgets' collectData()
Click "collectData() all"…
JavaScript — type definitions
[
// text
{ key: 'witness', label: 'Witness', icon: `…`,
type: 'text', ph: 'Witness name…' },
// textarea
{ key: 'note', label: 'Internal Note', icon: `…`,
type: 'textarea', ph: 'Off-record notes…' },
// select (opts required)
{ key: 'status', label: 'Case Status', icon: `…`,
type: 'select',
opts: ['Select…', 'Under investigation', 'FIR registered'] },
// chiplist — multiple values per instance
{ key: 'phone', label: 'Phone', icon: `…`,
type: 'chiplist', ph: 'Type number, press Enter…' },
// keyvalue — field name : value
{ key: 'custom', label: 'Custom Tag', icon: `…`,
type: 'keyvalue', phKey: 'Field name…', phVal: 'Value…' },
// dateNote — date picker + always-visible note row
{ key: 'followup', label: 'Follow-up Date', icon: `…`,
type: 'dateNote', ph: 'What to follow up on…' },
]
04 · Flags
hasNote & hasDate Flags
Add an action/note sub-row or a date sub-row to any simple type without switching to multi-field mode. Both can be combined.
FIR tag — type:'text' + hasNote:true · Officer tag — text + hasDate + hasNote
Interactive
output
—
JavaScript
// hasNote — appends an "Action / note" textarea row
{ key: 'fir', label: 'FIR No.', icon: `…`,
type: 'text', ph: 'FIR No. 000/2025…',
hasNote: true }
// → { value: "FIR No. 047/2025", note: "IPC 302, 307" }
// hasDate — appends a date picker sub-row
{ key: 'officer', label: 'Officer', icon: `…`,
type: 'text', ph: 'Name, rank…',
hasDate: true,
hasNote: true }
// → { value: "SP Rajesh Kumar", date: "2025-05-13", note: "Present at scene" }
05 · Advanced
Multi-field (fields[])
For tags that need several named sub-fields per instance, replace
type with a fields array. Each field has a name, type, and optional label / ph. The collectData entry is a flat object keyed by field name.
Court hearing — each instance records: court, date, judge, outcome, links
Interactive
output — flat object per instance
—
JavaScript — fields[] definition
{
key: 'court', label: 'Court / Hearing',
icon: `<line x1="12" y1="1" x2="12" y2="23"/>…`,
fields: [
{ name: 'court', type: 'text', ph: 'Court name…' },
{ name: 'date', type: 'date', label: 'Hearing date' },
{ name: 'judge', type: 'text', ph: 'Presiding judge…',
label: 'Judge' },
{ name: 'outcome', type: 'note', ph: 'What happened…' },
{ name: 'links', type: 'chiplist', ph: 'Paste URL, Enter…' },
{ name: 'verdict', type: 'select',
opts: ['Select…', 'Pending', 'Convicted', 'Acquitted'] },
],
}
// collectData() entry shape:
// { court:'…', date:'2025-05-13', judge:'…', outcome:'…',
// links:['https://…'], verdict:'Pending' }
Field name note and date are reserved. When type:'note' or type:'date' is used inside fields[], the row renders in the note-row or date-row style (subdued italic background) regardless of its position in the array.
06 · Configuration
Custom tagDefs
Pass
tagDefs to the constructor to extend the built-in 16 tags. By default they merge by key — any tag with a matching key overrides the default; new keys are appended. Set replaceTagDefs: true to use only your tags.
Merge — adds "Journalist" tag + overrides built-in "court" with multi-field
Interactive
output
—
JavaScript — merge mode (default)
const widget = new ArkMetaTags('#host', {
tagDefs: [
// Entirely new tag (appended to strip)
{
key: 'journalist', label: 'Journalist',
icon: `<path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 013 3L7 19l-4 1 1-4z"/>`,
type: 'text', ph: 'Reporter name & outlet…',
},
// Override built-in 'court' with multi-field definition
{
key: 'court',
fields: [
{ name: 'court', type: 'text', ph: 'Court name…' },
{ name: 'date', type: 'date', label: 'Hearing date' },
{ name: 'note', type: 'note', ph: 'What happened…' },
],
},
],
// replaceTagDefs: false, ← default; set true to skip built-ins
});
JavaScript — replace mode
const widget = new ArkMetaTags('#host', {
replaceTagDefs: true, // ignore all 16 built-in tags
tagDefs: [
{ key: 'sku', label: 'SKU', icon: `…`, type: 'text' },
{ key: 'colour', label: 'Colour', icon: `…`, type: 'chiplist' },
{ key: 'expiry', label: 'Expiry', icon: `…`, type: 'dateNote' },
],
});
07 · Configuration
Options Reference
All options are passed as the second argument to the constructor.
| Option | Type | Default | Description |
|---|---|---|---|
tagDefs |
Array | null | Custom tag definitions. Merged with built-ins by key unless replaceTagDefs is true. |
replaceTagDefs |
boolean | false | When true, tagDefs completely replaces built-in defaults. No merging. |
onChange |
function | null | Called with the full collectData() object whenever an instance is added or removed. |
sectionTitle |
string | 'Details — tap to add' | Text inside the ornamental section-rule divider above the tag strip. |
hint |
string | 'Tap a tag to add…' | Small italic hint line below the section title. |
showRule |
boolean | true | Show or hide the ornamental section-rule line above the tag strip. |
injectStyles |
boolean | true | Auto-inject the library's CSS into <head>. Disable if you manage styles yourself. |
JavaScript — all options
new ArkMetaTags('#host', {
tagDefs : [], // extend / replace built-in tags
replaceTagDefs: false, // true = use tagDefs only
onChange : meta => {}, // structural-change callback
sectionTitle : 'Add details', // section-rule label
hint : 'Tap to add', // italic hint below title
showRule : true, // show/hide ornamental divider
injectStyles : true, // auto-inject library CSS
});
08 · API
Methods
All methods are on the instance returned by
new ArkMetaTags(…).| Method | Returns | Description |
|---|---|---|
collectData() |
object | Returns a plain object keyed by tag key, each value an array of instance objects. See formats per type above. |
loadData(meta) |
void | Clears all entries, then re-renders from a collectData()-shaped object. Inverse of collectData. |
addTag(key) |
void | Programmatically add one instance of the tag with key key. Same as tapping the tag chip. |
reset() |
void | Remove all instances and groups. Fires ark-meta:change with empty data. |
destroy() |
void | Unmount: empties the container element. Call before removing the container from the DOM. |
ArkMetaTags.version |
string | Static property — current library version string. |
ArkMetaTags.defaultTagDefs |
Array | Static property — the full array of 16 built-in tag definitions. Useful for inspection or partial cloning. |
Methods playground — call any method and observe the widget
Interactive
result
—
JavaScript — all methods
const w = new ArkMetaTags('#host', { /* options */ });
// Read all entries
const data = w.collectData();
// → { court:[{value,note}], phone:[{values:[…]}], … }
// Restore (clear first, then fill)
w.loadData(data);
// Programmatic add
w.addTag('court'); // adds one instance, same as clicking tag chip
// Clear everything
w.reset();
// Unmount (empties container)
w.destroy();
// Static
console.log(ArkMetaTags.version); // '1.0.0'
console.log(ArkMetaTags.defaultTagDefs); // [ {key,label,…}, … ]
09 · API
Events
All events are dispatched on the host container element with
bubbles: true — so you can also listen on a parent. Every event's e.detail includes instance — the live ArkMetaTags object.| Event | e.detail | Fired when |
|---|---|---|
ark-meta:add |
{ key, iid, instance } |
A new instance is added (tag chip tapped or addTag() called). |
ark-meta:remove |
{ key, iid, instance } |
An instance row is deleted (× button clicked). |
ark-meta:change |
{ data, instance } |
Any structural change (add, remove, or reset). data is the full collectData() object. |
onChange vs ark-meta:change — They fire at the same time. Use the onChange constructor option when you want a clean closure. Use DOM events when you need to listen from outside the constructor, or bubble up to a parent.
Live event log — interact with the widget and watch events fire in real time
Events
Event log
Waiting for events — add or remove a tag above…
JavaScript — all events
const host = document.getElementById('meta-host');
const w = new ArkMetaTags(host, {
onChange(data) {
// Fires on every add / remove / reset
console.log('onChange:', data);
},
});
// Or listen via DOM events:
host.addEventListener('ark-meta:add', e => {
console.log('Added:', e.detail.key, 'iid:', e.detail.iid);
// e.detail.instance → the ArkMetaTags object
});
host.addEventListener('ark-meta:remove', e => {
console.log('Removed:', e.detail.key, 'iid:', e.detail.iid);
});
host.addEventListener('ark-meta:change', e => {
// e.detail.data = full collectData() output
saveToServer(e.detail.data);
});
10 · Testing
ArkTestData — Companion Test Library
ark-test-data.js generates realistic Indian news-reporter data. It covers all 16 tag keys with correctly-shaped random values so you can seed, test, and reload the widget without touching real data.| Method | Returns | Description |
|---|---|---|
generateCase() | object | One full case object: title, location, meta (all tag keys), body HTML, reporter, priority… |
generateCases(n) | Array | Array of N cases with unique IDs and staggered dates. |
generateMeta() | object | Only the meta portion — compatible with widget.loadData(). |
generateLocation() | object | Random Indian location with city, state, GPS, pincode, police station. |
seedLocalStorage(n) | Array | Generates N cases and writes them to localStorage['nt_drafts']. Appends by default. |
clearLocalStorage() | void | Removes localStorage['nt_drafts']. |
consoleReport(cases) | void | Prints a console.table summary of a cases array. |
smokeTest() | {pass,fail} | Runs ~20 assertions. Logs ✓/✗ per assertion. Returns pass/fail counts. |
corpus | object | Exposes raw data arrays: CITIES, STATES, HEADLINES, IPC_SECTIONS, etc. |
rng | object | Exposes random helpers: rng.pick(arr), rng.int(min,max), rng.name(), rng.phone()… |
Test data generators — live output
ArkTestData
output
Click any button above…
Widget loaded from generateMeta()
JavaScript — ArkTestData usage
// Generate and inspect
const singleCase = ArkTestData.generateCase();
const metaOnly = ArkTestData.generateMeta();
const tenCases = ArkTestData.generateCases(10);
// Load random data into any ArkMetaTags widget
widget.loadData(ArkTestData.generateMeta());
// Seed localStorage (used by cases-list.html)
ArkTestData.seedLocalStorage(20); // append 20
ArkTestData.seedLocalStorage(5, true); // replace with 5
ArkTestData.clearLocalStorage();
// Console report
ArkTestData.consoleReport(tenCases);
// Use corpus for custom generators
const city = ArkTestData.rng.pick(ArkTestData.corpus.CITIES);
const phone = ArkTestData.rng.phone();
const name = ArkTestData.rng.name();
11 · Testing
Smoke Test
Run
ArkTestData.smokeTest() to verify the data generator and localStorage integration. The test runner below runs in the page — no console required.
ArkTestData.smokeTest() — click to run all assertions
Test runner
JavaScript — integration test pattern
// Integration test: generate → loadData → collectData → verify shapes
function integrationTest(widget) {
const meta = ArkTestData.generateMeta();
widget.loadData(meta); // fill the widget
const out = widget.collectData(); // read it back
// court entries must have value and note
if (out.court) {
out.court.forEach(e => {
assert('court has value', 'value' in e);
assert('court has note', 'note' in e);
});
}
// phone must have values array
if (out.phone) {
assert('phone values is array', Array.isArray(out.phone[0].values));
}
// custom must have key + value
if (out.custom) {
out.custom.forEach(e => {
assert('custom has key', 'key' in e);
assert('custom has value', 'value' in e);
});
}
// followup must have date + note
if (out.followup) {
out.followup.forEach(e => {
assert('followup has date', typeof e.date === 'string');
assert('followup has note', typeof e.note === 'string');
});
}
// round-trip: loadData then reset should give empty
widget.reset();
const empty = widget.collectData();
assert('reset gives empty object', Object.keys(empty).length === 0);
}