v1.0.0

πŸ”Œ Plugins

OnigiriJS features a powerful plugin system that allows you to extend the framework with additional functionality. Built-in plugins provide storage, routing, animations, security, and more.

Plugin System Overview

The OnigiriJS plugin system allows you to:

  • Install pre-built plugins from the registry
  • Create custom plugins to extend functionality
  • Share and reuse code across projects
  • Prevent duplicate plugin installations
  • Access plugins through a centralized registry

Installing Plugins

// Install by name (from registry)
O.use('storage');
O.use('router');
O.use('animation');

// Install with options
O.use('storage', {
    type: 'local',
    prefix: 'myapp_'
});

// Install custom plugin
O.use(MyCustomPlugin);

// Chain installations
O.use('storage')
  .use('router')
  .use('animation');

Plugin Registry

// List available plugins
const plugins = O.pluginRegistry.list();
console.log(plugins); // ['storage', 'router', 'animation', ...]

// Check if plugin exists
if (O.pluginRegistry.has('storage')) {
    console.log('Storage plugin available');
}

// Check if plugin is installed
if (O.pluginRegistry.isInstalled('storage')) {
    console.log('Storage plugin is active');
}

// Get plugin definition
const storagePlugin = O.pluginRegistry.get('storage');

Built-in Plugins

πŸ—„οΈ Storage

v1.0.0

LocalStorage and SessionStorage wrapper with JSON serialization support.

🧭 Router

v1.0.0

Hash-based and History API routing with parameter extraction.

✨ Animation

v1.0.0

CSS animation helpers for fading, sliding, and custom animations.

πŸ”’ Security

v1.0.0

CSRF token management for forms and AJAX requests.

πŸͺ Cookies

v1.0.0

Cookie management with expiration and security options.

Storage Plugin

Provides a simple interface for working with browser storage (localStorage or sessionStorage) with automatic JSON serialization.

Installation

// Use localStorage (default)
O.use('storage');

// Use sessionStorage
O.use('storage', { type: 'session' });

// Custom prefix
O.use('storage', { 
    type: 'local',
    prefix: 'myapp_',
    serialize: true
});

Configuration Options

Option Type Default Description
type string 'local' 'local' or 'session'
prefix string 'onigiri_' Key prefix for all items
serialize boolean true Auto JSON serialize/deserialize

Methods

O.storage.set(key, value) β†’ boolean
// Store data
O.storage.set('user', { name: 'John', id: 123 });
O.storage.set('theme', 'dark');

// Store arrays
O.storage.set('cart', [
    { id: 1, name: 'Salmon Onigiri' },
    { id: 2, name: 'Tuna Onigiri' }
]);
O.storage.get(key, defaultValue) β†’ any
// Retrieve data
const user = O.storage.get('user');
console.log(user.name); // 'John'

// With default value
const theme = O.storage.get('theme', 'light');

// Non-existent key returns default
const missing = O.storage.get('nonexistent', null); // null
O.storage.remove(key) β†’ this
// Remove item
O.storage.remove('user');

// Chain operations
O.storage.remove('user').remove('theme');
O.storage.clear() β†’ this
// Remove all items with prefix
O.storage.clear();
O.storage.has(key) β†’ boolean
// Check if key exists
if (O.storage.has('user')) {
    console.log('User data found');
}
O.storage.keys() β†’ string[]
// Get all keys (without prefix)
const keys = O.storage.keys();
console.log(keys); // ['user', 'theme', 'cart']

Complete Example

// User preferences manager
O.use('storage', { prefix: 'app_' });

// Save preferences
function savePreferences(prefs) {
    O.storage.set('preferences', prefs);
    console.log('Preferences saved');
}

// Load preferences
function loadPreferences() {
    return O.storage.get('preferences', {
        theme: 'light',
        language: 'en',
        notifications: true
    });
}

// Apply preferences
const prefs = loadPreferences();
document.body.className = prefs.theme;

