🚀 Quick Start
Your First Secure Component
Let's build a multilingual counter with CSRF protection and local storage:
<!-- HTML -->
<meta name="csrf-token" content="<?= csrf_token() ?>">
<div id="app"></div>
<!-- Load OnigiriJS -->
<script src="onigiri-core.js"></script>
<script src="onigiri-events.js"></script>
<script src="onigiri-components.js"></script>
<script src="onigiri-security.js"></script>
<script src="onigiri-ajax.js"></script>
<script src="onigiri-storage.js"></script>
<script src="onigiri-translation.js"></script>
// Initialize security and translations
Onigiri.security.init({ autoInjectCSRF: true });
Onigiri.i18n.init({ locale: 'en' });
// Add translations
Onigiri.i18n.addMessages({
en: {
counter: {
title: 'Rice Ball Counter: {count}',
totalClicks: 'Total clicks: {total}',
lastSaved: 'Last saved: {time}',
addButton: 'Add Rice Ball',
resetButton: 'Reset',
never: 'Never'
}
},
es: {
counter: {
title: 'Contador de Onigiri: {count}',
totalClicks: 'Clics totales: {total}',
lastSaved: 'Último guardado: {time}',
addButton: 'Añadir Onigiri',
resetButton: 'Reiniciar',
never: 'Nunca'
}
}
});
// Create a reactive counter component
const counter = new Onigiri.prototype.Component({
data: {
count: 0,
clicks: [],
lastSaved: null
},
computed: {
totalClicks() {
return this.clicks.length;
}
},
methods: {
async increment() {
this.count++;
this.clicks.push({
action: 'increment',
count: this.count,
time: new Date().toLocaleTimeString()
});
// Save to server with auto-CSRF
try {
await Onigiri.post('/api/counter', {
count: this.count
});
this.lastSaved = new Date().toLocaleTimeString();
} catch (error) {
console.error('Failed to save:', error);
}
},
reset() {
this.count = 0;
this.clicks = [];
this.lastSaved = null;
Onigiri.storage.remove('counter');
}
},
watchers: {
count(newVal) {
// Auto-save to local storage
Onigiri.storage.set('counter', newVal);
console.log('🍙 Count saved:', newVal);
}
},
template: function() {
const title = Onigiri.t('counter.title', { count: this.count });
const totalText = Onigiri.t('counter.totalClicks', { total: this.totalClicks });
const savedTime = this.lastSaved || Onigiri.t('counter.never');
const savedText = Onigiri.t('counter.lastSaved', { time: savedTime });
const addBtn = Onigiri.t('counter.addButton');
const resetBtn = Onigiri.t('counter.resetButton');
return \`
<div class="counter-widget">
<h2>🍙 \${title}</h2>
<p>\${totalText}</p>
<p>\${savedText}</p>
<button onclick="counter.increment()">\${addBtn}</button>
<button onclick="counter.reset()">\${resetBtn}</button>
</div>
\`;
},
created() {
// Load saved count
this.count = Onigiri.storage.get('counter', 0);
console.log('🍙 Component created!');
},
mounted() {
console.log('🍙 Counter mounted to DOM!');
}
});
// Mount the component
counter.mount('#app');
🎨 DOM Manipulation
OnigiriJS provides jQuery-like syntax for DOM operations:
// Select and manipulate elements
O('.button').on('click', function() {
O(this).addClass('active');
O('#message').text('🍙 Button clicked!');
});
// Chain methods
O('.items')
.addClass('loaded')
.attr('data-status', 'ready')
.css({ color: 'green', fontSize: '16px' })
.fadeIn(300);
// Traverse DOM
O('.item').parent().addClass('parent-active');
O('.container').children().addClass('child');
O('.item').siblings().addClass('sibling');
🌐 Multilingual Applications
Build fully internationalized apps with automatic HTML translation:
<!-- HTML with translation attributes -->
<div id="language-app">
<h1 data-i18n="app.welcome"></h1>
<p data-i18n="app.description"></p>
<form>
<input type="text"
data-i18n-placeholder="form.username"
name="username">
<input type="email"
data-i18n-placeholder="form.email"
name="email">
<button type="submit" data-i18n="form.submit"></button>
</form>
<p id="item-count"></p>
<p id="last-login"></p>
<select id="language-selector">
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
<option value="ja">日本語</option>
</select>
</div>
<script src="onigiri.translation.js"></script>
<script>
// Initialize translations
Onigiri.i18n.init({
locale: 'en',
autoDetect: true // Auto-detect from browser
});
// Add translations for multiple languages
Onigiri.i18n.addMessages({
en: {
app: {
welcome: 'Welcome to OnigiriJS!',
description: 'Build amazing multilingual applications'
},
form: {
username: 'Enter your username',
email: 'Enter your email',
submit: 'Submit Form'
},
items: {
zero: 'No items',
one: 'One item',
other: '{count} items'
},
lastLogin: 'Last login: {date}'
},
es: {
app: {
welcome: '¡Bienvenido a OnigiriJS!',
description: 'Crea aplicaciones multilingües increíbles'
},
form: {
username: 'Ingresa tu nombre de usuario',
email: 'Ingresa tu correo',
submit: 'Enviar Formulario'
},
items: {
zero: 'Sin artículos',
one: 'Un artículo',
other: '{count} artículos'
},
lastLogin: 'Último acceso: {date}'
},
fr: {
app: {
welcome: 'Bienvenue sur OnigiriJS!',
description: 'Créez des applications multilingues incroyables'
},
form: {
username: 'Entrez votre nom d\'utilisateur',
email: 'Entrez votre email',
submit: 'Soumettre le Formulaire'
},
items: {
zero: 'Aucun élément',
one: 'Un élément',
other: '{count} éléments'
},
lastLogin: 'Dernière connexion: {date}'
},
ja: {
app: {
welcome: 'OnigiriJSへようこそ!',
description: '素晴らしい多言語アプリケーションを構築'
},
form: {
username: 'ユーザー名を入力',
email: 'メールアドレスを入力',
submit: 'フォームを送信'
},
items: {
zero: 'アイテムなし',
one: '1つのアイテム',
other: '{count}個のアイテム'
},
lastLogin: '最終ログイン: {date}'
}
});
// Translate the page
Onigiri.i18n.translatePage();
// Use plural forms
const itemCount = 5;
O('#item-count').text(Onigiri.tc('items', itemCount, { count: itemCount }));
// Format dates by locale
const loginDate = new Date();
const formattedDate = Onigiri.i18n.formatDate(loginDate, 'datetime');
O('#last-login').text(Onigiri.t('lastLogin', { date: formattedDate }));
// Language switcher
O('#language-selector').on('change', function(e) {
Onigiri.i18n.setLocale(e.target.value);
// Page automatically re-translates!
});
// Listen for locale changes
document.addEventListener('onigiri:locale:changed', function(e) {
console.log('🌐 Locale changed to:', e.detail.locale);
// Update dynamic content
const count = 5;
O('#item-count').text(Onigiri.tc('items', count, { count }));
const date = Onigiri.i18n.formatDate(new Date(), 'datetime');
O('#last-login').text(Onigiri.t('lastLogin', { date }));
});
</script>
🍙 Pro Tip: Translations are automatically saved to localStorage and persist across sessions!
✅ Form Validation Example
<form id="registration-form">
<input type="text" name="username" placeholder="Username">
<input type="email" name="email" placeholder="Email">
<input type="password" name="password" placeholder="Password">
<input type="number" name="age" placeholder="Age">
<button type="submit">Register</button>
</form>
<script>
const form = O('#registration-form');
form.on('submit', async function(e) {
e.preventDefault();
// Validate form
const result = O(this).validate({
username: {
required: true,
minLength: 3,
maxLength: 20,
alphanumeric: true
},
email: {
required: true,
email: true
},
password: {
required: true,
minLength: 8,
pattern: '^(?=.*[A-Z])(?=.*[0-9])'
},
age: {
required: true,
numeric: true,
min: 18,
max: 120
}
});
if (result.isValid) {
// Submit with CSRF protection
const formData = {
username: O('[name="username"]').val(),
email: O('[name="email"]').val(),
password: O('[name="password"]').val(),
age: O('[name="age"]').val()
};
await Onigiri.post('/api/register', formData);
alert('🍙 Registration successful!');
} else {
// Show validation errors
Object.keys(result.errors).forEach(field => {
console.log(\`\${field}: \${result.errors[field].join(', ')}\`);
});
}
});
</script>
🎨 Animation Example
// Smooth animations built-in
O('.message').fadeIn(300, function() {
console.log('Fade in complete!');
});
O('.alert').fadeOut(500);
O('.menu').slideDown(400);
O('.dropdown').slideUp(300);
// Toggle with animation
const toggleMenu = () => {
const menu = O('.mobile-menu');
if (menu.hasClass('open')) {
menu.removeClass('open').slideUp(300);
} else {
menu.addClass('open').slideDown(300);
}
};
💾 Storage with Expiration
// Set storage prefix for your app
Onigiri.storage.setPrefix('myapp_');
// Save data
Onigiri.storage.set('user', {
name: 'Chef',
favoriteOnigiri: 'salmon'
});
// Save with 1 hour expiration
Onigiri.storage.set('session', userData, {
expires: 3600000 // milliseconds
});
// Get with default value
const user = Onigiri.storage.get('user', { name: 'Guest' });
// Session storage (cleared on tab close)
Onigiri.storage.session.set('tempCart', cartItems);
// Get all keys with prefix
const allUserData = Onigiri.storage.getAll('user_');
📡 AJAX with Auto-CSRF
// GET request
const users = await Onigiri.get('/api/users');
// POST request with CSRF token (automatic)
await Onigiri.post('/api/users', {
name: 'John Doe',
email: 'john@example.com'
});
// PUT request
await Onigiri.put('/api/users/1', {
name: 'Jane Doe'
});
// DELETE request
await Onigiri.delete('/api/users/1');
// Custom AJAX with options
Onigiri.ajax({
url: '/api/endpoint',
method: 'POST',
data: { key: 'value' },
csrf: true, // default
timeout: 5000
}).then(response => {
console.log('Success:', response);
}).catch(error => {
console.error('Error:', error);
});
🔄 PJAX Navigation
// Initialize PJAX
Onigiri.pjax.init({
timeout: 5000,
scrollTo: 0
});
// HTML markup
<div id="pjax-container">
<!-- Content loaded here -->
</div>
<a href="/page" data-pjax="#pjax-container">
Load Page via PJAX
</a>
// Listen to PJAX events
document.addEventListener('onigiri:pjax:complete', (e) => {
console.log('🍙 Page loaded:', e.detail.url);
// Re-initialize components and translations
Onigiri.i18n.translatePage();
});
🍙 Pro Tip: All AJAX requests and form submissions automatically include CSRF tokens when the security module is initialized!
🔌 HumHub Integration with i18n
// Create a multilingual HumHub module component
Onigiri.humhub('onigiriWidget', {
selector: '.onigiri-widget',
pjax: true, // Auto-remount on PJAX
data: {
items: [],
loading: false
},
methods: {
async loadItems() {
this.loading = true;
try {
const data = await Onigiri.get('/onigiri/api/items');
this.items = data;
} catch (error) {
console.error('Failed to load items:', error);
} finally {
this.loading = false;
}
}
},
created() {
// Auto-detect HumHub language
if (typeof humhub !== 'undefined') {
const locale = humhub.config.get('language');
Onigiri.i18n.setLocale(locale);
}
console.log('🍙 HumHub widget created!');
},
mounted() {
// Translate page elements
Onigiri.i18n.translatePage();
this.loadItems();
}
});
🎯 Complete Multilingual Example
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="csrf-token" content="<?= csrf_token() ?>">
<title>OnigiriJS Demo</title>
</head>
<body>
<div id="app">
<h1 data-i18n="app.title"></h1>
<select id="lang">
<option value="en">English</option>
<option value="es">Español</option>
<option value="ja">日本語</option>
</select>
</div>
<!-- Load OnigiriJS modules -->
<script src="onigiri-core.js"></script>
<script src="onigiri-events.js"></script>
<script src="onigiri-components.js"></script>
<script src="onigiri-security.js"></script>
<script src="onigiri-ajax.js"></script>
<script src="onigiri-storage.js"></script>
<script src="onigiri-animate.js"></script>
<script src="onigiri-translation.js"></script>
<script>
// Initialize security and translations
Onigiri.security.init({ autoInjectCSRF: true });
Onigiri.i18n.init({ locale: 'en', autoDetect: true });
// Add translations
Onigiri.i18n.addMessages({
en: { app: { title: 'Hello OnigiriJS! 🍙' } },
es: { app: { title: '¡Hola OnigiriJS! 🍙' } },
ja: { app: { title: 'こんにちは OnigiriJS! 🍙' } }
});
// Your application code
const app = new Onigiri.prototype.Component({
data: {
message: 'Hello OnigiriJS! 🍙'
},
template: function() {
return \`<h1>\${this.message}</h1>\`;
},
mounted() {
// Translate page
Onigiri.i18n.translatePage();
// Language switcher
O('#lang').on('change', (e) => {
Onigiri.i18n.setLocale(e.target.value);
});
console.log('🍙 App mounted successfully!');
}
});
app.mount('#app');
</script>
</body>
</html>
Next Steps
✅ 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.