v1.0.0

πŸ”Œ HumHub Integration

OnigiriJS is purpose-built for HumHub modules, providing seamless integration with HumHub's architecture, PJAX system, and module API.

Why OnigiriJS for HumHub?

  • Automatic PJAX re-mounting for HumHub navigation
  • Built-in CSRF protection compatible with HumHub
  • Reactive components that work with HumHub's jQuery
  • Event system that integrates with HumHub events
  • Local storage with proper prefixing
  • Easy module initialization

Quick Start

1. Add OnigiriJS to Your Module

// File structure
modules/onigiriwidget/
β”œβ”€β”€ assets/
β”‚   └── OnigiriWidgetAsset.php
β”œβ”€β”€ controllers/
β”œβ”€β”€ models/
β”œβ”€β”€ resources/
β”‚   β”œβ”€β”€ js/onigiri-core.js
β”‚   β”œβ”€β”€ js/onigiri-events.js
β”‚   β”œβ”€β”€ js/onigiri-components.js
β”‚   β”œβ”€β”€ js/onigiri-security.js
β”‚   β”œβ”€β”€ js/onigiri-ajax.js
β”‚   β”œβ”€β”€ js/onigiri-storage.js
β”‚   β”œβ”€β”€ js/onigiri-humhub.js
β”‚   └── js/widget.js
β”œβ”€β”€ views/
└── Module.php

2. Register in AssetBundle

<?php
// assets/OnigiriWidgetAsset.php
namespace humhub\modules\onigiriwidget\assets;

use yii\web\AssetBundle;

class OnigiriWidgetAsset extends AssetBundle
{
    public $sourcePath = '@onigiriwidget/resources';
    
    public $js = [
        'js/onigiri-core.js',
        'js/onigiri-events.js',
        'js/onigiri-components.js',
        'js/onigiri-security.js',
        'js/onigiri-ajax.js',
        'js/onigiri-storage.js',
        'js/onigiri-humhub.js',
        'js/widget.js'
    ];
}
?>

3. Create Your Widget

// resources/js/widget.js
Onigiri.humhub('onigiriWidget', {
    selector: '.onigiri-widget',
    
    data: {
        items: [],
        loading: false,
        error: null
    },
    
    methods: {
        async loadItems() {
            this.loading = true;
            this.error = null;
            
            try {
                const data = await Onigiri.get(
                    humhub.config.get('baseUrl') + '/onigiriwidget/api/items'
                );
                this.items = data;
            } catch (error) {
                this.error = 'Failed to load items';
                console.error(error);
            } finally {
                this.loading = false;
            }
        }
    },
    
    created() {
        console.log('πŸ™ Widget created!');
        // Initialize security with HumHub CSRF
        Onigiri.security.init();
    },
    
    mounted() {
        console.log('πŸ™ Widget mounted!');
        this.loadItems();
    }
});

4. Add Widget to View

<?php
// views/index.php
use humhub\modules\onigiriwidget\assets\OnigiriWidgetAsset;

OnigiriWidgetAsset::register($this);
?>

<div class="onigiri-widget">
    <h2>πŸ™ Onigiri Widget</h2>
    <div id="widget-content"></div>
</div>

HumHub Module Configuration

Component Options

Onigiri.humhub('moduleName', {
    selector: '.my-widget',     // Widget selector
    autoInit: true,              // Auto-initialize (default: true)
    pjax: true,                  // Auto-remount on PJAX (default: true)
    
    // Standard component options
    data: { /* ... */ },
    methods: { /* ... */ },
    computed: { /* ... */ },
    watchers: { /* ... */ },
    
    // Lifecycle hooks
    created() { /* ... */ },
    mounted() { /* ... */ }
});

Disable Auto-Init

// Manual initialization
Onigiri.humhub('customWidget', {
    selector: '.custom-widget',
    autoInit: false,  // Don't auto-mount
    
    data: { count: 0 }
});

// Initialize manually when needed
$(document).on('custom:event', function() {
    const widget = Onigiri.humhub.customWidget;
    widget.mount('.custom-widget');
});

Disable PJAX Re-mounting

// Don't remount on PJAX navigation
Onigiri.humhub('staticWidget', {
    selector: '.static-widget',
    pjax: false,  // Won't remount on PJAX
    
    mounted() {
        // Only runs once on initial page load
    }
});

Working with HumHub API

Making API Calls

const widget = new Onigiri.prototype.Component({
    methods: {
        async loadData() {
            // Get base URL from HumHub config
            const baseUrl = humhub.config.get('baseUrl');
            
            // Make request with CSRF
            const data = await Onigiri.ajax({
                url: baseUrl + '/onigiri/api/data',
                method: 'GET',
                headers: {
                    'X-CSRF-Token': humhub.config.get('csrf.token')
                }
            });
            
            return data;
        },
        
        async saveData(item) {
            const baseUrl = humhub.config.get('baseUrl');
            
            await Onigiri.post(
                baseUrl + '/onigiri/api/save',
                item
            );
        }
    }
});

Using HumHub Configuration