// Update single preference
function updateTheme(theme) {
    const prefs = loadPreferences();
    prefs.theme = theme;
    savePreferences(prefs);
}

Router Plugin

Provides client-side routing with support for hash-based and HTML5 History API modes, parameter extraction, and 404 handling.

Installation

// Hash-based routing (default)
O.use('router');

// HTML5 History API
O.use('router', { 
    mode: 'history',
    root: '/app'
});

Configuration Options

Option Type Default Description
mode string 'hash' 'hash' or 'history'
root string '/' Base path for history mode

Defining Routes

O.use('router');

// Simple route
O.router.add('/', (path, params) => {
    console.log('Home page');
});

// Route with parameters
O.router.add('/user/:id', (path, params) => {
    console.log('User ID:', params.id);
    loadUser(params.id);
});

// Multiple parameters
O.router.add('/product/:category/:id', (path, params) => {
    console.log('Category:', params.category);
    console.log('Product ID:', params.id);
});

// 404 fallback
O.router.add('*', (path, params) => {
    console.log('Page not found:', path);
    show404Page();
});

// Start router
O.router.start();

Navigation

// Navigate programmatically
O.router.navigate('/user/123');
O.router.navigate('/product/onigiri/456');

// From links (hash mode)
<a href="#/user/123">View User</a>

// From links (history mode with data-route)
<a href="/user/123" data-route>View User</a>

Complete SPA Example

O.use('router');

// Define routes
O.router
    .add('/', showHome)
    .add('/about', showAbout)
    .add('/products', showProducts)
    .add('/product/:id', showProduct)
    .add('*', show404);

// Route handlers
function showHome() {
    O('#app').html(`
        <h1>πŸ™ Welcome to Onigiri Shop</h1>
        <p>Browse our delicious selection!</p>
        <a href="#/products">View Products</a>
    `);
}

function showProducts() {
    O('#app').html(`
        <h1>Our Products</h1>
        <div id="product-list"></div>
    `);
    
    // Load products
    const products = [
        { id: 1, name: 'Salmon Onigiri' },
        { id: 2, name: 'Tuna Onigiri' },
        { id: 3, name: 'Umeboshi Onigiri' }
    ];
    
    products.forEach(p => {
        O('#product-list').append(`
            <a href="#/product/${p.id}">${p.name}</a>
        `);
    });
}

function showProduct(path, params) {
    const productId = params.id;
    
    O('#app').html(`
        <h1>Product #${productId}</h1>
        <a href="#/products">Back to Products</a>
    `);
}

function show404() {
    O('#app').html(`
        <h1>404 - Page Not Found</h1>
        <a href="#/">Go Home</a>
    `);
}

// Start
O.router.start();

Animation Plugin

Provides jQuery-like animation methods using CSS transitions for smooth, performant animations.

Installation

O.use('animation');

Fade Animations

O(selector).fadeIn(duration, callback)
// Fade in element
O('.modal').fadeIn(300);

// With callback
O('.notification').fadeIn(200, function() {
    console.log('Notification shown');
});
O(selector).fadeOut(duration, callback)
// Fade out element
O('.modal').fadeOut(300);

// Remove after fade out
O('.alert').fadeOut(200, function() {
    O(this).remove();
});

Slide Animations

O(selector).slideDown(duration, callback)
// Slide down (reveal)
O('.dropdown-menu').slideDown(300);

// Toggle on click
O('.accordion-header').on('click', function() {
    O(this).siblings('.accordion-content').slideDown(300);
});
O(selector).slideUp(duration, callback)
// Slide up (hide)
O('.dropdown-menu').slideUp(300);

// Close accordion
O('.close-btn').on('click', function() {
    O('.accordion-content').slideUp(300);
});

Custom Animations

O(selector).animate(properties, duration, callback)
// Custom animation
O('.box').animate({
    width: '300px',
    height: '200px',
    opacity: '0.5'
}, 500);

