# JFast Javascript Library

### 1 . Why jFast?

| Attribute                 | What it means                                                                   |
| ------------------------- | ------------------------------------------------------------------------------- |
| **Tiny footprint**        | ≈ 2 KB gzipped – ideal for embed widgets, dashboards, or legacy projects.       |
| **jQuery‑style API**      | 80 % coverage of the helpers devs reach for most (`addClass`, `on`, `ajax`, …). |
| **No build‑step**         | Ship the un‑minified file for debugging; run your own minifier if desired.      |
| **Polyfill friendly**     | Bundles fallbacks for `Set` and `NodeList.forEach` so IE11 gets basic support.  |
| **Modern under the hood** | Uses `querySelectorAll`, `classList`, and Promises.                             |

***

### 2 . Installation

```html
<!-- 1. Download jfast‑1.2.3.js and place it in /assets/js -->
<script src="/assets/js/jfast-1.2.3.js"></script>

<!-- 2. Or, serve from your own CDN / S3 bucket -->
<script src="https://cdn.your‑domain.com/lib/jfast/1.2.3/jfast.min.js"></script>
```

When the script loads you’ll see a yellow console log:

```
jFast loaded version 1.2.3
```

and two globals become available:

```js
console.log(window.jFast === window.$);   // true
```

### 3 . Creating a Collection

Below are **all** accepted constructor signatures, plus runnable snippets you can paste into DevTools.

| Call               | Typical Use Case                      | Example                                                                         |
| ------------------ | ------------------------------------- | ------------------------------------------------------------------------------- |
| `jFast('section')` | Select DOM by CSS                     | `$('.box').addClass('open')`                                                    |
| `jFast(domNode)`   | Wrap an existing DOM node             | `jconst btn = document.getElementById('send');\n$(btn).attr('disabled', true);` |
| `jFast(NodeList)`  | Hand‑off result of `querySelectorAll` | `const nodes = document.querySelectorAll('.note');\n$(nodes).hide();`           |
| `jFast([el1,el2])` | Wrap an array of nodes                | `$( [header, footer] ).css('background', '#333');`                              |
| `jFast(function)`  | DOM‑ready callback                    | `$(function(){ console.log('DOM ready'); });`                                   |
| *no value*         | Empty collection (edge cases)         | `jFast().length === 0;`                                                         |

**Quick demo**

```html
<ul id="tasks">
  <li>Write docs</li>
  <li class="done">Ship release</li>
</ul>
<script>
  $('#tasks li:not(.done)')
    .addClass('pending')
    .each((i, li) => console.log('Todo #'+i, li.textContent));
</script>
```

### 4 . Core Iterator APIs

**4.1 `.each(callback)`**

Iterate the wrapped set. Unlike plain `forEach`, the callback is executed with `(element, index)` to match jQuery style.

```js
$('a.external').each((i, a) => a.target = '_blank');
```

**4.2 `.eq(index)`**

Returns a new jFast collection containing just the element at *index*. Negative indices count from the end.

```js
// Hide the last column in a table
$('table tr').find('td').eq(-1).hide();
```

**4.3 `.first()` / `.last()`**

Sugar for `.eq(0)` and `.eq(length - 1)`.

```js
$('.slide').last().addClass('current');
```

***

### 5 . Class Helpers

| Method                         | Purpose                           | Minimal Demo                                |
| ------------------------------ | --------------------------------- | ------------------------------------------- |
| `.addClass(names)`             | Add one or more classes           | `$('#msg').addClass('show animated');`      |
| `.removeClass(names)`          | Remove class(es)                  | `$('nav').removeClass('sticky');`           |
| `.toggleClass(names, [state])` | Flip class or force boolean state | `$('.panel').toggleClass('open');`          |
| `.hasClass(name)`              | Boolean test on first element     | `if ( $('#modal').hasClass('open') ) {...}` |

**Practical scenario**

```js
// click to open/close FAQ answers
$('.faq-question').on('click', function () {
  $(this)
    .next('.faq-answer')
    .toggleClass('hidden');
});
```

***

### 6 . Attribute & Property Utilities

**6.1 `.attr(name)` / `.attr(name, value)`**

```js
// Read
const src = $('img.preview').attr('src');

// Write
$('img.preview').attr('alt', 'Screenshot');
```

*Remove an attribute*:

```js
$('input.temp').attr('autocomplete', null);
```

**6.2 `.prop(name[, value])`**

Directly modify DOM properties (not string attributes):

```js
$('input[type=checkbox]').prop('checked', false);
```

**6.3 `.data(key[, value])`**

Free of JSON parsing/serialisation:

```html
<div id="card" data-id="9810"></div>
<script>
  $('#card').data('id');       // "9810"
  $('#card').data('state', 'archived');
</script>
```

***

### 7 . Text, HTML, and Inline CSS

| Getter       | Setter              | Sample                                            |
| ------------ | ------------------- | ------------------------------------------------- |
| `.text()`    | `.text(value)`      | `$('#status').text('Saving…');`                   |
| `.html()`    | `.html(markup)`     | `$('#list').html('<li>New item</li>');`           |
| `.css(prop)` | `.css(prop, value)` | `$('#box').css('width', '250px');`                |
|              | `.css(object)`      | `$('#box').css({height:120, background:'#eef'});` |

### 8 . DOM Insertion & Removal

