diff --git a/framework/core/js/package-lock.json b/framework/core/js/package-lock.json
index 1eb3fe378..db8dbabeb 100644
--- a/framework/core/js/package-lock.json
+++ b/framework/core/js/package-lock.json
@@ -5153,6 +5153,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
+ "lodash-es": {
+ "version": "4.17.10",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.10.tgz",
+ "integrity": "sha512-iesFYPmxYYGTcmQK0sL8bX3TGHyM6b2qREaB4kamHfQyfPJP0xgoGxp19nsH16nsfquLdiyKyX3mQkfiSGV8Rg=="
+ },
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
diff --git a/framework/core/js/package.json b/framework/core/js/package.json
index 62604b61f..b9d25ff98 100644
--- a/framework/core/js/package.json
+++ b/framework/core/js/package.json
@@ -9,6 +9,7 @@
"flarum-webpack-config": "^0.1.0-beta.8",
"jquery": "^3.3.1",
"jquery.hotkeys": "^0.1.0",
+ "lodash-es": "^4.17.10",
"m.attrs.bidi": "github:tobscure/m.attrs.bidi",
"mithril": "^0.2.8",
"moment": "^2.22.2",
diff --git a/framework/core/js/src/admin/AdminApplication.js b/framework/core/js/src/admin/AdminApplication.js
index 6212eec09..ca71ee2c2 100644
--- a/framework/core/js/src/admin/AdminApplication.js
+++ b/framework/core/js/src/admin/AdminApplication.js
@@ -17,13 +17,10 @@ export default class AdminApplication extends Application {
}
};
- /**
- * @inheritdoc
- */
- boot(data) {
- routes(this);
+ constructor() {
+ super();
- super.boot(data);
+ routes(this);
}
/**
diff --git a/framework/core/js/src/admin/index.js b/framework/core/js/src/admin/index.js
index ca7d19d02..b17a192ef 100644
--- a/framework/core/js/src/admin/index.js
+++ b/framework/core/js/src/admin/index.js
@@ -1,13 +1,3 @@
-import 'expose-loader?$!expose-loader?jQuery!jquery';
-import 'expose-loader?m!mithril';
-import 'expose-loader?moment!moment';
-import 'expose-loader?m.bidi!m.attrs.bidi';
-import 'bootstrap/js/affix';
-import 'bootstrap/js/dropdown';
-import 'bootstrap/js/modal';
-import 'bootstrap/js/tooltip';
-import 'bootstrap/js/transition';
-
import AdminApplication from './AdminApplication';
const app = new AdminApplication();
diff --git a/framework/core/js/src/common/Application.js b/framework/core/js/src/common/Application.js
index 05df4d6cb..4a7799110 100644
--- a/framework/core/js/src/common/Application.js
+++ b/framework/core/js/src/common/Application.js
@@ -10,7 +10,6 @@ import Session from './Session';
import extract from './utils/extract';
import Drawer from './utils/Drawer';
import mapRoutes from './utils/mapRoutes';
-import patchMithril from './utils/patchMithril';
import RequestError from './utils/RequestError';
import ScrollListener from './utils/ScrollListener';
import { extend } from './extend';
@@ -21,6 +20,7 @@ import Discussion from './models/Discussion';
import Post from './models/Post';
import Group from './models/Group';
import Notification from './models/Notification';
+import { flattenDeep } from 'lodash-es';
/**
* The `App` class provides a container for an application, as well as various
@@ -115,16 +115,17 @@ export default class Application {
*/
requestError = null;
+ data;
+
title = '';
titleCount = 0;
- boot(data) {
- this.data = data;
-
- this.translator.locale = data.locale;
-
- patchMithril(window);
+ load(payload) {
+ this.data = payload;
+ this.translator.locale = payload.locale;
+ }
+ boot() {
this.initializers.toArray().forEach(initializer => initializer(this));
this.store.pushPayload({data: this.data.resources});
@@ -139,6 +140,18 @@ export default class Application {
this.mount();
}
+ bootExtensions(extensions) {
+ Object.keys(extensions).forEach(name => {
+ const extension = extensions[name];
+
+ const extenders = flattenDeep(extension.extend);
+
+ for (const extender of extenders) {
+ extender.extend(this, { name, exports: extension });
+ }
+ });
+ }
+
mount() {
this.modal = m.mount(document.getElementById('modal'), );
this.alerts = m.mount(document.getElementById('alerts'), );
diff --git a/framework/core/js/src/common/extend/Model.js b/framework/core/js/src/common/extend/Model.js
new file mode 100644
index 000000000..240be8384
--- /dev/null
+++ b/framework/core/js/src/common/extend/Model.js
@@ -0,0 +1,41 @@
+export default class Routes {
+ type;
+ attributes = [];
+ hasOnes = [];
+ hasManys = [];
+
+ constructor(type, model = null) {
+ this.type = type;
+ this.model = model;
+ }
+
+ attribute(name) {
+ this.attributes.push(name);
+
+ return this;
+ }
+
+ hasOne(type) {
+ this.hasOnes.push(type);
+
+ return this;
+ }
+
+ hasMany(type) {
+ this.hasManys.push(type);
+
+ return this;
+ }
+
+ extend(app, extension) {
+ if (this.model) {
+ app.store.models[this.type] = this.model;
+ }
+
+ const model = app.store.models[this.type];
+
+ this.attributes.forEach(name => model.prototype[name] = model.attribute(name));
+ this.hasOnes.forEach(name => model.prototype[name] = model.hasOne(name));
+ this.hasManys.forEach(name => model.prototype[name] = model.hasMany(name));
+ }
+}
\ No newline at end of file
diff --git a/framework/core/js/src/common/extend/PostTypes.js b/framework/core/js/src/common/extend/PostTypes.js
new file mode 100644
index 000000000..4d9df731f
--- /dev/null
+++ b/framework/core/js/src/common/extend/PostTypes.js
@@ -0,0 +1,13 @@
+export default class PostTypes {
+ postComponents = {};
+
+ add(name, component) {
+ this.postComponents[name] = component;
+
+ return this;
+ }
+
+ extend(app, extension) {
+ Object.assign(app.postComponents, this.postComponents);
+ }
+}
\ No newline at end of file
diff --git a/framework/core/js/src/common/extend/Routes.js b/framework/core/js/src/common/extend/Routes.js
new file mode 100644
index 000000000..013e52836
--- /dev/null
+++ b/framework/core/js/src/common/extend/Routes.js
@@ -0,0 +1,13 @@
+export default class Routes {
+ routes = {};
+
+ add(name, path, component) {
+ this.routes[name] = { path, component };
+
+ return this;
+ }
+
+ extend(app, extension) {
+ Object.assign(app.routes, this.routes);
+ }
+}
\ No newline at end of file
diff --git a/framework/core/js/src/common/extend/index.js b/framework/core/js/src/common/extend/index.js
new file mode 100644
index 000000000..6d17eb79a
--- /dev/null
+++ b/framework/core/js/src/common/extend/index.js
@@ -0,0 +1,3 @@
+export { default as Model } from './Model';
+export { default as PostTypes } from './PostTypes';
+export { default as Routes } from './Routes';
diff --git a/framework/core/js/src/common/index.js b/framework/core/js/src/common/index.js
index e69de29bb..00bc255bd 100644
--- a/framework/core/js/src/common/index.js
+++ b/framework/core/js/src/common/index.js
@@ -0,0 +1,18 @@
+import 'expose-loader?$!expose-loader?jQuery!jquery';
+import 'expose-loader?m!mithril';
+import 'expose-loader?moment!moment';
+import 'expose-loader?m.bidi!m.attrs.bidi';
+import 'bootstrap/js/affix';
+import 'bootstrap/js/dropdown';
+import 'bootstrap/js/modal';
+import 'bootstrap/js/tooltip';
+import 'bootstrap/js/transition';
+import 'jquery.hotkeys/jquery.hotkeys';
+
+import patchMithril from './utils/patchMithril';
+
+patchMithril(window);
+
+import * as Extend from './extend/index';
+
+export { Extend };
diff --git a/framework/core/js/src/forum/ForumApplication.js b/framework/core/js/src/forum/ForumApplication.js
index e3c4ca5a2..34d685843 100644
--- a/framework/core/js/src/forum/ForumApplication.js
+++ b/framework/core/js/src/forum/ForumApplication.js
@@ -63,13 +63,10 @@ export default class ForumApplication extends Application {
*/
history = new History();
- /**
- * @inheritdoc
- */
- boot(data) {
- routes(this);
+ constructor() {
+ super();
- super.boot(data);
+ routes(this);
}
/**
diff --git a/framework/core/js/src/forum/index.js b/framework/core/js/src/forum/index.js
index f5946c7fe..2a5e26fbd 100644
--- a/framework/core/js/src/forum/index.js
+++ b/framework/core/js/src/forum/index.js
@@ -1,15 +1,5 @@
-import 'expose-loader?$!expose-loader?jQuery!jquery';
-import 'expose-loader?m!mithril';
-import 'expose-loader?moment!moment';
import 'expose-loader?punycode!punycode';
import 'expose-loader?ColorThief!color-thief-browser';
-import 'expose-loader?m.bidi!m.attrs.bidi';
-import 'bootstrap/js/affix';
-import 'bootstrap/js/dropdown';
-import 'bootstrap/js/modal';
-import 'bootstrap/js/tooltip';
-import 'bootstrap/js/transition';
-import 'jquery.hotkeys/jquery.hotkeys';
import ForumApplication from './ForumApplication';
diff --git a/framework/core/src/Extend/Assets.php b/framework/core/src/Extend/Assets.php
index 0b8f70419..bd5fb6592 100644
--- a/framework/core/src/Extend/Assets.php
+++ b/framework/core/src/Extend/Assets.php
@@ -57,7 +57,7 @@ class Assets implements ExtenderInterface
$event->view->getJs()->addString(function () use ($extension) {
$name = $extension->getId();
- return 'var module={};'.file_get_contents($this->js).";flarum.extensions['$name']=module.exports";
+ return 'var module={};'.file_get_contents($this->js).";\nflarum.extensions['$name']=module.exports";
});
}
}
diff --git a/framework/core/views/frontend/app.blade.php b/framework/core/views/frontend/app.blade.php
index 1cc8c02d2..4d69331e3 100644
--- a/framework/core/views/frontend/app.blade.php
+++ b/framework/core/views/frontend/app.blade.php
@@ -30,7 +30,7 @@
@if ($allowJs)
@foreach ($jsUrls as $url)
@@ -42,7 +42,9 @@
@if (! $debug)
try {
@endif
- flarum.core.app.boot(@json($payload));
+ flarum.core.app.load(@json($payload));
+ flarum.core.app.bootExtensions(flarum.extensions);
+ flarum.core.app.boot();
@if (! $debug)
} catch (e) {
window.location += (window.location.search ? '&' : '?') + 'nojs=1';