// Complex animation
O('.card').animate({
    transform: 'translateX(100px) rotate(10deg)',
    backgroundColor: '#e74c3c',
    borderRadius: '20px'
}, 800, function() {
    console.log('Animation complete');
});

Animation Examples

// Modal with fade
O('#show-modal').on('click', () => {
    O('.modal-overlay').fadeIn(200, () => {
        O('.modal').fadeIn(300);
    });
});

O('#close-modal').on('click', () => {
    O('.modal').fadeOut(200, () => {
        O('.modal-overlay').fadeOut(200);
    });
});

// Accordion
O('.accordion-header').on('click', function() {
    const content = O(this).siblings('.accordion-content');
    const isOpen = O(this).hasClass('open');
    
    // Close all
    O('.accordion-content').slideUp(300);
    O('.accordion-header').removeClass('open');
    
    // Open clicked (if closed)
    if (!isOpen) {
        content.slideDown(300);
        O(this).addClass('open');
    }
});

// Notification system
function showNotification(message, type = 'info') {
    const notification = document.createElement('div');
    notification.className = `notification ${type}`;
    notification.textContent = message;
    document.body.appendChild(notification);
    
    O(notification)
        .fadeIn(200)
        .animate({ transform: 'translateY(0)' }, 300);
    
    setTimeout(() => {
        O(notification).fadeOut(300, function() {
            this.remove();
        });
    }, 3000);
}

Security Plugin

Manages CSRF tokens for forms and AJAX requests to protect against cross-site request forgery attacks.

Installation

O.use('security');

// Custom configuration
O.use('security', {
    tokenName: 'csrf_token',
    headerName: 'X-CSRF-Token',
    metaName: 'csrf-token'
});

Setting CSRF Token

// Set token (usually from server)
O.security.setToken('abc123xyz');

// In your HTML (Laravel example)
<meta name="csrf-token" content="{{ csrf_token() }}">

// Token is automatically read from meta tag

Forms

// Auto-add to forms on submit
<form method="POST" action="/api/save">
    <!-- CSRF token automatically added -->
    <input name="email" type="email">
    <button type="submit">Submit</button>
</form>

// Disable auto-add for specific form
<form data-no-csrf>
    <!-- No CSRF token added -->
</form>

// Manually add to form
const form = document.querySelector('#myForm');
O.security.addCSRFToForm(form);

AJAX Requests

// Auto-add to AJAX requests (if ajax plugin loaded)
O.post('/api/user', { name: 'John' })
    .then(data => console.log(data));

// Manually add to headers
const headers = O.security.addCSRFToHeaders({
    'Content-Type': 'application/json'
});

fetch('/api/user', {
    method: 'POST',
    headers: headers,
    body: JSON.stringify({ name: 'John' })
});

Complete Example

// Initialize with token from server
O.use('security');

// Token already in meta tag
<meta name="csrf-token" content="{{ csrf_token() }}">

// Forms automatically protected
O('#user-form').on('submit', function(e) {
    e.preventDefault();
    
    // CSRF token already added
    const formData = new FormData(this);
    
    fetch('/api/user', {
        method: 'POST',
        body: formData
    }).then(response => response.json())
      .then(data => console.log('Success:', data));
});

// AJAX with CSRF
O.ajax({
    url: '/api/data',
    method: 'POST',
    data: { key: 'value' },
    csrf: true // enabled by default
}).then(data => {
    console.log('Data saved');
});

Cookies Plugin

Provides an easy-to-use interface for managing browser cookies with expiration, security, and SameSite options.

Installation

O.use('cookies');

Setting Cookies

O.cookies.set(name, value, options)
// Simple cookie
O.cookies.set('username', 'john');

// With expiration (days)
O.cookies.set('session', 'abc123', { expires: 7 });