| Method                | What it does                                                                              | Live Example                                                                          |
| --------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `.append(content)`    | Add *content* to **inside‑end** of every element in the set.                              | `$('#log').append('<li>Finished at '+Date.now()+'</li>');`                            |
| `.prepend(content)`   | Add *content* to **inside‑start**.                                                        | `$('ul').prepend('<li class=\"first\">Top item</li>');`                               |
| `.before(content)`    | Insert *content* **before** each element.                                                 | `$('.alert').before('<hr>');`                                                         |
| `.after(content)`     | Insert *content* **after** each element.                                                  | `$('.alert').after('<button class=\"close\">×</button>');`                            |
| `.remove()`           | Detach each element from the DOM. Returns the same collection (now empty).                | `$('.toast .close').on('click', e => $(e.delegateTarget).parent('.toast').remove());` |
| `.empty()`            | **Clear** `.innerHTML` of every element but keep the elements themselves.                 | `$('#results').empty(); // ready for fresh data`                                      |
| `.clone([deep=true])` | Produce a *new* jFast collection containing clones. `deep=false` copies only the element. | `const $copy = $('#card').clone();\n$('#cards').append($copy);`                       |

> **Tip — Passing existing nodes**\
> All insertion helpers accept **strings, HTMLElement(s), NodeLists, or jFast objects**. If you pass DOM nodes, the library clones them when needed so every target gets its own copy.

```js
$tmpl = $('#tmpl .item');      // template item stored off‑DOM
$('.list').append( $tmpl.clone() );  // safe – original remains intact
```

### 9 . Geometry & Scrolling Helpers

These methods read or write layout‑related values; none trigger a reflow more than necessary.

**9.1 `.width([value])` & `.height([value])`**

*Getter* – returns the element’s **content box** size (ignores padding, border, margin).\
\&#xNAN;*Setter* – accepts number (pixels) or any valid CSS length string.

```js
// Read
const modalW = $('#modal').width();          // e.g. 512

// Write absolute px
$('#sidebar').width(240);

// Write responsive unit
$('#sidebar').css('width', '33.333%');       // or use .css directly
```

**9.2 `innerWidth()` / `innerHeight()`**

Returns **content + padding + border** (matches `jQuery.innerWidth`). Useful for box‑model arithmetic:

```js
if ( $box.innerHeight() < 200 ) $box.height(200);
```

**9.3 `outerWidth([includeMargin])` / `outerHeight([includeMargin])`**

Adds margin when the optional boolean is `true`.

```js
const cardTotal = $('.card').outerHeight(true);  // full footprint
```

**9.4 `.offset()`**

Absolute page coordinates *(pixels from viewport’s top‑left, including scroll)*:

```js
const pos = $('#avatar').offset(); // {top:123, left:456}
window.scrollTo({ top: pos.top - 20, behavior:'smooth' });
```

**9.5 `.position()`**

CSS `position` relative to **offsetParent** (similar to jQuery):

```js
const { top, left } = $('#tooltip').position();
console.log('Tooltip is', top, 'px below its anchor');
```

**9.6 `.offsetParent()`**

Wraps and returns the element’s offset parent or `document.documentElement`.

```js
$('#badge').offsetParent().css('outline','1px dashed red');
```

**9.7 `scrollTop([value])` / `scrollLeft([value])`**

Works on both **elements** and **`window`**.

```js
// Get current scroll
const y = $(window).scrollTop();

// Scroll container to bottom
$('#chat').scrollTop( $('#chat')[0].scrollHeight );
```

**9.8 `.index([element])`**

*No argument* → index of first element within its parent.\
\&#xNAN;*Element argument* → index of that element within the wrapped set.

```js
const clickedCol = $(e.target).index();         // which column?
if ( clickedCol === 0 ) sortBy('name');
```

### 10 . Traversal Helpers

| Method                | Returned collection                         | Typical Use                          | Example                                           |
| --------------------- | ------------------------------------------- | ------------------------------------ | ------------------------------------------------- |
| `.find(sel)`          | **All** descendants matching `sel`.         | Narrow to deeper nodes.              | `$('#nav').find('a.active')`                      |
| `.closest(sel)`       | First ancestor (self‑incl.) matching `sel`. | Bubble upward until match.           | `$(btn).closest('form').submit()`                 |
| `.parent()`           | The direct parent for every element.        | Jump one level up.                   | `$('.item').parent().addClass('has‑item')`        |
| `.parents()`          | **All** ancestors for every element.        | Apply global change along the chain. | `$('#hero').parents().css('position','relative')` |
| `.children()`         | Direct child elements of each node.         | Loop through immediate kids.         | `$('#menu').children().addClass('link')`          |
| `.next()` / `.prev()` | Immediate next/previous sibling.            | Carousel, tab order, etc.            | `$('.tab.active').next().click()`                 |
| `.siblings()`         | All siblings except the element itself.     | Row/column highlighting.             | `$(cell).siblings().addClass('dim')`              |
| `.filter(sel)`        | Elements that **pass** selector test.       | Refine a set post‑selection.         | `$('li').filter('.active, .current')`             |
| `.not(sel)`           | Elements that **fail** selector test.       | Exclude unwanted nodes.              | `$('li').not('.disabled')`                        |
| `.is(sel)`            | **Bool** – does the *first* element match?  | Conditionals.                        | `if ( $(el).is(':visible') ) …`                   |
| `.add(sel)`           | Union of current set + `sel`.               | Merge disparate selections.          | `$('.btn').add('#save')`                          |

