What to copy
The encoder is the only required file. It has no packages, database calls, network requests, or Build Planner service dependency. Put the matching file in your own repository and deploy it with the rest of your site.
Browser and Node
Python 3
PHP 7.3+
The form pages, app.js, example.css, and example-catalog.js are reference material. Use them if they save you time, but they are not required by the encoder.
Recommended setup
Save the JSON, not just the generated code. That lets a writer reopen the editor, lets your revision system show changes, and lets you regenerate codes after updating the encoder.
Browser or Node integration
Load gbp1.js after your normal form code. Build a plain object from your form state and call the encoder. A failed validation throws an Error, so keep the copy button disabled until encoding succeeds.
<script src="/assets/build-planner/gbp1.js"></script>
<script>
function refreshBuildPlannerExport() {
const buildObject = readBuildFromYourEditor();
const output = document.querySelector("#build-planner-code");
const copyButton = document.querySelector("#copy-build-planner-code");
try {
output.value = GBP1.encodeBuild(buildObject);
copyButton.disabled = false;
} catch (error) {
output.value = "";
copyButton.disabled = true;
showBuildError(error.message);
}
}
</script>
For Node or a bundler using CommonJS:
const { encodeBuild } = require("./gbp1.js");
const code = encodeBuild(buildObject);
A small valid object
Most sections may be empty while a writer is getting started. The root build and each setup still need a name.
const buildObject = {
name: "Stamina Warden",
role: "Damage",
author: "Guide Author",
sourceUrl: "https://example.com/guides/stamina-warden",
selectedSetup: 1,
setups: [{
name: "General setup",
defaultQuality: 5,
defaultLevel: 50,
defaultChampionPoints: 160,
equipment: {},
alternatives: {},
skillBars: { front: [], back: [] },
character: {
attributes: { health: 0, magicka: 0, stamina: 64 },
subclassLines: []
},
champion: {},
consumables: [],
checklist: [],
buffAssumptions: {}
}]
};
React, Angular, Vue, and WordPress
These examples keep the encoder inside the same application as the guide editor. They use the existing site's accounts and permissions; none of them sends build data to gravvy.net.
React
Put gbp1.js in public/vendor/build-planner/, then load it once from your application's HTML template:
<script src="/vendor/build-planner/gbp1.js"></script>
Make the export a derived value from the build already held by your editor. Do not put the generated code in a second editable state.
function encodeForBuildPlanner(build) {
try {
return { code: window.GBP1.encodeBuild(build), error: "" };
} catch (error) {
return { code: "", error: error.message };
}
}
export function BuildPlannerExport({ build }) {
const result = encodeForBuildPlanner(build);
return (
<section>
<label htmlFor="gbp1-code">Build Planner code</label>
<textarea id="gbp1-code" readOnly value={result.code} />
{result.error && <p role="alert">{result.error}</p>}
<button
type="button"
disabled={!result.code}
onClick={() => navigator.clipboard.writeText(result.code)}
>
Copy code
</button>
</section>
);
}
For TypeScript, declare the small global API in a project declaration file:
declare global {
interface Window {
GBP1: {
encodeBuild(build: unknown): string;
VERSION: number;
PREFIX: string;
};
}
}
export {};
Angular
Copy the file to src/assets/vendor/build-planner/gbp1.js and add it to the application's scripts list in angular.json. Angular loads entries there like scripts added to index.html. Wrap the global in a service so components do not depend on it directly.
import { Injectable } from '@angular/core';
declare const GBP1: {
encodeBuild(build: unknown): string;
};
@Injectable({ providedIn: 'root' })
export class BuildPlannerExportService {
encode(build: unknown): string {
return GBP1.encodeBuild(build);
}
}
Call the service after the reactive form has produced the build object. Put encoder errors into the form's existing validation summary and disable the copy action until the error is gone. See Angular's workspace configuration reference for the current scripts option.
Vue
Put gbp1.js in public/vendor/build-planner/ and load it from index.html. A computed value keeps the output in step with the editor.
<script setup>
import { computed } from 'vue';
const props = defineProps({
build: { type: Object, required: true }
});
const exportResult = computed(() => {
try {
return { code: window.GBP1.encodeBuild(props.build), error: '' };
} catch (error) {
return { code: '', error: error.message };
}
});
function copyCode() {
navigator.clipboard.writeText(exportResult.value.code);
}
</script>
<template>
<section>
<label for="gbp1-code">Build Planner code</label>
<textarea id="gbp1-code" :value="exportResult.code" readonly />
<p v-if="exportResult.error" role="alert">
{{ exportResult.error }}
</p>
<button
type="button"
:disabled="!exportResult.code"
@click="copyCode"
>
Copy code
</button>
</section>
</template>
Vite copies files from public without transforming them. If the app is mounted below the domain root, use the base path configured by the project. See Vite's static asset guide.
WordPress
For WordPress, use the PHP encoder in a small site plugin. Store the build object as post metadata on the guide post. Authors and contributors continue to use WordPress accounts, revisions, and whatever editorial plugin the site already uses.
Register the metadata once from the plugin. Replace guide with the site's post type; that post type must support revisions if the build data should follow its revision history.
add_action('init', function (): void {
register_post_meta('guide', '_build_planner', [
'type' => 'object',
'single' => true,
'show_in_rest' => false,
'revisions_enabled' => true,
'auth_callback' => static function (
bool $allowed,
string $metaKey,
int $postId
): bool {
return current_user_can('edit_post', $postId);
},
]);
});
A public guide can generate its code while rendering:
<?php
require_once __DIR__ . '/build-planner/gbp1.php';
$build = get_post_meta(get_the_ID(), '_build_planner', true);
$code = '';
$error = '';
if (is_array($build)) {
try {
$code = gbpEncodeBuild($build);
} catch (Throwable $exception) {
$error = $exception->getMessage();
}
}
?>
<?php if ($code !== ''): ?>
<label for="gbp1-code">Build Planner code</label>
<textarea id="gbp1-code" readonly><?php
echo esc_textarea($code);
?></textarea>
<?php endif; ?>
If a block-editor sidebar or custom guide editor needs a preview endpoint, register it from the same plugin and rely on WordPress capabilities:
add_action('rest_api_init', function (): void {
register_rest_route('build-planner/v1', '/encode', [
'methods' => 'POST',
'permission_callback' => static function (): bool {
return current_user_can('edit_posts');
},
'callback' => static function (WP_REST_Request $request) {
try {
$build = $request->get_json_params();
return ['code' => gbpEncodeBuild($build)];
} catch (Throwable $exception) {
return new WP_Error(
'invalid_build',
$exception->getMessage(),
['status' => 422]
);
}
},
]);
});
current_user_can('edit_post', $postId) when saving metadata. A general edit_posts check is enough for the stateless preview shown above, but it is not enough to authorize changes to a particular post. WordPress documents both endpoint permission callbacks and revision-enabled metadata.Generate on a Python or PHP backend
Server-side generation is a good fit when the guide already lives in a database. Load the stored build object, check that the current user may view or edit that guide, and return the code from your normal controller or API route.
Python
from gbp1 import encode_build
def build_planner_export(saved_guide):
# Map your saved guide record to the documented GBP1 shape.
build_dict = saved_guide["build_planner"]
return encode_build(build_dict)
PHP
require_once __DIR__ . '/gbp1.php';
// Load this through your usual repository and permission checks.
$buildArray = $guide['build_planner'];
$code = gbpEncodeBuild($buildArray);
Both functions throw when the input is invalid. Handle that the same way you handle other guide validation errors; do not publish the old code after the saved build becomes invalid.
Reuse as much of the example editor as you need
The live form demonstrates every GBP1 section, but app.js is a reference editor rather than a packaged widget. It expects the IDs and containers found in the example pages and loads example-build.json for its sample data.
If your site already has a component system, use the example to copy field behavior and object shapes into your own components. If it has a simple server-rendered editor, copying the form markup can be quicker.
When adapting the full form
- Copy the form containers from one of the language examples.
- Copy
app.js,example.css, and your language encoder. - Replace
example-catalog.jswith choices from your own ESO catalog. - Replace the sample-build loader with the guide JSON returned by your site.
- Save changes through your existing draft or revision endpoint.
- Keep the generated code read-only. It is output, not an editable source field.
Fit it into a volunteer writing workflow
Build Planner does not need its own contributor accounts. The person editing a build should be authorized by your site, just as they are for the written guide.
| Your site handles | The GBP1 encoder handles |
|---|---|
| Login, writer roles, guide ownership, and team access | Turning an accepted build object into an import code |
| Drafts, autosave, revisions, review, and publishing | Checking GBP1 field types, ranges, limits, and relationships |
| Your ESO catalog, search, labels, and localized names | Writing those chosen IDs and values into format 9 |
| The public guide page and its copy button | Returning a deterministic GBP1: string |
A writer can be allowed to edit a draft without being allowed to publish it. Generate a preview code for drafts, but only expose the public code from the currently published revision.
Map your guide model to GBP1
You do not have to rename your database tables or form fields. Write one mapping function at the boundary between your guide model and the encoder.
| Common guide concept | GBP1 location |
|---|---|
| Guide title, class, role, patch, author, URL | Root build fields |
| Trial, non-trial, beginner, or alternate loadout | One entry in setups |
| Gear table | setup.equipment using canonical slot keys |
| Alternate piece or fallback set | setup.alternatives[slot] |
| Front and back action bars | setup.skillBars.front and .back |
| Attributes, race, Mundus, curse, class lines | setup.character |
| Champion allocations and slots | setup.champion |
| Food, potions, poisons, or other supplies to obtain | setup.consumables |
| Passives and unlocks the player still needs | setup.checklist |
| Conditions assumed while discussing stats | setup.buffAssumptions |
The field reference lists every key, limit, and enum. The sample build shows all sections together.
Use your own ESO catalog
The small catalog shipped with the example only makes the form usable. It is not a game database. Connect the editor to the same set, item, skill, and Champion data your site already maintains.
- Store stable ESO IDs when your source provides them.
- Keep the display name as a fallback for readers and localization.
- Do not guess an ID from a name; different records and languages can share text.
- Keep item links and icon paths as data. Render writer-entered names and notes as text, not HTML.
- Filter choices by slot family so armor, jewelry, and weapon-only fields cannot be mixed.
Validation and safety
The encoders validate the GBP1 shape, but they do not replace your site's normal request security or editorial rules.
- Check authentication, guide ownership, and edit permission before reading or saving draft data.
- Use your framework's CSRF protection on form posts.
- Apply request-size limits before decoding large JSON bodies.
- Run the encoder on the server before saving or publishing when the server is authoritative.
- Escape guide titles, notes, names, and URLs when displaying them.
- Clear stale output whenever the current object fails validation.
Client-side encoding is fine for instant previews. For moderated or published guides, server-side validation gives the publication workflow one authoritative result.
Keep the integration maintainable
Vendor the encoder as a small third-party component in your repository. Record that it writes GBP1 format 9 and keep a copy of the included README near it.
- Do not edit the byte-writing logic to match your database. Map your data before calling it.
- Keep optional, site-specific fields outside the GBP1 object.
- When updating the encoder, run the supplied parity and invalid-input tests before deploying.
- Test at least one real code by importing it into the current Build Planner add-on.
Old codes remain tied to the format version written into them. Your saved JSON should stay independent of that encoded output.
Before you ship
- The encoder file is served from your own application or asset host.
- Writers use your existing login, draft, and review workflow.
- The stored JSON can reopen the editor without reading the GBP1 code.
- Every setup maps to the correct gear, skills, Champion Points, supplies, and notes.
- Your own ESO catalog has replaced the sample records.
- Invalid builds cannot leave an old copyable code on screen.
- The public page only exposes data from the published guide revision.
- A generated code has been imported successfully in game.