// Secure cookie
O.cookies.set('token', 'xyz789', {
    expires: 30,
    secure: true,
    sameSite: 'Strict'
});

// Full options
O.cookies.set('preferences', JSON.stringify({ theme: 'dark' }), {
    path: '/',
    domain: '.example.com',
    expires: 365,
    secure: true,
    sameSite: 'Lax'
});

Getting Cookies

O.cookies.get(name) β†’ string|null
// Get cookie value
const username = O.cookies.get('username');
console.log(username); // 'john'

// Non-existent cookie
const missing = O.cookies.get('nonexistent'); // null

// Parse JSON
const prefs = JSON.parse(O.cookies.get('preferences') || '{}');

Removing Cookies

O.cookies.remove(name, options)
// Remove cookie
O.cookies.remove('username');

// Match path and domain when removing
O.cookies.remove('session', {
    path: '/',
    domain: '.example.com'
});

Checking Cookies

O.cookies.has(name) β†’ boolean
// Check if cookie exists
if (O.cookies.has('session')) {
    console.log('User is logged in');
} else {
    redirectToLogin();
}

Cookie Options

Option Type Default Description
path string '/' Cookie path
domain string current Cookie domain
expires number null Expiration in days
secure boolean false HTTPS only
sameSite string 'Lax' 'Strict', 'Lax', or 'None'

Complete Example

O.use('cookies');

// User preferences manager
const UserPrefs = {
    save(prefs) {
        O.cookies.set('user_prefs', JSON.stringify(prefs), {
            expires: 365,
            secure: true
        });
    },
    
    load() {
        const data = O.cookies.get('user_prefs');
        return data ? JSON.parse(data) : {
            theme: 'light',
            language: 'en'
        };
    },
    
    update(key, value) {
        const prefs = this.load();
        prefs[key] = value;
        this.save(prefs);
    },
    
    clear() {
        O.cookies.remove('user_prefs');
    }
};

// Usage
UserPrefs.update('theme', 'dark');
const prefs = UserPrefs.load();
console.log(prefs.theme); // 'dark'

// Session management
function createSession(token) {
    O.cookies.set('session_token', token, {
        expires: 1, // 1 day
        secure: true,
        sameSite: 'Strict'
    });
}

function isLoggedIn() {
    return O.cookies.has('session_token');
}

function logout() {
    O.cookies.remove('session_token');
    O.router.navigate('/login');
}

Creating Custom Plugins

You can create your own plugins to extend OnigiriJS functionality.

Plugin Structure

const MyPlugin = {
    name: 'myplugin',
    version: '1.0.0',
    
    install: function(Onigiri, options) {
        // Extend Onigiri prototype
        Onigiri.prototype.myMethod = function() {
            // Your code here
            return this; // for chaining
        };
        
        // Add static methods
        Onigiri.myStaticMethod = function() {
            // Your code here
        };
        
        // Mark module as loaded
        Onigiri.modules.myplugin = true;
    }
};

// Register plugin
O.pluginRegistry.register('myplugin', MyPlugin);

// Use plugin
O.use('myplugin');

Example: Tooltip Plugin

const TooltipPlugin = {
    name: 'tooltip',
    version: '1.0.0',
    
    install: function(Onigiri, options) {
        const defaults = {
            position: 'top',
            delay: 200
        };
        
        const config = Onigiri.extend({}, defaults, options);
        
        Onigiri.prototype.tooltip = function(text) {
            this.each(el => {
                el.setAttribute('data-tooltip', text);
                
                el.addEventListener('mouseenter', (e) => {
                    setTimeout(() => {
                        const tooltip = document.createElement('div');
                        tooltip.className = 'onigiri-tooltip';
                        tooltip.textContent = text;
                        tooltip.style.position = 'absolute';
                        document.body.appendChild(tooltip);
                        
                        // Position tooltip
                        const rect = el.getBoundingClientRect();
                        tooltip.style.left = rect.left + 'px';
                        tooltip.style.top = (rect.top - tooltip.offsetHeight - 5) + 'px';
                        
                        el._tooltip = tooltip;
                    }, config.delay);
                });
                
                el.addEventListener('mouseleave', () => {
                    if (el._tooltip) {
                        el._tooltip.remove();
                        el._tooltip = null;
                    }
                });
            });
            
            return this;
        };
        
        Onigiri.modules.tooltip = true;
    }
};

