# 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*
