Custom $dotapp library
Open the live demo at /documentation/examples/run/library.
Introduction
DotApp is not “PHP plus a random jQuery file”. The same request model (load, CRC, parseReply) is how you extend the frontend. You write a vanilla widget, then hang it on $dotapp with $dotapp().fn('name', fn). Put the file in your module assets. Never edit app/parts/js/.
The live Examples module ships exNotify (factory toasts), exCopy (per-button clipboard), exStepper (one node → API), and exPing (widget that POSTs to PHP).
Boot events
Libraries register on dotapp-register. Page scripts run on dotapp. Load order:
<script src="/assets/dotapp/dotapp.js"></script>
<script src="/assets/modules/Examples/js/ex-ui.js"></script>
<script src="/assets/modules/Examples/js/library.js"></script>
Guard double registration with isRegistered and catch an error message that contains already registered.
Registering fn()
Factory (empty $dotapp()): toasts and dialogs. Per-element: one node returns the widget API, many nodes return this.
(function () {
var isRegistered = false;
var runMe = function ($dotapp) {
if (isRegistered) return;
isRegistered = true;
$dotapp().fn("exNotify", function (opts) {
if (opts && typeof opts === "object") {
return ExNotify.show(opts);
}
return this;
});
$dotapp().fn("exStepper", function (options) {
var els = this.getElements();
if (els.length !== 1) {
throw new Error("exStepper requires exactly one element");
}
var el = els[0];
if (!el._exStepper) el._exStepper = new ExStepper(el, options || {});
return el._exStepper;
});
};
if (window.$dotapp) runMe(window.$dotapp);
else window.addEventListener("dotapp-register", function () { runMe(window.$dotapp); }, { once: true });
})();
Page code then does $dotapp().exNotify({ title: "Saved", text: "…" }) and $dotapp("#qty").exStepper(). Do not wrap $.fn.plugin. Rewrite in vanilla DOM.
Calling PHP from the widget
Inside the library, HTTP goes through this.load + parseReply. Never fetch a DotApp endpoint.
ExPing.prototype.send = function () {
var self = this;
this.dotApp.load(this.settings.url, "POST", {},
function (raw) {
var reply = self.dotApp.parseReply(raw);
ExNotify.show({ title: "PHP", text: (reply && reply.message) ? reply.message : "No reply" });
}
);
};
PHP answers with crcCheck() and ajaxReply:
public static function ping($request)
{
if (!$request->crcCheck()) {
return DotApp::DotApp()->ajaxReply(['status' => 0, 'message' => 'Bad request'], 400);
}
return DotApp::DotApp()->ajaxReply([
'status' => 1,
'message' => 'PHP answered at ' . date('H:i:s'),
], 200);
}
CSS
Keep widget classes on {modulename}_* or a shared demo sheet. The live styles are in
/app/modules/Examples/assets/css/examples.css and are served as
/assets/modules/Examples/css/examples.css. Toast stack, stepper, and snippet block live there so you can copy the look into your module.
Live demo
/documentation/examples/run/library
— toasts, copy, stepper, and a PHP ping. Source: ex-ui.js, library.js, Controllers/Library.php.