// Register and use
O.pluginRegistry.register('tooltip', TooltipPlugin);
O.use('tooltip', { delay: 300 });

// Usage
O('.info-icon').tooltip('Click for more information');
O('.button').tooltip('Submit the form');

Example: Form Serializer Plugin

const FormSerializerPlugin = {
    name: 'formserializer',
    version: '1.0.0',
    
    install: function(Onigiri) {
        Onigiri.prototype.serialize = function() {
            const form = this.elements[0];
            if (!form || form.tagName !== 'FORM') {
                console.error('serialize() requires a form element');
                return {};
            }
            
            const data = {};
            const formData = new FormData(form);
            
            for (let [key, value] of formData.entries()) {
                if (data[key]) {
                    // Handle multiple values (checkboxes, multi-select)
                    if (!Array.isArray(data[key])) {
                        data[key] = [data[key]];
                    }
                    data[key].push(value);
                } else {
                    data[key] = value;
                }
            }
            
            return data;
        };
        
        Onigiri.prototype.deserialize = function(data) {
            const form = this.elements[0];
            if (!form || form.tagName !== 'FORM') {
                console.error('deserialize() requires a form element');
                return this;
            }
            
            Object.keys(data).forEach(key => {
                const input = form.querySelector(`[name="${key}"]`);
                if (input) {
                    if (input.type === 'checkbox' || input.type === 'radio') {
                        input.checked = input.value === data[key] || data[key] === true;
                    } else {
                        input.value = data[key];
                    }
                }
            });
            
            return this;
        };
        
        Onigiri.modules.formserializer = true;
    }
};

// Register and use
O.pluginRegistry.register('formserializer', FormSerializerPlugin);
O.use('formserializer');

// Usage
const formData = O('#user-form').serialize();
console.log(formData); // { name: 'John', email: 'john@example.com' }

// Populate form
O('#user-form').deserialize({
    name: 'John Doe',
    email: 'john@example.com'
});

Example: Lazy Load Plugin

const LazyLoadPlugin = {
    name: 'lazyload',
    version: '1.0.0',
    
    install: function(Onigiri, options) {
        const defaults = {
            threshold: 0.1,
            rootMargin: '50px'
        };
        
        const config = Onigiri.extend({}, defaults, options);
        
        Onigiri.prototype.lazyLoad = function() {
            if (!('IntersectionObserver' in window)) {
                // Fallback: load all images immediately
                this.each(el => {
                    if (el.dataset.src) {
                        el.src = el.dataset.src;
                    }
                });
                return this;
            }
            
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const img = entry.target;
                        if (img.dataset.src) {
                            img.src = img.dataset.src;
                            img.removeAttribute('data-src');
                        }
                        observer.unobserve(img);
                    }
                });
            }, {
                threshold: config.threshold,
                rootMargin: config.rootMargin
            });
            
            this.each(el => observer.observe(el));
            
            return this;
        };
        
        Onigiri.modules.lazyload = true;
    }
};

// Register and use
O.pluginRegistry.register('lazyload', LazyLoadPlugin);
O.use('lazyload');

// HTML
<img data-src="/images/photo1.jpg" alt="Photo 1">
<img data-src="/images/photo2.jpg" alt="Photo 2">

// Initialize lazy loading
O('img[data-src]').lazyLoad();

