JS Production Builder
Select the components your project needs and compile them into a single minified production bundle. Core modules are always included. The output overwrites view/front/themes/master/js/wf-production.min.js .
Build Configuration
Ready. Select components and click Build Bundle.
Core Files
Always included in every bundle — these provide the base runtime, DOM utilities, and internationalization support. They cannot be deselected.
| File | Description | Included |
|---|---|---|
| wf-base.js | All WF JS components extend WojoBase. Responsibilities: | always |
| wf-utils.js | Helpers used by multiple components — import only what you need. | always |
| wf-i18n.js | Simple, zero-dependency translation registry used by all WF components for any UI strings they generate (aria labels, button text, etc.). | always |
Components
Select the components to include. Only checked components are bundled — unused code is excluded to keep the output lean.
Editor Build
Compile all editor modules into a single minified file. The output overwrites view/front/themes/master/js/wf-editor.min.js. The bundle exposes window.WojoEditor with init() and WojoI18n. Set data-wf-editor-mode on the container element to control the editor mode declaratively — simple, hybrid, or advanced.
Ready. Click Build Editor Bundle to compile.
Usage
Always load the framework bundle before the editor bundle — both use defer, so they execute in declaration order after parsing completes. Wrap the footer script in DOMContentLoaded so it runs after both deferred bundles have executed. The framework auto-inits components from data-wf-* attributes; component defaults and i18n are the only things that need an explicit call. Each bundle has its own isolated WojoI18n instance — init both with the same URL; English is built-in and skipped automatically, non-English locales fetch the JSON once and the browser cache serves the second call at no extra cost.
Editor content is kept in sync with a hidden <textarea> inside your CMS form. Add data-wf-editor-for="editor" to the textarea — the editor reads the initial value from it on load and writes JSON back automatically when the form submits. No save handler or per-page JS required; the field name, form action, and method all live in HTML.
<!-- 1. Bundles — framework first, editor second (both deferred, run in declaration order) -->
<script defer src="/assets/wf-production.min.js"></script>
<script defer src="/assets/wf-editor.min.js"></script>
<!-- 2. CMS form — all fields including editor content submit together via standard POST.
Each CMS section (blog post, page, product…) sets its own action URL and fields;
the footer JS never changes. -->
<form method="post" action="/cms/api/save-post">
<input name="title" value="<?= htmlspecialchars($row['title']) ?>">
<input name="slug" value="<?= htmlspecialchars($row['slug']) ?>">
<!-- Hidden textarea linked to the editor by matching data-wf-editor-for to the editor id.
• Initial value: PHP renders DB content here (plain HTML or editor JSON).
• On submit: editor writes its current JSON into this field automatically.
• On server: read $_POST['content'] like any other form field. -->
<textarea name="content" data-wf-editor-for="editor" hidden><?= $row['content'] ?></textarea>
<!-- Editor container — mode and toolbar position come from data attributes -->
<div id="editor" data-wf-editor-mode="hybrid" data-wf-toolbar-position="attached"></div>
<!-- Toolbar is optional — remove the element entirely if not needed -->
<div id="editor-toolbar" class="be-editor-toolbar" role="banner">
<div class="be-editor-toolbar-slot" data-be-toolbar-slot="history"></div>
<div class="be-toolbar-spacer"></div>
<div class="be-editor-toolbar-slot" data-be-toolbar-slot="responsive"></div>
<div class="be-editor-toolbar-slot" data-be-toolbar-slot="renders"></div>
<div class="be-editor-toolbar-slot" data-be-toolbar-slot="actions"></div>
</div>
<div id="be-preview-pane" class="be-preview-pane" hidden></div>
<button type="submit">Save</button>
</form>
<!-- 3. Footer JS — identical on every CMS page; nothing here changes per section -->
<script>
document.addEventListener('DOMContentLoaded', function () {
// ── Framework ─────────────────────────────────────────────────────────────
// Components auto-init from data-wf-* attributes — no extra JS needed.
// Set global defaults for components that require project-specific config.
const { WojoDatePicker } = WojoFramework.components['wf-date-picker'];
WojoDatePicker.setDefaults({
weekStart: 1, // 0 = Sunday · 1 = Monday
format: 'dd/mm/yyyy', // 'mm/dd/yyyy' · 'yyyy-mm-dd'
});
// ── i18n ─────────────────────────────────────────────────────────────────
// Each bundle has its own WojoI18n instance — init both with the same URL.
// English (<html lang="en">) is built-in; init() is a no-op for English.
// Non-English: JSON fetched once by the first bundle, served from browser
// cache for the second — no extra network request.
const i18nUrl = '/assets/i18n/{locale}.json';
WojoFramework.core['wf-i18n'].WojoI18n.init(i18nUrl);
if (window.WojoEditor) WojoEditor.WojoI18n.init(i18nUrl);
// ── Editor ────────────────────────────────────────────────────────────────
// The editor auto-discovers the linked textarea via data-wf-editor-for="editor"
// and reads initial content from its value. On form submit the editor writes
// its current JSON back into the textarea — no save handler required.
const editorEl = document.getElementById('editor');
if (!editorEl) return;
const editor = WojoEditor.init(editorEl, {
urls: {
imageList: '/api/api-list-images.php',
imageUpload: '/api/api-upload-image.php',
videoUpload: '/api/api-upload-video.php',
linkList: '/api/api-list-links.php',
blockTemplateList: '/api/api-block-templates.php',
colorsUrl: '/themes/master/css/wf-colors.css',
iconList: '/themes/master/css/wf-icons.css',
previewCss: '/themes/master/css/main.css',
previewJs: '/assets/wf-production.min.js',
},
});
// ── Toolbar (optional) ────────────────────────────────────────────────────
const toolbarEl = document.getElementById('editor-toolbar');
if (toolbarEl) {
editor.mountToolbar({
toolbar: toolbarEl,
previewPane: document.getElementById('be-preview-pane'),
editorRoot: editorEl,
defaultOutputView: 'none',
});
}
});
</script>