***

**10.1 `.find(selector)`**

Searches **below** every element in the current set.

```html
<nav id="mainNav">
  <a href="/">Home</a>
  <a href="/shop" class="active">Shop</a>
</nav>
<script>
  $('#mainNav')
    .find('a.active')
    .css('font-weight',700); // only one link affected
</script>
```

> **Performance tip** – `find()` re‑collects, so keep the result in a variable if you reuse it.

***

**10.2 `.closest(selector)`**

Works like native `Element.closest`. Includes the element itself when checking.

```js
// inside a click handler on the document
$(e.target)
  .closest('[data-modal]')
  .addClass('open');
```

***

**10.3 `.parent()` & `.parents()`**

`parent()` climbs **one** level.\
`parents()` returns *every* ancestor up to `<html>`.

```js
$('.note').parent().addClass('has-note');

$('.btn-save')
  .parents('section')        // only ancestors that are <section>
  .addClass('has-unsaved');
```

***

**10.4 `.children()`**

```html
<ul id="files">
  <li>index.html</li>
  <li>app.js</li>
</ul>
<script>
  $('#files').children().each((i, li) => console.log(li.textContent));
</script>
```

***

**10.5 `.next()` & `.prev()`**

```js
$('.wizard-step.active')
  .next()       // the step after the current one
  .addClass('active')
  .prev()       // go back to the previously active
  .removeClass('active');
```

***

**10.6 `.siblings()`**

```js
$('.gallery img.selected')
  .siblings()
  .css('opacity',0.3);
```

***

**10.7 `.filter()`, `.not()`, `.is()`**

```js
// Disable all but visible inputs
$('input').not(':visible').prop('disabled', true);

// Count only checked boxes
const checked = $('input[type=checkbox]').filter(':checked').length;

// Detect whether the page uses a dark theme
if ( $('body').is('.dark') ) enableNightMode();
```

***

**10.8 `.add(selector)`**

Merge two unrelated selections into one chainable set.

```js
$('.toolbar button')
  .add('#globalSave')
  .on('click', saveDocument);
```

***

#### Mini Project Example – Building a Stepper

Below is a concise snippet that combines traversal helpers to implement a step‑by‑step UI.

```html
<ol id="steps">
  <li class="step current">Choose plan</li>
  <li class="step">Billing info</li>
  <li class="step">Confirm</li>
</ol>
<button id="next">Next</button>

<script>
  $('#next').on('click', () => {
    const $current = $('#steps .current');
    $current.removeClass('current');
    $current.next('.step').addClass('current');
    
    // If we reached the end, disable the button
    if ( $('#steps .step').last().is('.current') ) {
      $('#next').prop('disabled', true);
    }
  });
</script>
```

Traversal calls used:

* `#steps .current` – initial selection
* `.next('.step')` – sibling traversal with selector filter
* `.last()` / `.is()` – index check & boolean match

### 11 . Effects & Visibility Helpers

| Method             | Purpose                                                                 | Default Duration | Example                         |
| ------------------ | ----------------------------------------------------------------------- | ---------------- | ------------------------------- |
| `.show([ms])`      | Make the element visible (removes `display:none` and optionally fades). | *none*           | `$('#loader').show(300)`        |
| `.hide([ms])`      | Fade out then `display:none`.                                           | *none*           | `$('.alert').hide(500)`         |
| `.toggle([ms])`    | Switch between show ↔ hide.                                             | *none*           | `$('#panel').toggle(200)`       |
| `.fadeIn([ms])`    | Alias of `.show()` with default 400 ms when called without arg.         | 400 ms           | `$('img').fadeIn()`             |
| `.fadeOut([ms])`   | Alias of `.hide()` with default 400 ms.                                 | 400 ms           | `$('img').fadeOut(150)`         |
| `.slideDown([ms])` | Animate height from 0 → full.                                           | 400 ms           | `$('.faq-answer').slideDown()`  |
| `.slideUp([ms])`   | Animate height from full → 0, then `display:none`.                      | 400 ms           | `$('.faq-answer').slideUp(250)` |

**11.1 Show / Hide Example – Toast Notification**