Plugin Best Practices

  • Always include name and version properties
  • Return this from prototype methods for chaining
  • Use Onigiri.extend() for option merging
  • Mark your module as loaded with Onigiri.modules.yourplugin = true
  • Handle edge cases and provide fallbacks
  • Document configuration options clearly
  • Clean up event listeners and resources if needed
  • Test plugin with multiple elements (use each())
  • Register plugins before using them
  • Check for dependencies before installing

Multiple Plugin Usage

// Install multiple plugins at once
O.use('storage', { prefix: 'app_' })
  .use('router')
  .use('animation')
  .use('security')
  .use('cookies');

// Check what's loaded
console.log(O.modules);
// {
//   core: true,
//   storage: true,
//   router: true,
//   animation: true,
//   security: true,
//   cookies: true
// }

// Conditional plugin loading
if (needsRouting) {
    O.use('router');
}

if (needsAnimations) {
    O.use('animation');
}

Real-World Plugin Example

// Complete app using multiple plugins
O.use('storage', { prefix: 'shop_' })
  .use('router')
  .use('animation')
  .use('security');

// Initialize app
const App = {
    init() {
        this.loadPreferences();
        this.setupRoutes();
        this.setupEvents();
    },
    
    loadPreferences() {
        const prefs = O.storage.get('preferences', {
            theme: 'light',
            currency: 'USD'
        });
        
        document.body.className = prefs.theme;
    },
    
    setupRoutes() {
        O.router
            .add('/', this.showHome.bind(this))
            .add('/products', this.showProducts.bind(this))
            .add('/product/:id', this.showProduct.bind(this))
            .add('/cart', this.showCart.bind(this))
            .add('/checkout', this.showCheckout.bind(this))
            .add('*', this.show404.bind(this))
            .start();
    },
    
    setupEvents() {
        // Add to cart
        O(document).on('click', '.add-to-cart', (e) => {
            const productId = O(e.target).data('productId');
            this.addToCart(productId);
        });
        
        // Toggle theme
        O('#theme-toggle').on('click', () => {
            this.toggleTheme();
        });
    },
    
    showHome() {
        O('#app').html(`
            <h1>πŸ™ Onigiri Shop</h1>
            <p>Welcome to our store!</p>
            <a href="#/products">Browse Products</a>
        `).fadeIn(300);
    },
    
    showProducts() {
        const products = O.storage.get('products', []);
        
        let html = '<h1>Products</h1><div class="product-grid">';
        products.forEach(p => {
            html += `
                <div class="product-card">
                    <h3>${p.name}</h3>
                    <p>${p.price}</p>
                    <button class="add-to-cart" data-product-id="${p.id}">
                        Add to Cart
                    </button>
                </div>
            `;
        });
        html += '</div>';
        
        O('#app').html(html).fadeIn(300);
    },
    
    showProduct(path, params) {
        const product = this.getProduct(params.id);
        
        O('#app').html(`
            <h1>${product.name}</h1>
            <p>${product.description}</p>
            <p class="price">${product.price}</p>
            <button class="add-to-cart" data-product-id="${product.id}">
                Add to Cart
            </button>
            <a href="#/products">Back to Products</a>
        `).fadeIn(300);
    },
    
    addToCart(productId) {
        let cart = O.storage.get('cart', []);
        
        const existing = cart.find(item => item.id === productId);
        if (existing) {
            existing.quantity++;
        } else {
            const product = this.getProduct(productId);
            cart.push({
                id: product.id,
                name: product.name,
                price: product.price,
                quantity: 1
            });
        }
        
        O.storage.set('cart', cart);
        this.showNotification('Added to cart! πŸ™');
    },
    
    showNotification(message) {
        const notif = document.createElement('div');
        notif.className = 'notification';
        notif.textContent = message;
        document.body.appendChild(notif);
        
        O(notif).fadeIn(200);
        
        setTimeout(() => {
            O(notif).fadeOut(300, function() {
                this.remove();
            });
        }, 2000);
    },
    
    toggleTheme() {
        const prefs = O.storage.get('preferences', {});
        prefs.theme = prefs.theme === 'dark' ? 'light' : 'dark';
        O.storage.set('preferences', prefs);
        
        document.body.className = prefs.theme;
    },
    
    getProduct(id) {
        const products = O.storage.get('products', []);
        return products.find(p => p.id === parseInt(id));
    },
    
    show404() {
        O('#app').html(`
            <h1>404 - Page Not Found</h1>
            <a href="#/">Go Home</a>
        `).fadeIn(300);
    }
};