// Access HumHub configuration
const userId = humhub.config.get('user.id');
const userGuid = humhub.config.get('user.guid');
const csrfToken = humhub.config.get('csrf.token');
const baseUrl = humhub.config.get('baseUrl');

// Use in component
Onigiri.humhub('userWidget', {
    data: {
        userId: humhub.config.get('user.id'),
        preferences: {}
    },
    
    created() {
        // Load user-specific data
        this.loadPreferences();
    },
    
    methods: {
        async loadPreferences() {
            const key = \`user_\${this.userId}_prefs\`;
            this.preferences = Onigiri.storage.get(key, {});
        }
    }
});

HumHub Events Integration

Listen to HumHub Events

Onigiri.humhub('eventWidget', {
    mounted() {
        // Listen to HumHub ready event
        $(document).on('humhub:ready', () => {
            console.log('πŸ™ HumHub is ready!');
            this.init();
        });
        
        // Listen to PJAX events
        $(document).on('pjax:success', () => {
            console.log('πŸ™ PJAX navigation completed');
            this.refresh();
        });
        
        // Listen to custom HumHub events
        $(document).on('humhub:content:updated', () => {
            console.log('Content updated');
            this.reload();
        });
    }
});

Emit HumHub Events

const widget = Onigiri.humhub('customWidget', {
    methods: {
        save() {
            // Save data...
            
            // Trigger HumHub event
            $(document).trigger('humhub:modules:onigiri:saved', {
                itemId: this.currentItem.id
            });
        }
    }
});

// Other modules can listen
$(document).on('humhub:modules:onigiri:saved', function(e, data) {
    console.log('Onigiri saved:', data.itemId);
});

PJAX Integration

Automatic Re-mounting

// Widget automatically remounts after HumHub PJAX navigation
Onigiri.humhub('pjaxWidget', {
    selector: '.pjax-widget',
    pjax: true,  // Default behavior
    
    mounted() {
        // Called on initial load AND after each PJAX navigation
        console.log('πŸ™ Widget (re)mounted');
        this.initialize();
    },
    
    methods: {
        initialize() {
            // Re-initialize component state
            this.loadData();
            this.setupEventListeners();
        }
    }
});

Clean Up on PJAX

Onigiri.humhub('cleanupWidget', {
    data: {
        timers: [],
        listeners: []
    },
    
    mounted() {
        // Set up timers
        const timer = setInterval(() => {
            this.checkStatus();
        }, 5000);
        
        this.timers.push(timer);
    },
    
    beforeDestroy() {
        // Clean up timers before PJAX navigation
        this.timers.forEach(timer => clearInterval(timer));
        this.timers = [];
        
        console.log('πŸ™ Cleaned up before PJAX');
    }
});

Complete Module Example

PHP Controller

<?php
// controllers/ApiController.php
namespace humhub\modules\onigiriwidget\controllers;

use humhub\components\Controller;
use yii\web\Response;

class ApiController extends Controller
{
    public $enableCsrfValidation = true;
    
    public function actionItems()
    {
        \Yii::$app->response->format = Response::FORMAT_JSON;
        
        return [
            ['id' => 1, 'name' => 'Salmon Onigiri', 'price' => 3.50],
            ['id' => 2, 'name' => 'Tuna Onigiri', 'price' => 3.50],
            ['id' => 3, 'name' => 'Umeboshi Onigiri', 'price' => 3.00]
        ];
    }
    
    public function actionSave()
    {
        \Yii::$app->response->format = Response::FORMAT_JSON;
        
        $data = \Yii::$app->request->post();
        
        // Validate and save...
        
        return ['success' => true, 'id' => 123];
    }
}
?>

JavaScript Component

// resources/js/onigiri-shop.js
Onigiri.humhub('onigiriShop', {
    selector: '.onigiri-shop',
    
    data: {
        items: [],
        cart: [],
        loading: false,
        error: null
    },
    
    computed: {
        cartTotal() {
            return this.cart.reduce((sum, item) => {
                return sum + (item.price * item.quantity);
            }, 0);
        },
        
        cartCount() {
            return this.cart.reduce((sum, item) => {
                return sum + item.quantity;
            }, 0);
        }
    },
    
    methods: {
        async loadItems() {
            this.loading = true;
            this.error = null;
            
            try {
                const baseUrl = humhub.config.get('baseUrl');
                this.items = await Onigiri.get(
                    baseUrl + '/onigiriwidget/api/items'
                );
            } catch (error) {
                this.error = 'Failed to load menu';
                console.error(error);
            } finally {
                this.loading = false;
            }
        },
        
        addToCart(item) {
            const existing = this.cart.find(i => i.id === item.id);
            
            if (existing) {
                existing.quantity++;
            } else {
                this.cart.push({ ...item, quantity: 1 });
            }
            
            this.saveCart();
            
            // Show notification
            humhub.modules.ui.status.success(
                \`πŸ™ Added \${item.name} to cart!\`
            );
        },
        
        removeFromCart(itemId) {
            this.cart = this.cart.filter(i => i.id !== itemId);
            this.saveCart();
        },
        
        saveCart() {
            const userId = humhub.config.get('user.id');
            Onigiri.storage.set(\`cart_\${userId}\`, this.cart);
        },
        
        loadCart() {
            const userId = humhub.config.get('user.id');
            this.cart = Onigiri.storage.get(\`cart_\${userId}\`, []);
        },
        
        async checkout() {
            const baseUrl = humhub.config.get('baseUrl');
            
            try {
                const result = await Onigiri.post(
                    baseUrl + '/onigiriwidget/api/checkout',
                    { items: this.cart }
                );
                
                humhub.modules.ui.status.success(
                    'πŸ™ Order placed successfully!'
                );
                
                this.cart = [];
                this.saveCart();
                
            } catch (error) {
                humhub.modules.ui.status.error(
                    'Checkout failed: ' + error.message
                );
            }
        }
    },
    
    watchers: {
        cart(newCart) {
            // Update cart badge
            O('.cart-badge').text(this.cartCount);
        }
    },
    
    created() {
        console.log('πŸ™ Onigiri Shop created!');
        
        // Initialize security
        Onigiri.security.init({
            csrfToken: humhub.config.get('csrf.token')
        });
        
        // Load saved cart
        this.loadCart();
    },
    
    mounted() {
        console.log('πŸ™ Onigiri Shop mounted!');
        
        // Load menu items
        this.loadItems();
        
        // Render initial UI
        this.render();
    },
    
    render() {
        const container = O('.onigiri-shop');
        
        if (this.loading) {
            container.html('<p>Loading menu... πŸ™</p>');
            return;
        }
        
        if (this.error) {
            container.html(\`<p class="error">\${this.error}</p>\`);
            return;
        }
        
        let html = '<h2>πŸ™ Menu</h2><div class="items">';
        
        this.items.forEach(item => {
            html += \`
                <div class="menu-item" data-id="\${item.id}">
                    <h3>\${item.name}</h3>
                    <p>$\${item.price.toFixed(2)}</p>
                    <button class="add-to-cart" data-id="\${item.id}">
                        Add to Cart
                    </button>
                </div>
            \`;
        });
        
        html += '</div>';
        
        if (this.cart.length > 0) {
            html += \`
                <div class="cart">
                    <h3>Cart (\${this.cartCount} items)</h3>
                    <p>Total: $\${this.cartTotal.toFixed(2)}</p>
                    <button class="checkout-btn">Checkout</button>
                </div>
            \`;
        }
        
        container.html(html);
        
        // Bind events
        O('.add-to-cart').on('click', (e) => {
            const id = parseInt(O(e.target).data('id'));
            const item = this.items.find(i => i.id === id);
            if (item) this.addToCart(item);
        });
        
        O('.checkout-btn').on('click', () => {
            this.checkout();
        });
    }
});

View Template

<?php
// views/index.php
use humhub\modules\onigiriwidget\assets\OnigiriShopAsset;

OnigiriShopAsset::register($this);
?>

<div class="panel panel-default">
    <div class="panel-heading">
        πŸ™ Onigiri Shop
    </div>
    <div class="panel-body">
        <div class="onigiri-shop">
            <!-- Widget content rendered here -->
        </div>
    </div>
</div>

HumHub UI Integration

Using HumHub Status Messages

// Success message
humhub.modules.ui.status.success('πŸ™ Saved successfully!');

// Error message
humhub.modules.ui.status.error('Failed to save');

// Info message
humhub.modules.ui.status.info('Loading data...');

// In component
Onigiri.humhub('widget', {
    methods: {
        async save() {
            try {
                await Onigiri.post('/api/save', this.data);
                humhub.modules.ui.status.success('πŸ™ Saved!');
            } catch (error) {
                humhub.modules.ui.status.error('Save failed');
            }
        }
    }
});

Using HumHub Modals

// Open modal with content
humhub.modules.ui.modal.global.load('/onigiri/modal/view');

// In component
O('.show-details').on('click', function() {
    const id = O(this).data('id');
    humhub.modules.ui.modal.global.load(
        \`/onigiriwidget/details?id=\${id}\`
    );
});
πŸ™ Pro Tip: OnigiriJS automatically handles HumHub's PJAX navigation, so your components will remount correctly after page transitions!

Best Practices for HumHub Modules

  • Always initialize security with HumHub's CSRF token
  • Use HumHub's base URL for API calls
  • Store user-specific data with user ID prefix
  • Clean up timers/listeners in beforeDestroy
  • Use HumHub's UI components for consistency
  • Test with PJAX navigation enabled
  • Handle missing jQuery gracefully
  • Use HumHub events for inter-module communication
  • Follow HumHub's module structure
  • Provide fallbacks for disabled JavaScript

Debugging

// Enable debug logging
Onigiri.humhub('debugWidget', {
    created() {
        console.log('πŸ™ Widget created');
        console.log('User ID:', humhub.config.get('user.id'));
        console.log('CSRF Token:', humhub.config.get('csrf.token'));
    },
    
    mounted() {
        console.log('πŸ™ Widget mounted');
        console.log('Element:', this.$el);
    },
    
    beforeDestroy() {
        console.log('πŸ™ Widget destroying');
    }
});

βœ… 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.