<pre class="language-html"><code class="lang-html"><strong>&#x3C;div id="toast" class="toast">Saved!&#x3C;/div>
</strong>&#x3C;style>
  .toast{position:fixed;bottom:1rem;right:1rem;padding:1em;background:#333;color:#fff;display:none}
&#x3C;/style>
&#x3C;script>
  function flashToast() {
    $('#toast')
      .fadeIn(300)
      .delay(2000)            // custom helper below ▼
      .fadeOut(300);
  }

  // Tiny helper: chainable delay using setTimeout
  jFast.prototype.delay = function (ms) {
    return this.each(el => setTimeout(()=>{}, ms));
  };

  $('#saveBtn').on('click', flashToast);
&#x3C;/script>
</code></pre>

**11.2 Accordion with Slide Helpers**

```html
<h3 class="acc-title">Section 1</h3>
<div class="acc-body">Hidden text…</div>

<h3 class="acc-title">Section 2</h3>
<div class="acc-body">More hidden text…</div>

<script>
  $('.acc-title').on('click', function () {
    const $body = $(this).next('.acc-body');
    $('.acc-body').not($body).slideUp(); // close others
    $body.slideToggle();                 // jFast alias we can add
  });

  // Optional—extend jFast with slideToggle for convenience
  jFast.prototype.slideToggle = function (ms){
    return this.each(el => {
      const $el = $(el);
      $el.is(':visible') ? $el.slideUp(ms) : $el.slideDown(ms);
    });
  };
</script>
```

***

### 12 . Event System

jFast replicates jQuery’s flexible `.on()` signature, adds `.one()`, and keeps an **internal WeakMap** to clean listeners automatically.

**12.1 `.on(events, [selector], handler [, capture])`**

| Pattern                      | Use‑case                           | Example                                   |
| ---------------------------- | ---------------------------------- | ----------------------------------------- |
| `.on('click', fn)`           | Direct binding                     | `$('#btn').on('click', save)`             |
| `.on('keyup change', fn)`    | Multiple events space‑separated    | `$('input').on('keyup change', validate)` |
| `.on('click', '.child', fn)` | Delegation (parent stays constant) | `$('#list').on('click', 'li', editItem)`  |

```js
function editItem(e){
  // e.delegateTarget is the <li> that matched '.child'
  console.log('Editing', e.delegateTarget.textContent);
}
```

**12.2 `.one(events, [, selector], handler)`**

Runs **once**, then auto‑removes.

```js
$('#modal').one('shown', () => console.log('Modal opened for the first time only'));
```

**12.3 `.off(events, [, selector], [handler])`**

Removes listeners. Parameters mirror `.on()`. Omitting `handler` drops **all** listeners matching event + selector.

```js
$(window).off('scroll.myNS'); // if you namespaced your events
```

**12.4 `.trigger(type, [detail])`**

Dispatch custom or native events with optional `detail` payload.

```js
// Somewhere deep in a component
$(this).trigger('saved', { id: 42 });

// Elsewhere
$(document).on('saved', (e)=> console.log('Item saved!', e.detail.id));
```

> **Note**: Native bubbling rules apply, so you can trigger on a child and listen on `<body>`.

**12.5 Event Shortcut Methods**

For convenience jFast defines **typed aliases**:

```js
$('#field').focus();           // trigger focus
$('#field').focus(handler);    // bind

$('form').submit(e => e.preventDefault());
$(document).keydown(e => e.key === 'Escape' && close());
```

Available shortcuts: `click`, `change`, `keydown`, `keyup`, `keypress`, `mouseover`, `mouseout`, `mouseenter`, `mouseleave`.

***

**12.6 Real‑World: Live Search with Debounce**

```html
<input id="search" placeholder="Search…" autocomplete="off">
<ul id="results"></ul>

<script>
  // --- simple debounce utility
  function debounce(fn, wait){
    let t; return (...args)=>{ clearTimeout(t); t=setTimeout(()=>fn.apply(this,args), wait); };
  }

  $('#search').on('keyup', debounce(function(){
    const q = $(this).val().trim();
    if (!q) return $('#results').empty();

    $.getJSON('/api/search', {q})
      .success(data => {
        const html = data.results.map(r => `<li>${r.title}</li>`).join('');
        $('#results').html(html);
      });
  }, 300));
</script>
```

*Techniques used*

* Direct event binding (`keyup`)
* Reading input value with `.val()`
* Ajax shorthand `$.getJSON`
* DOM diff via `.html(html)`

***

#### Recap

**Effects handled:** show/hide, fade, slide, toggle.\
**Events mastered:** `.on`, `.one`, `.off`, `.trigger`, plus typed shortcuts & delegation patterns.

### 13 . Form Helpers

| API                      | Read / Write                                                                                                                                | Quick Example                                 |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `.val()` / `.val(value)` | Get or set an element’s value. Works on `<input>`, `<select>`, `<textarea>`, check‑/radio boxes (boolean support), and multi‑select arrays. | `$('#email').val('user@example.com');`        |
| `.serialize()`           | Return URL‑encoded query string from the first `<form>` in the set.                                                                         | `$('form').serialize(); // "name=Bob&age=28"` |
| `.serializeArray()`      | Return array `[{name:'', value:''}, …]`.                                                                                                    | `console.table($('form').serializeArray());`  |
| `.submit()`              | Programmatically send a form (respects `requestSubmit` when present). Passing a submit button as the jFast object triggers that button.     | `$('#myForm').submit();`                      |

**13.1 `.val()` in Depth**

```html
<select id="lang" multiple>
  <option value="en" selected>English</option>
  <option value="vi" selected>Vietnamese</option>
  <option value="jp">Japanese</option>
</select>

<script>
  // Get → returns array for multi‑select
  const langs = $('#lang').val();      // ["en","vi"]

  // Set (array) → selects those options
  $('#lang').val(['jp']);
</script>
```

Checkbox toggle:

```js
// set boolean
$('#notify').val(true);          // checks it
$('#notify').val(false);         // unchecks
```

**13.2 AJAX Submit with `.serialize()`**

```html
<form id="login">
  <input name="user" placeholder="Username">
  <input name="pass" type="password">
  <button type="submit">Login</button>
</form>

<script>
  $('#login').on('submit', e => {
    e.preventDefault();                     // stop native redirect
    const qs = $(e.currentTarget).serialize();
    $.post('/api/login', qs)
      .success(res  => console.log('OK', res))
      .fail   (err  => alert('Bad credentials'));
  });
</script>
```

### 14 . Ajax in Depth — Working With Remote Data

jFast’s Ajax layer shadows jQuery’s API **line‑for‑line** yet trims weight by delegating to the native `XMLHttpRequest`. You may call it at three levels of abstraction:

1. Low‑level **`$.ajax( settings )`** – full control.
2. Semantic helpers **`$.get / $.post / $.getJSON`** – 90 % of daily tasks.
3. Chainable XHR “deferred” object – classic jQuery style (`done / fail / always`) *plus* promise (`then / catch / finally`).

Below is a complete reference with production‑grade snippets.

***

#### 14.1 `$.ajax( settings )` — Master Control

**14.1.1 Settings Object Cheat‑Sheet**

| Key                    | Type / Default                                     | Description                                                  |
| ---------------------- | -------------------------------------------------- | ------------------------------------------------------------ |
| `url`                  | **string** / *required*                            | Endpoint.                                                    |
| `type`                 | `"GET"`                                            | HTTP verb (`"POST"`, `"PUT"`, …).                            |
| `data`                 | object \| string \| `FormData`                     | Will be encoded automatically unless `FormData`.             |
| `dataType`             | `"json" \| "html" \| "script"`                     | Guides auto‑parsing & `Accept` header (falls back to `*/*`). |
| `contentType`          | `application/x-www-form-urlencoded; charset=UTF-8` | `false` = let the browser pick (required for `FormData`).    |
| `async`                | `true`                                             | Set `false` only for legacy sync calls (blocks UI).          |
| `headers`              | object                                             | Extra request headers.                                       |
| `username`, `password` | string                                             | For HTTP basic auth.                                         |
| Callbacks              | `beforeSend`, `success`, `error`, `complete`       | Same signatures as jQuery.                                   |

> **Encoding Rules**
>
> * If `data` is **plain object** → encoded via `$.param()` unless `contentType` starts with `application/json`.
> * If `type` is *GET/HEAD/DELETE* and `data` is not `null` → appended to the URL query string.
> * If `data` is `FormData` → jFast will *not* set `Content‑Type` so the boundary is correct.

**14.1.2 Full Example – PUT JSON With Auth Header**

```js
const profile = { bio: 'Hello there', age: 30 };

$.ajax({
  url: '/api/v1/users/42',
  type: 'PUT',
  data: JSON.stringify(profile),
  contentType: 'application/json',
  dataType: 'json',
  headers: { 'Authorization': 'Bearer ' + token },
  beforeSend(xhr) { console.log('Uploading…'); },
  success(data, status, xhr) { console.log('Saved!', data); },
  error(xhr, status, text) { console.error('Server said:', text); },
  complete(xhr, status) { console.log('Request finished:', status); }
});
```

**14.1.3 Promise Interface (& Abort)**

```js
const req = $.ajax('/search?q=cat')
  .then(res  => render(res.items))
  .catch(e   => alert('Network problem: ' + e.status))
  .finally(() => spinner.hide());

// Abort button
$('#cancel').on('click', () => req.abort && req.abort());
```

> The returned XHR object is **augmented** with both promise methods *and* jQuery‑style `done / fail / always`.

**14.1.4 Monitoring Upload / Download Progress**

```js
$.ajax({
  url: '/upload',
  type: 'POST',
  data: new FormData($('#fileForm')[0]),
  contentType: false,
  beforeSend(xhr){
    xhr.upload.onprogress = e => {
      if (e.lengthComputable){
        const pct = (e.loaded / e.total * 100) | 0;
        $('#bar').css('width', pct+'%');
      }
    };
  }
});
```

***

#### 14.2 Helper Shortcuts

Helpers internally call `$.ajax` but save keystrokes. **All optional arguments may be omitted in order**; jFast adjusts.

| Helper                                          | Signature (flexible)      | Internals                  |
| ----------------------------------------------- | ------------------------- | -------------------------- |
| `$.get(url [, data] [, success] [, dataType])`  | Always *GET*.             | `$.ajax({type:'GET', …})`  |
| `$.post(url [, data] [, success] [, dataType])` | Always *POST*.            | `$.ajax({type:'POST', …})` |
| `$.getJSON(url [, data] [, success])`           | Forces `dataType:'json'`. | `$.get(… , 'json')`        |

**14.2.1 GET With Query Object**

```js
$.get('/api/products', { page: 2, tag: 'book' }, buildGrid, 'html');
```

**14.2.2 POST Form (x‑www‑form‑urlencoded)**

```js
$.post('/login', { user, pass })
  .done(()  => location.reload())
  .fail(()  => shakeForm());
```

**14.2.3 GET JSON via `$.getJSON`**

```js
$.getJSON('/weather', { city: 'Singapore' })
  .then(({ temp, icon }) => $('#temp').text(temp + '°C')
                                      .attr('class', icon));
```

***

#### 14.3 Using FormData for File Uploads

```html
<form id="avatarForm">
  <input type="file" name="avatar">
  <button>Upload</button>
</form>

<script>
$('#avatarForm').on('submit', e => {
  e.preventDefault();
  const formData = new FormData(e.currentTarget);

  $.ajax({
    url: '/api/upload/avatar',
    type: 'POST',
    data: formData,
    contentType: false,   // *DO NOT OVERRIDE* for FormData
    processData: false,   // jFast handles this automatically
    dataType: 'json'
  })
  .success(data => alert('URL: ' + data.url))
  .error  (()  => alert('Upload failed'));
});
</script>
```

***

#### 14.4 Error‑Handling Patterns

| Scenario                    | Strategy                                                                                                               |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| **Validation errors (422)** | In `error` callback, check `xhr.status === 422` then read `JSON.parse(xhr.responseText)` to show field‑level messages. |
| **Timeout / offline**       | Use `xhr.status === 0` or wrap the promise in a `Promise.race([ajax, timeout])`.                                       |
| **Retry**                   | Write a helper: `function withRetry(fn, n){ return fn().catch(e => n>0 ? withRetry(fn, n‑1) : Promise.reject(e)); }`.  |

***

#### 14.5 Quick Reference

```
$.ajax(settings)    // full power
$.get(url[,data][,success][,type])
$.post(url[,data][,success][,type])
$.getJSON(url[,data][,success])

XHR methods: then, catch, finally, done, fail, always, success, error, complete, abort
```

*With this toolkit you can cover everything from vanilla REST endpoints to streaming uploads in a few lines of code.*

### 15 . Why Static Utilities?

Everything in this chapter is **independent of a wrapped DOM collection**; you can feed plain arrays, NodeLists, or objects. jFast purposely mirrors jQuery naming so muscle memory applies.

***

**15.1 `jFast.each( collection, callback )`**

*Iterates any array‑like structure.*

```js
const colors = ['teal', 'pink', 'orange'];

$.each(colors, (idx, val) => console.log(idx, val.toUpperCase()));
// 0 TEAL · 1 PINK · 2 ORANGE
```

Characteristics:

* Works on **NodeList** directly (no spread required).
* Callback signature is `(index, item)` like jQuery (not `(item,index)`).

***

**15.2 `jFast.map( collection, callback )`**

*Transforms each item; returns **new array**.*

```js
const lens = $.map(document.querySelectorAll('p'), p => p.textContent.length);
console.log(lens); // e.g. [25, 18, 42]
```

If the callback returns `null` or `undefined`, the item is *skipped* (parity with jQuery).

***

**15.3 `jFast.grep( array, predicate )`**

*Filters items; returns **new array**.*

```js
const nums   = [5, 8, 9, 13, 21, 22];
const primes = $.grep(nums, n => [2,3,5,7,11,13,17,19,23].includes(n));
```

> **Tip** – Because `grep` creates a new array, it’s ideal for immutable Redux‑style flows.

***

**15.4 `jFast.inArray( item, array )`**

Returns **index** (`>=0`) or `-1`.

```js
if ($.inArray('admin', user.roles) === -1) denyAccess();
```

***

**15.5 `jFast.param( object [, prefix] )`**

Serialises nested objects the **PHP / Rails‑style** way.

```js
const criteria = {
  sort:  'date',
  range: { from: '2025-01-01', to: '2025-06-30' },
  tags:  ['node', 'api']
};

const qs = $.param(criteria);
console.log(qs);
// "sort=date&range[from]=2025-01-01&range[to]=2025-06-30&tags[0]=node&tags[1]=api"

fetch('/search?' + qs).then(r => r.json());
```

***

**15.6 `jFast.extend( target, …sources )` — Deep Merge Engine**

Unlike a shallow `Object.assign`, jFast’s `extend` **recurses into nested objects** but leaves arrays intact.

```js
const defaults = {
  ui:   { theme: 'light', density: 'comfortable' },
  rate: 1000
};
const userCfg  = { ui: { density: 'compact' } };

const finalCfg = $.extend({}, defaults, userCfg);
/* finalCfg === {
     ui:   { theme: 'light', density: 'compact' },
     rate: 1000
   } */
```

**15.6.1 Adding Prototype Plugins**

Because `extend` is exported, you can patch either the global `jFast` **or** its prototype.

```js
// Static helper
$.extend($, {
  random(min, max){ return Math.floor(Math.random()*(max-min+1))+min; }
});

// Chainable instance helper
$.extend($.prototype, {
  flash(ms = 150){
    return this.each(el => {
      const orig = el.style.transition;
      el.style.transition = 'background ' + ms + 'ms';
      el.style.background = '#ffe66d';
      setTimeout(() => { el.style.background=''; el.style.transition=orig; }, ms);
    });
  }
});

// Usage
console.log($.random(1,6)); // dice roll
$('.btn-save').flash();
```

***

**15.7 Utility Comparison Table**

| Use‑case            | Vanilla ES                  | jFast Utility              | Snippet                       |
| ------------------- | --------------------------- | -------------------------- | ----------------------------- |
| Iterate NodeList    | `Array.from(nodes).forEach` | `$.each(nodes, fn)`        | `$.each(document.links, cb)`  |
| Map values          | `[...arr].map`              | `$.map(arr, fn)`           | `$.map($('li'), li => li.id)` |
| Deep‑merge configs  | `structuredClone` + custom  | `$.extend`                 | `$.extend({}, def, opt)`      |
| Encode query string | `URLSearchParams`           | `$.param` (handles nested) | `$.param({page:2})`           |

***

#### What’s Next?

With Static Utilities mastered you have:

* Collection‑agnostic helpers (`each`, `map`, `grep`).
* Query‑string encoding & deep merges.
* A building block (`extend`) for your own plugins.

### 16 . Authoring Plug‑ins

jFast keeps the **exact extension workflow** you know from jQuery: augment either `jFast.prototype` (for chainables) or `jFast` itself (for statics). Everything hinges on `jFast.extend`.

**16.1 Guidelines**

| Rule                                                                   | Rationale                                     |
| ---------------------------------------------------------------------- | --------------------------------------------- |
| Return `this` to stay chainable.                                       | Keeps plug‑in composable with core helpers.   |
| Store per‑element state in `el.dataset` or a `WeakMap`.                | Avoids leaking memory when nodes are removed. |
| Use a unique *data‑key namespace* (e.g. `"popover-active"`).           | Prevents collisions between plug‑ins.         |
| Accept an **options object** & merge with defaults via `jFast.extend`. | Enables declarative config + sane fall‑backs. |

**16.2 Micro‑Plug‑in Example — `highlight()`**

```js
(function ($) {
  const defaults = { color: '#fffd54', duration: 600 };

  $.extend($.prototype, {
    highlight(opts){
      opts = $.extend({}, defaults, opts);
      return this.each(el => {
        const orig   = getComputedStyle(el).backgroundColor;
        el.style.transition = `background ${opts.duration}ms`;
        el.style.background = opts.color;
        setTimeout(() => el.style.background = orig, opts.duration);
      });
    }
  });
})(jFast);

// usage
$('table tr.error').highlight({ color:'#ff8080' });
```

**16.3 Feature‑Rich Plug‑in Template (Modal)**

```js
/* modal.js – UMD style */
(function (root, factory) {
  if (typeof define === 'function' && define.amd) define(['jFast'], factory);
  else factory(root.jFast);
}(this, function ($) {

  const DATA_KEY = 'modalInstance';
  const defaults = { closeBtn: true, esc: true, backdrop: true };

  class Modal {
    constructor(el, opts){
      this.$el  = $(el);
      this.opts = $.extend({}, defaults, opts);
      this.bind();
    }
    bind(){
      if (this.opts.closeBtn){
        this.$el.find('[data-dismiss="modal"]').on('click', ()=> this.hide());
      }
      if (this.opts.esc){
        $(document).on('keydown.modal', e => e.key==='Escape' && this.hide());
      }
    }
    show(){
      this.$el.fadeIn(200).attr('aria-hidden', false);
      $(document.body).addClass('modal-open');
    }
    hide(){
      this.$el.fadeOut(150).attr('aria-hidden', true);
      $(document.body).removeClass('modal-open');
    }
    toggle(){ this.$el.is(':visible') ? this.hide() : this.show(); }
  }

  /* jFast bridge */
  $.extend($.prototype, {
    modal(cmdOrOpts){
      return this.each(el => {
        let inst = $(el).data(DATA_KEY);
        if (!inst){
          inst = new Modal(el, typeof cmdOrOpts==='object' ? cmdOrOpts : {});
          $(el).data(DATA_KEY, inst);
        }
        if (typeof cmdOrOpts === 'string'){
          inst[cmdOrOpts] && inst[cmdOrOpts]();
        }
      });
    }
  });
}));
```

Usage:

```js
$('#myModal').modal({ backdrop:false });
$('#launch').on('click', () => $('#myModal').modal('show'));
```

***

#### 16.4 Testing jFast Plug‑ins with Jest + jsdom

1. **Install Dev Deps**

```bash
npm i --save-dev jest @jest-environment-jsdom
```

2. **Setup Global `$` in `jest.setup.js`**

```js
require('../dist/jfast');           // expose global jFast
global.$ = global.jFast;
```

```json
// jest.config.json
{
  "setupFiles": ["<rootDir>/test/jest.setup.js"],
  "testEnvironment": "jsdom"
}
```

3. **Write a Spec**

```js
describe('highlight plug‑in', () => {
  document.body.innerHTML = '<p id="a">text</p>';
  
  test('changes background then reverts', done => {
    const $p = $('#a');
    $p.highlight({ color:'#abc', duration:50 });

    expect($p.css('background')).toBe('rgb(170, 187, 204)');
    
    setTimeout(() => {
      expect($p.css('background')).toBe('');
      done();
    }, 60);
  });
});
```

4. **Run**

```bash
npx jest
```

jsdom simulates enough of CSSOM for simple animation assertions; for layout you might stub `getBoundingClientRect`.

***

#### 16.5 Performance Best‑Practices

| Topic                          | Recommendation                                                                                  | Why                                           |
| ------------------------------ | ----------------------------------------------------------------------------------------------- | --------------------------------------------- |
| **Batch DOM writes**           | Build HTML strings or `DocumentFragment`, then `.append()` once.                                | Cuts reflow counts.                           |
| **Delegate events**            | Attach one `on('click', selector, …)` at container level instead of many direct listeners.      | Lower memory & faster diff when nodes mutate. |
| **Avoid synchronous Ajax**     | `async:false` blocks the UI; keep default `true`.                                               | Network stalls freeze the main thread.        |
| **Measure**                    | Drop `console.time()` around chains or use Chrome DevTools Performance tab.                     | Data > guesswork.                             |
| **Destroy observers**          | If a plug‑in uses `ResizeObserver` or `MutationObserver`, disconnect in `remove()` or `hide()`. | Prevents leaks on SPA route change.           |
| **Minify for production**      | jFast is readable by design—pass through Terser to shave \~35 %.                                | Smaller payload → TTI gains.                  |
| **Tree‑shake unused plug‑ins** | Author plug‑ins as separate files so bundlers can exclude them.                                 | Keeps your vendor chunk tiny.                 |

**Example – Batch Update vs. O(n) Loop**

```js
// baseline: O(n) reflow
$('li').each((i, el) => $(el).addClass('done'));

// optimized: CSS class on the parent
$('#todo').addClass('all-done');
```

***

#### 16.6 Performance Snippet: Virtualized List (100 k rows)

```js
function mountVirtual($container, rows){
  const rowH = 30, buffer = 10;
  $container.height(rows.length * rowH);     // big scroll area

  const $viewport = $('<div class="viewport"></div>').appendTo($container.parent());

  function render(){
    const top = $container.parent().scrollTop();
    const start = Math.max(0, Math.floor(top / rowH) - buffer);
    const end   = Math.min(rows.length, start + Math.ceil($viewport.height()/rowH) + buffer*2);
    const slice = rows.slice(start, end)
                      .map((txt,i)=> `<div class="row" style="top:${(start+i)*rowH}px">${txt}</div>`)
                      .join('');
    $container.html(slice);
  }
  $container.parent().on('scroll', render);
  render();
}
```

Demonstrates:

* Single delegated `scroll` handler.
* `position:absolute` rows avoid layout for hidden items.
* `html(slice)` rewrites once per frame.

#### A. Frequently Asked Questions (FAQ)

| Question                               | Short Answer                                                                                                                              |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Does jFast support IE 11?**          | Partially. Core DOM helpers work thanks to built‑in polyfills (`Set`, `NodeList.forEach`). You still need a Promise polyfill for Ajax.    |
| **Can I load jFast alongside jQuery?** | Yes. jFast attaches **both** `jFast` and `$`. If jQuery is present first, jFast will **not** overwrite `$`; access it via `window.jFast`. |
| **How big is the minified build?**     | Un‑minified: 9.6 KB. Minified + gzip: ≈ 2 KB.                                                                                             |
| **Is there ES Module support?**        | Planned for v1.3.0. For now you can `import` the UMD file in bundlers (Rollup / Webpack) without issues.                                  |
| **How do I remove the global `$`?**    | Wrap the script in a closure: `;(function(priv){ /* docs here */ })(jFast);` and avoid referring to `$` globally.                         |
| **What about TypeScript types?**       | A community‑maintained `@types/jfast` will debut after v1.3.0. Until then, declare `const $: typeof jFast;` in a `d.ts`.                  |

***

#### B. Migrating From jQuery 3.x to jFast 1.x

| jQuery Code                                 | jFast Equivalent                                    | Notes                                                    |
| ------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------- |
| `$(dom).fadeToggle(150)`                    | `$(dom).toggle(150)`                                | jFast combines fade into `toggle`.                       |
| `$.ajaxSetup({...})`                        | *(none)*                                            | jFast aims for statelessness; pass settings per‑request. |
| `$('form').serializeArray()`                | Same                                                | Fully supported.                                         |
| `$(img).on('load', fn)`                     | Same                                                | Event helpers identical.                                 |
| `$(el).css({ top:0 }).animate({ top:200 })` | Use CSS transitions or `slideDown/Up`               | jFast provides only basic slide/fade; no full `animate`. |
| `$.Deferred()`                              | Use native `Promise` or `$.ajax()` returned promise | Promise polyfill required on old browsers.               |

**Cheat Migration Recipe**

1. Replace jQuery script tag with jFast.
2. Scan for non‑supported APIs: `.animate`, `ajaxSetup`, `$.proxy`, `$.fn.extend` (replace with `jFast.extend`).
3. Polyfill Promise if targeting ES5 browsers.
4. Test on evergreen browsers; then add polyfills as necessary.

***

#### C. Roadmap (2025 ↦ 2026)

| Milestone  | ETA     | Highlights                                                                              |
| ---------- | ------- | --------------------------------------------------------------------------------------- |
| **v1.3.0** | Q3 2025 | Native ES Module bundle, built‑in `Promise` polyfill toggle, new helper `.toggleAttr`   |
| **v1.4.0** | Q1 2026 | Animation engine (CSS keyframe wrapper), `$.ajaxSetup`, fetch‑based Ajax fallback       |
| **v2.0.0** | TBD     | Drops IE 11 support, swaps internal XHR for `fetch`, tree‑shake‑friendly build targets. |

Community feedback on GitHub **issues tagged `proposal-*`** steers prioritisation.

***

#### D. Additional Resources

* **GitHub** – <https://github.com/cmsfullform/jfast>
* **Issues / Discussions** – good first issues labelled **“help wanted”**
* **CDN** – CDN link generator: `https://cdn.jsdelivr.net/npm/jfast@1.2.3/dist/jfast.min.js`
* **Starter Plug‑ins** – `modal.js`, `tooltip.js`, `virtualList.js` in `/plugins` directory
* **Unit Test Suite** – sample Jest configuration in `/test` folder

***

#### E. Final Thoughts

jFast was built for developers who **love jQuery’s ergonomics** but need a **fraction‑of‑the‑weight** helper in 2025‑era projects. Its small, readable source aims to be a living tutorial—feel free to fork, audit, and tailor it to your own design system.

> **Thank you** for reading through all parts.\
> If you find a bug, open an issue or submit a pull‑request—every line counts!

*— CMS Full Form, Maintainer*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.cmsfullform.com/jfast-document.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