// Start app
App.init();

Plugin Loading Order

πŸ™ Important: Load plugins in the correct order if they have dependencies. The core library must always be loaded first, followed by any required plugins before dependent plugins.
<!-- Correct loading order -->
<script src="onigiri-core.js"></script>
<script src="onigiri-events.js"></script>      <!-- Required by components -->
<script src="onigiri-ajax.js"></script>         <!-- Can use security if loaded -->
<script src="onigiri-plugins.js"></script>      <!-- Adds plugin system -->
<script src="onigiri-components.js"></script>   <!-- Requires events -->
<script src="onigiri-validation.js"></script>
<script src="onigiri-translation.js"></script>

<!-- Initialize plugins -->
<script>
    O.use('storage')
      .use('router')
      .use('animation')
      .use('security')
      .use('cookies');
</script>

Troubleshooting

Plugin Not Found

// Error: Plugin "storage" not found
// Solution: Make sure onigiri-plugins.js is loaded
<script src="onigiri-plugins.js"></script>

// Or register your custom plugin first
O.pluginRegistry.register('myplugin', MyPlugin);
O.use('myplugin');

Plugin Already Installed

// Warning: Plugin "storage" already installed
// This is safe - plugin won't be installed twice
O.use('storage'); // First time
O.use('storage'); // Ignored with warning

Missing Dependencies

// Check if required module is loaded
if (!O.modules.events) {
    console.error('Events module required');
}

// Better: Check in plugin install
install: function(Onigiri) {
    if (!Onigiri.modules.events) {
        throw new Error('MyPlugin requires Events module');
    }
    // ... install code
}
βœ“ Success! You now know how to use and create OnigiriJS plugins. Start extending your framework with custom functionality!

Additional Resources

  • Review the core documentation for base methods
  • Check out example projects using plugins
  • Explore community plugins (coming soon)
  • Join the OnigiriJS community for support
  • Contribute your own plugins to the ecosystem

βœ… Development Roadmap

Track the progress of OnigiriJS modules. Tasks are marked complete by the development team.

OnigiriJS Module Roadmap

Implementation progress of planned modules

6 / 21 completed (29%)
onigiri-state
Shared global & scoped state management
onigiri-directives
Declarative DOM bindings (o-show, o-model, etc.)
onigiri-resource
REST-style data models over AJAX
onigiri-observe
Intersection & Mutation observer helpers
onigiri-humhub-ui
Standard HumHub UI abstractions (modal, notify, confirm)
onigiri-lifecycle
Component lifecycle hooks
onigiri-guard
Debounce, throttle, single-run guards
onigiri-scroll
Scroll save/restore & helpers (PJAX-friendly)
onigiri-permission
Client-side permission awareness
onigiri-portal
DOM teleport / overlay mounting
onigiri-router
Micro router (non-SPA, PJAX-first)
onigiri-sanitize
HTML & input sanitization
onigiri-shortcut
Keyboard shortcut manager
onigiri-queue
Sequential async task runner
onigiri-gesture
Touch & swipe helpers
onigiri-devtools
Debugging & inspection helpers
onigiri-plugin
Plugin registration system
onigiri-time
Relative time & timezone utilities
onigiri-emojis
Emoji Picker and Manager
onigiri-tasks
Task Management
onigiri-polls
Polls creation and management
Note: Task completion is managed by the OnigiriJS development team.