diff --git a/app/assets/javascripts/admin/helpers/human-size.js.es6 b/app/assets/javascripts/admin/helpers/human-size.js.es6
index a43897c627e..a50cfe58190 100644
--- a/app/assets/javascripts/admin/helpers/human-size.js.es6
+++ b/app/assets/javascripts/admin/helpers/human-size.js.es6
@@ -1,3 +1,3 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper(size => I18n.toHumanSize(size));
diff --git a/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6 b/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6
index 73bac433799..b58e9a552ce 100644
--- a/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6
+++ b/app/assets/javascripts/admin/helpers/preserve-newlines.js.es6
@@ -1,4 +1,4 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
import { escapeExpression } from 'discourse/lib/utilities';
export default htmlHelper(str => escapeExpression(str).replace(/\n/g, "
"));
diff --git a/app/assets/javascripts/admin/helpers/value-at-tl.js.es6 b/app/assets/javascripts/admin/helpers/value-at-tl.js.es6
index aeac49e83d6..48d2271caba 100644
--- a/app/assets/javascripts/admin/helpers/value-at-tl.js.es6
+++ b/app/assets/javascripts/admin/helpers/value-at-tl.js.es6
@@ -1,4 +1,4 @@
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
registerUnbound('value-at-tl', function(data, params) {
var tl = parseInt(params.level, 10);
diff --git a/app/assets/javascripts/discourse/helpers/fa-icon.js.es6 b/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6
similarity index 54%
rename from app/assets/javascripts/discourse/helpers/fa-icon.js.es6
rename to app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6
index 043c6a50e66..3c1f8b9b5ab 100644
--- a/app/assets/javascripts/discourse/helpers/fa-icon.js.es6
+++ b/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6
@@ -1,8 +1,7 @@
-import { h } from 'virtual-dom';
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
-function iconClasses(icon, params) {
- var classes = "fa fa-" + icon;
+export function iconClasses(icon, params) {
+ let classes = "fa fa-" + icon;
if (params.modifier) { classes += " fa-" + params.modifier; }
if (params['class']) { classes += ' ' + params['class']; }
return classes;
@@ -21,23 +20,6 @@ export function iconHTML(icon, params) {
return html;
}
-export function iconNode(icon, params) {
- params = params || {};
-
- const properties = {
- className: iconClasses(icon, params),
- attributes: { "aria-hidden": true }
- };
-
- if (params.title) { properties.attributes.title = params.title; }
-
- if (params.label) {
- return h('i', properties, h('span.sr-only', I18n.t(params.label)));
- } else {
- return h('i', properties);
- }
-}
-
registerUnbound('fa-icon', function(icon, params) {
return new Handlebars.SafeString(iconHTML(icon, params));
});
diff --git a/app/assets/javascripts/wizard/helpers/i18n.js.es6 b/app/assets/javascripts/discourse-common/helpers/i18n.js.es6
similarity index 50%
rename from app/assets/javascripts/wizard/helpers/i18n.js.es6
rename to app/assets/javascripts/discourse-common/helpers/i18n.js.es6
index 21d53160f98..455cc65a355 100644
--- a/app/assets/javascripts/wizard/helpers/i18n.js.es6
+++ b/app/assets/javascripts/discourse-common/helpers/i18n.js.es6
@@ -1,3 +1,3 @@
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
registerUnbound('i18n', (key, params) => I18n.t(key, params));
diff --git a/app/assets/javascripts/discourse/lib/helpers.js.es6 b/app/assets/javascripts/discourse-common/lib/helpers.js.es6
similarity index 96%
rename from app/assets/javascripts/discourse/lib/helpers.js.es6
rename to app/assets/javascripts/discourse-common/lib/helpers.js.es6
index 9eccf5651ca..45c065124cd 100644
--- a/app/assets/javascripts/discourse/lib/helpers.js.es6
+++ b/app/assets/javascripts/discourse-common/lib/helpers.js.es6
@@ -1,4 +1,4 @@
-import { get } from 'discourse/lib/raw-handlebars';
+import { get } from 'discourse-common/lib/raw-handlebars';
// `Ember.Helper` is only available in versions after 1.12
export function htmlHelper(fn) {
diff --git a/app/assets/javascripts/discourse/lib/raw-handlebars.js.es6 b/app/assets/javascripts/discourse-common/lib/raw-handlebars.js.es6
similarity index 100%
rename from app/assets/javascripts/discourse/lib/raw-handlebars.js.es6
rename to app/assets/javascripts/discourse-common/lib/raw-handlebars.js.es6
diff --git a/app/assets/javascripts/discourse-common/resolver.js.es6 b/app/assets/javascripts/discourse-common/resolver.js.es6
new file mode 100644
index 00000000000..fdf1a1abeab
--- /dev/null
+++ b/app/assets/javascripts/discourse-common/resolver.js.es6
@@ -0,0 +1,218 @@
+/* global requirejs, require */
+
+var classify = Ember.String.classify;
+var get = Ember.get;
+
+var LOADING_WHITELIST = ['badges', 'userActivity', 'userPrivateMessages', 'admin', 'adminFlags',
+ 'user', 'preferences', 'adminEmail', 'adminUsersList'];
+var _dummyRoute;
+var _loadingView;
+
+function loadingResolver(cb) {
+ return function(parsedName) {
+ var fullNameWithoutType = parsedName.fullNameWithoutType;
+
+ if (fullNameWithoutType.indexOf('Loading') >= 0) {
+ fullNameWithoutType = fullNameWithoutType.replace('Loading', '');
+ if (LOADING_WHITELIST.indexOf(fullNameWithoutType) !== -1) {
+ return cb(fullNameWithoutType);
+ }
+ }
+ };
+}
+
+function parseName(fullName) {
+ const nameParts = fullName.split(":"),
+ type = nameParts[0], fullNameWithoutType = nameParts[1],
+ name = fullNameWithoutType,
+ namespace = get(this, 'namespace'),
+ root = namespace;
+
+ return {
+ fullName: fullName,
+ type: type,
+ fullNameWithoutType: fullNameWithoutType,
+ name: name,
+ root: root,
+ resolveMethodName: "resolve" + classify(type)
+ };
+}
+
+export function buildResolver(baseName) {
+ return Ember.DefaultResolver.extend({
+ parseName,
+
+ resolveRouter(parsedName) {
+ const routerPath = `${baseName}/router`;
+ if (requirejs.entries[routerPath]) {
+ const module = require(routerPath, null, null, true);
+ return module.default;
+ }
+ return this._super(parsedName);
+ },
+
+ normalize(fullName) {
+ const split = fullName.split(':');
+ if (split.length > 1) {
+ const appBase = `${baseName}/${split[0]}s/`;
+ const adminBase = 'admin/' + split[0] + 's/';
+
+ // Allow render 'admin/templates/xyz' too
+ split[1] = split[1].replace('.templates', '').replace('/templates', '');
+
+ // Try slashes
+ let dashed = Ember.String.dasherize(split[1].replace(/\./g, '/'));
+ if (requirejs.entries[appBase + dashed] || requirejs.entries[adminBase + dashed]) {
+ return split[0] + ":" + dashed;
+ }
+
+ // Try with dashes instead of slashes
+ dashed = Ember.String.dasherize(split[1].replace(/\./g, '-'));
+ if (requirejs.entries[appBase + dashed] || requirejs.entries[adminBase + dashed]) {
+ return split[0] + ":" + dashed;
+ }
+ }
+ return this._super(fullName);
+ },
+
+ customResolve(parsedName) {
+ // If we end with the name we want, use it. This allows us to define components within plugins.
+ const suffix = parsedName.type + 's/' + parsedName.fullNameWithoutType,
+ dashed = Ember.String.dasherize(suffix),
+ moduleName = Object.keys(requirejs.entries).find(function(e) {
+ return (e.indexOf(suffix, e.length - suffix.length) !== -1) ||
+ (e.indexOf(dashed, e.length - dashed.length) !== -1);
+ });
+
+ var module;
+ if (moduleName) {
+ module = require(moduleName, null, null, true /* force sync */);
+ if (module && module['default']) { module = module['default']; }
+ }
+ return module;
+ },
+
+ resolveWidget(parsedName) {
+ return this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveAdapter(parsedName) {
+ return this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveModel(parsedName) {
+ return this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveView(parsedName) {
+ return this.findLoadingView(parsedName) || this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveHelper(parsedName) {
+ return this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveController(parsedName) {
+ return this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveComponent(parsedName) {
+ return this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveRoute(parsedName) {
+ return this.findLoadingRoute(parsedName) || this.customResolve(parsedName) || this._super(parsedName);
+ },
+
+ resolveTemplate(parsedName) {
+ return this.findPluginMobileTemplate(parsedName) ||
+ this.findPluginTemplate(parsedName) ||
+ this.findMobileTemplate(parsedName) ||
+ this.findTemplate(parsedName) ||
+ Ember.TEMPLATES.not_found;
+ },
+
+ findLoadingRoute: loadingResolver(function() {
+ _dummyRoute = _dummyRoute || Ember.Route.extend();
+ return _dummyRoute;
+ }),
+
+ findLoadingView: loadingResolver(function() {
+ if (!_loadingView) {
+ _loadingView = require('discourse/views/loading', null, null, true /* force sync */);
+ if (_loadingView && _loadingView['default']) { _loadingView = _loadingView['default']; }
+ }
+ return _loadingView;
+ }),
+
+ findPluginTemplate(parsedName) {
+ var pluginParsedName = this.parseName(parsedName.fullName.replace("template:", "template:javascripts/"));
+ return this.findTemplate(pluginParsedName);
+ },
+
+ findPluginMobileTemplate(parsedName) {
+ if (this.mobileView) {
+ var pluginParsedName = this.parseName(parsedName.fullName.replace("template:", "template:javascripts/mobile/"));
+ return this.findTemplate(pluginParsedName);
+ }
+ },
+
+ findMobileTemplate(parsedName) {
+ if (this.mobileView) {
+ var mobileParsedName = this.parseName(parsedName.fullName.replace("template:", "template:mobile/"));
+ return this.findTemplate(mobileParsedName);
+ }
+ },
+
+ findTemplate(parsedName) {
+ const withoutType = parsedName.fullNameWithoutType,
+ slashedType = withoutType.replace(/\./g, '/'),
+ decamelized = withoutType.decamelize(),
+ dashed = decamelized.replace(/\./g, '-').replace(/\_/g, '-'),
+ templates = Ember.TEMPLATES;
+
+ return this._super(parsedName) ||
+ templates[slashedType] ||
+ templates[withoutType] ||
+ templates[dashed] ||
+ templates[decamelized.replace(/\./, '/')] ||
+ templates[decamelized.replace(/\_/, '/')] ||
+ templates[`${baseName}/templates/${withoutType}`] ||
+ this.findAdminTemplate(parsedName) ||
+ this.findUnderscoredTemplate(parsedName);
+ },
+
+ findUnderscoredTemplate(parsedName) {
+ var decamelized = parsedName.fullNameWithoutType.decamelize();
+ var underscored = decamelized.replace(/\-/g, "_");
+ return Ember.TEMPLATES[underscored];
+ },
+
+ // Try to find a template within a special admin namespace, e.g. adminEmail => admin/templates/email
+ // (similar to how discourse lays out templates)
+ findAdminTemplate(parsedName) {
+ var decamelized = parsedName.fullNameWithoutType.decamelize();
+
+ if (decamelized.indexOf('components') === 0) {
+ const compTemplate = Ember.TEMPLATES['admin/templates/' + decamelized];
+ if (compTemplate) { return compTemplate; }
+ }
+
+ if (decamelized === "javascripts/admin") {
+ return Ember.TEMPLATES['admin/templates/admin'];
+ }
+
+ if (decamelized.indexOf('admin') === 0 || decamelized.indexOf('javascripts/admin') === 0) {
+ decamelized = decamelized.replace(/^admin\_/, 'admin/templates/');
+ decamelized = decamelized.replace(/^admin\./, 'admin/templates/');
+ decamelized = decamelized.replace(/\./g, '_');
+
+ const dashed = decamelized.replace(/_/g, '-');
+ return Ember.TEMPLATES[decamelized] ||
+ Ember.TEMPLATES[dashed] ||
+ Ember.TEMPLATES[dashed.replace('admin-', 'admin/')];
+ }
+ }
+
+ });
+}
diff --git a/app/assets/javascripts/discourse.js.es6 b/app/assets/javascripts/discourse.js.es6
index 23c233c7c4f..523b0ee2255 100644
--- a/app/assets/javascripts/discourse.js.es6
+++ b/app/assets/javascripts/discourse.js.es6
@@ -1,4 +1,4 @@
-import DiscourseResolver from 'discourse/ember/resolver';
+import { buildResolver } from 'discourse-common/resolver';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
const _pluginCallbacks = [];
@@ -31,7 +31,7 @@ const Discourse = Ember.Application.extend({
return url;
},
- Resolver: DiscourseResolver,
+ Resolver: buildResolver('discourse'),
@observes('_docTitle', 'hasFocus', 'notifyCount')
_titleChanged() {
diff --git a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6
index b2fea03ffdf..8dbb4d34231 100644
--- a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6
+++ b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6
@@ -1,4 +1,4 @@
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import DropdownButton from 'discourse/components/dropdown-button';
import computed from "ember-addons/ember-computed-decorators";
diff --git a/app/assets/javascripts/discourse/components/d-button.js.es6 b/app/assets/javascripts/discourse/components/d-button.js.es6
index 7737a995b27..66e393ba712 100644
--- a/app/assets/javascripts/discourse/components/d-button.js.es6
+++ b/app/assets/javascripts/discourse/components/d-button.js.es6
@@ -1,4 +1,4 @@
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
diff --git a/app/assets/javascripts/discourse/components/d-link.js.es6 b/app/assets/javascripts/discourse/components/d-link.js.es6
index 54b1486a081..1c94bdc8231 100644
--- a/app/assets/javascripts/discourse/components/d-link.js.es6
+++ b/app/assets/javascripts/discourse/components/d-link.js.es6
@@ -1,5 +1,5 @@
import computed from 'ember-addons/ember-computed-decorators';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import interceptClick from 'discourse/lib/intercept-click';
export default Ember.Component.extend({
diff --git a/app/assets/javascripts/discourse/components/directory-toggle.js.es6 b/app/assets/javascripts/discourse/components/directory-toggle.js.es6
index f0e0192e178..168db163c58 100644
--- a/app/assets/javascripts/discourse/components/directory-toggle.js.es6
+++ b/app/assets/javascripts/discourse/components/directory-toggle.js.es6
@@ -1,5 +1,5 @@
import StringBuffer from 'discourse/mixins/string-buffer';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
export default Ember.Component.extend(StringBuffer, {
tagName: 'th',
diff --git a/app/assets/javascripts/discourse/components/global-notice.js.es6 b/app/assets/javascripts/discourse/components/global-notice.js.es6
index 13994f28fbf..0e76a27e32a 100644
--- a/app/assets/javascripts/discourse/components/global-notice.js.es6
+++ b/app/assets/javascripts/discourse/components/global-notice.js.es6
@@ -1,6 +1,6 @@
import { on } from 'ember-addons/ember-computed-decorators';
import StringBuffer from 'discourse/mixins/string-buffer';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import LogsNotice from 'discourse/services/logs-notice';
export default Ember.Component.extend(StringBuffer, {
diff --git a/app/assets/javascripts/discourse/components/input-tip.js.es6 b/app/assets/javascripts/discourse/components/input-tip.js.es6
index 2ba1c074c4e..cb1bad446ef 100644
--- a/app/assets/javascripts/discourse/components/input-tip.js.es6
+++ b/app/assets/javascripts/discourse/components/input-tip.js.es6
@@ -1,5 +1,5 @@
import StringBuffer from 'discourse/mixins/string-buffer';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
export default Ember.Component.extend(StringBuffer, {
classNameBindings: [':tip', 'good', 'bad'],
diff --git a/app/assets/javascripts/discourse/components/notifications-button.js.es6 b/app/assets/javascripts/discourse/components/notifications-button.js.es6
index 8a3ffda21e2..2695cfc9186 100644
--- a/app/assets/javascripts/discourse/components/notifications-button.js.es6
+++ b/app/assets/javascripts/discourse/components/notifications-button.js.es6
@@ -1,6 +1,6 @@
import DropdownButton from 'discourse/components/dropdown-button';
import { allLevels, buttonDetails } from 'discourse/lib/notification-levels';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import computed from 'ember-addons/ember-computed-decorators';
export default DropdownButton.extend({
diff --git a/app/assets/javascripts/discourse/components/popup-input-tip.js.es6 b/app/assets/javascripts/discourse/components/popup-input-tip.js.es6
index c63997674c9..2c164ddf5eb 100644
--- a/app/assets/javascripts/discourse/components/popup-input-tip.js.es6
+++ b/app/assets/javascripts/discourse/components/popup-input-tip.js.es6
@@ -1,5 +1,5 @@
import StringBuffer from 'discourse/mixins/string-buffer';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend(StringBuffer, {
diff --git a/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6 b/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6
index c90efb35f9a..6d1c7ae7b31 100644
--- a/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6
+++ b/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6
@@ -1,4 +1,4 @@
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import DropdownButton from 'discourse/components/dropdown-button';
import computed from "ember-addons/ember-computed-decorators";
diff --git a/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 b/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6
index bd7ce01b141..a9ea76d56c0 100644
--- a/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6
+++ b/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6
@@ -1,4 +1,4 @@
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import Combobox from 'discourse/components/combo-box';
import { on, observes } from 'ember-addons/ember-computed-decorators';
diff --git a/app/assets/javascripts/discourse/components/topic-status.js.es6 b/app/assets/javascripts/discourse/components/topic-status.js.es6
index e8fc527cceb..2fa076736fc 100644
--- a/app/assets/javascripts/discourse/components/topic-status.js.es6
+++ b/app/assets/javascripts/discourse/components/topic-status.js.es6
@@ -1,4 +1,4 @@
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
import StringBuffer from 'discourse/mixins/string-buffer';
import { escapeExpression } from 'discourse/lib/utilities';
diff --git a/app/assets/javascripts/discourse/ember/resolver.js.es6 b/app/assets/javascripts/discourse/ember/resolver.js.es6
deleted file mode 100644
index 7e59a326b8e..00000000000
--- a/app/assets/javascripts/discourse/ember/resolver.js.es6
+++ /dev/null
@@ -1,206 +0,0 @@
-/* global requirejs, require */
-
-var classify = Ember.String.classify;
-var get = Ember.get;
-
-var LOADING_WHITELIST = ['badges', 'userActivity', 'userPrivateMessages', 'admin', 'adminFlags',
- 'user', 'preferences', 'adminEmail', 'adminUsersList'];
-var _dummyRoute;
-var _loadingView;
-
-function loadingResolver(cb) {
- return function(parsedName) {
- var fullNameWithoutType = parsedName.fullNameWithoutType;
-
- if (fullNameWithoutType.indexOf('Loading') >= 0) {
- fullNameWithoutType = fullNameWithoutType.replace('Loading', '');
- if (LOADING_WHITELIST.indexOf(fullNameWithoutType) !== -1) {
- return cb(fullNameWithoutType);
- }
- }
- };
-}
-
-function parseName(fullName) {
- const nameParts = fullName.split(":"),
- type = nameParts[0], fullNameWithoutType = nameParts[1],
- name = fullNameWithoutType,
- namespace = get(this, 'namespace'),
- root = namespace;
-
- return {
- fullName: fullName,
- type: type,
- fullNameWithoutType: fullNameWithoutType,
- name: name,
- root: root,
- resolveMethodName: "resolve" + classify(type)
- };
-}
-
-export default Ember.DefaultResolver.extend({
- parseName: parseName,
-
- normalize(fullName) {
- var split = fullName.split(':');
- if (split.length > 1) {
- var discourseBase = 'discourse/' + split[0] + 's/';
- var adminBase = 'admin/' + split[0] + 's/';
-
- // Allow render 'admin/templates/xyz' too
- split[1] = split[1].replace('.templates', '').replace('/templates', '');
-
- // Try slashes
- var dashed = Ember.String.dasherize(split[1].replace(/\./g, '/'));
- if (requirejs.entries[discourseBase + dashed] || requirejs.entries[adminBase + dashed]) {
- return split[0] + ":" + dashed;
- }
-
- // Try with dashes instead of slashes
- dashed = Ember.String.dasherize(split[1].replace(/\./g, '-'));
- if (requirejs.entries[discourseBase + dashed] || requirejs.entries[adminBase + dashed]) {
- return split[0] + ":" + dashed;
- }
- }
- return this._super(fullName);
- },
-
- customResolve(parsedName) {
- // If we end with the name we want, use it. This allows us to define components within plugins.
- const suffix = parsedName.type + 's/' + parsedName.fullNameWithoutType,
- dashed = Ember.String.dasherize(suffix),
- moduleName = Object.keys(requirejs.entries).find(function(e) {
- return (e.indexOf(suffix, e.length - suffix.length) !== -1) ||
- (e.indexOf(dashed, e.length - dashed.length) !== -1);
- });
-
- var module;
- if (moduleName) {
- module = require(moduleName, null, null, true /* force sync */);
- if (module && module['default']) { module = module['default']; }
- }
- return module;
- },
-
- resolveWidget(parsedName) {
- return this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveAdapter(parsedName) {
- return this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveModel(parsedName) {
- return this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveView(parsedName) {
- return this.findLoadingView(parsedName) || this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveHelper(parsedName) {
- return this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveController(parsedName) {
- return this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveComponent(parsedName) {
- return this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveRoute(parsedName) {
- return this.findLoadingRoute(parsedName) || this.customResolve(parsedName) || this._super(parsedName);
- },
-
- resolveTemplate(parsedName) {
- return this.findPluginMobileTemplate(parsedName) ||
- this.findPluginTemplate(parsedName) ||
- this.findMobileTemplate(parsedName) ||
- this.findTemplate(parsedName) ||
- Ember.TEMPLATES.not_found;
- },
-
- findLoadingRoute: loadingResolver(function() {
- _dummyRoute = _dummyRoute || Ember.Route.extend();
- return _dummyRoute;
- }),
-
- findLoadingView: loadingResolver(function() {
- if (!_loadingView) {
- _loadingView = require('discourse/views/loading', null, null, true /* force sync */);
- if (_loadingView && _loadingView['default']) { _loadingView = _loadingView['default']; }
- }
- return _loadingView;
- }),
-
- findPluginTemplate(parsedName) {
- var pluginParsedName = this.parseName(parsedName.fullName.replace("template:", "template:javascripts/"));
- return this.findTemplate(pluginParsedName);
- },
-
- findPluginMobileTemplate(parsedName) {
- if (this.mobileView) {
- var pluginParsedName = this.parseName(parsedName.fullName.replace("template:", "template:javascripts/mobile/"));
- return this.findTemplate(pluginParsedName);
- }
- },
-
- findMobileTemplate(parsedName) {
- if (this.mobileView) {
- var mobileParsedName = this.parseName(parsedName.fullName.replace("template:", "template:mobile/"));
- return this.findTemplate(mobileParsedName);
- }
- },
-
- findTemplate(parsedName) {
- const withoutType = parsedName.fullNameWithoutType,
- slashedType = withoutType.replace(/\./g, '/'),
- decamelized = withoutType.decamelize(),
- dashed = decamelized.replace(/\./g, '-').replace(/\_/g, '-'),
- templates = Ember.TEMPLATES;
-
- return this._super(parsedName) ||
- templates[slashedType] ||
- templates[withoutType] ||
- templates[dashed] ||
- templates[decamelized.replace(/\./, '/')] ||
- templates[decamelized.replace(/\_/, '/')] ||
- this.findAdminTemplate(parsedName) ||
- this.findUnderscoredTemplate(parsedName);
- },
-
- findUnderscoredTemplate(parsedName) {
- var decamelized = parsedName.fullNameWithoutType.decamelize();
- var underscored = decamelized.replace(/\-/g, "_");
- return Ember.TEMPLATES[underscored];
- },
-
- // Try to find a template within a special admin namespace, e.g. adminEmail => admin/templates/email
- // (similar to how discourse lays out templates)
- findAdminTemplate(parsedName) {
- var decamelized = parsedName.fullNameWithoutType.decamelize();
-
- if (decamelized.indexOf('components') === 0) {
- const compTemplate = Ember.TEMPLATES['admin/templates/' + decamelized];
- if (compTemplate) { return compTemplate; }
- }
-
- if (decamelized === "javascripts/admin") {
- return Ember.TEMPLATES['admin/templates/admin'];
- }
-
- if (decamelized.indexOf('admin') === 0 || decamelized.indexOf('javascripts/admin') === 0) {
- decamelized = decamelized.replace(/^admin\_/, 'admin/templates/');
- decamelized = decamelized.replace(/^admin\./, 'admin/templates/');
- decamelized = decamelized.replace(/\./g, '_');
-
- const dashed = decamelized.replace(/_/g, '-');
- return Ember.TEMPLATES[decamelized] ||
- Ember.TEMPLATES[dashed] ||
- Ember.TEMPLATES[dashed.replace('admin-', 'admin/')];
- }
- }
-
-});
diff --git a/app/assets/javascripts/discourse/helpers/application.js.es6 b/app/assets/javascripts/discourse/helpers/application.js.es6
index 2bedeb4b0bd..05306c0e62f 100644
--- a/app/assets/javascripts/discourse/helpers/application.js.es6
+++ b/app/assets/javascripts/discourse/helpers/application.js.es6
@@ -1,4 +1,4 @@
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
import { longDate, autoUpdatingRelativeAge, number } from 'discourse/lib/formatter';
const safe = Handlebars.SafeString;
diff --git a/app/assets/javascripts/discourse/helpers/border-color.js.es6 b/app/assets/javascripts/discourse/helpers/border-color.js.es6
index 7d6d2286760..4c327a8190e 100644
--- a/app/assets/javascripts/discourse/helpers/border-color.js.es6
+++ b/app/assets/javascripts/discourse/helpers/border-color.js.es6
@@ -1,3 +1,3 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper(color => `border-color: #${color}`);
diff --git a/app/assets/javascripts/discourse/helpers/bound-avatar-template.js.es6 b/app/assets/javascripts/discourse/helpers/bound-avatar-template.js.es6
index 31f5611c755..5682cd2fbdc 100644
--- a/app/assets/javascripts/discourse/helpers/bound-avatar-template.js.es6
+++ b/app/assets/javascripts/discourse/helpers/bound-avatar-template.js.es6
@@ -1,4 +1,4 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
import { avatarImg } from 'discourse/lib/utilities';
export default htmlHelper((avatarTemplate, size) => avatarImg({ size, avatarTemplate }));
diff --git a/app/assets/javascripts/discourse/helpers/bound-avatar.js.es6 b/app/assets/javascripts/discourse/helpers/bound-avatar.js.es6
index 79639c8a16a..2833f8cefb1 100644
--- a/app/assets/javascripts/discourse/helpers/bound-avatar.js.es6
+++ b/app/assets/javascripts/discourse/helpers/bound-avatar.js.es6
@@ -1,4 +1,4 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
import { avatarImg } from 'discourse/lib/utilities';
export default htmlHelper((user, size) => {
diff --git a/app/assets/javascripts/discourse/helpers/bound-category-link.js.es6 b/app/assets/javascripts/discourse/helpers/bound-category-link.js.es6
index 5d215405b26..a08d97d8876 100644
--- a/app/assets/javascripts/discourse/helpers/bound-category-link.js.es6
+++ b/app/assets/javascripts/discourse/helpers/bound-category-link.js.es6
@@ -1,4 +1,4 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
import { categoryLinkHTML } from 'discourse/helpers/category-link';
export default htmlHelper(categoryLinkHTML);
diff --git a/app/assets/javascripts/discourse/helpers/bound-date.js.es6 b/app/assets/javascripts/discourse/helpers/bound-date.js.es6
index b4f75cffc4c..beab3e1730a 100644
--- a/app/assets/javascripts/discourse/helpers/bound-date.js.es6
+++ b/app/assets/javascripts/discourse/helpers/bound-date.js.es6
@@ -1,4 +1,4 @@
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper(dt => autoUpdatingRelativeAge(new Date(dt), {format: 'medium', title: true }));
diff --git a/app/assets/javascripts/discourse/helpers/capitalize-string.js.es6 b/app/assets/javascripts/discourse/helpers/capitalize-string.js.es6
index 3914e405bd8..976a9ecd342 100644
--- a/app/assets/javascripts/discourse/helpers/capitalize-string.js.es6
+++ b/app/assets/javascripts/discourse/helpers/capitalize-string.js.es6
@@ -1,3 +1,3 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper(str => str[0].toUpperCase() + str.slice(1));
diff --git a/app/assets/javascripts/discourse/helpers/category-badge.js.es6 b/app/assets/javascripts/discourse/helpers/category-badge.js.es6
index c716537a6d3..74d28b745db 100644
--- a/app/assets/javascripts/discourse/helpers/category-badge.js.es6
+++ b/app/assets/javascripts/discourse/helpers/category-badge.js.es6
@@ -1,5 +1,5 @@
import { categoryLinkHTML } from 'discourse/helpers/category-link';
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
registerUnbound('category-badge', function(cat, options) {
options.link = false;
diff --git a/app/assets/javascripts/discourse/helpers/category-link.js.es6 b/app/assets/javascripts/discourse/helpers/category-link.js.es6
index bc184157485..618eca3211a 100644
--- a/app/assets/javascripts/discourse/helpers/category-link.js.es6
+++ b/app/assets/javascripts/discourse/helpers/category-link.js.es6
@@ -1,5 +1,5 @@
-import { registerUnbound } from 'discourse/lib/helpers';
-import { iconHTML } from 'discourse/helpers/fa-icon';
+import { registerUnbound } from 'discourse-common/lib/helpers';
+import { iconHTML } from 'discourse-common/helpers/fa-icon';
var get = Em.get,
escapeExpression = Handlebars.Utils.escapeExpression;
diff --git a/app/assets/javascripts/discourse/helpers/cold-age-class.js.es6 b/app/assets/javascripts/discourse/helpers/cold-age-class.js.es6
index 54c23bfb63b..5042a11120f 100644
--- a/app/assets/javascripts/discourse/helpers/cold-age-class.js.es6
+++ b/app/assets/javascripts/discourse/helpers/cold-age-class.js.es6
@@ -1,4 +1,4 @@
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
function daysSinceEpoch(dt) {
// 1000 * 60 * 60 * 24 = days since epoch
diff --git a/app/assets/javascripts/discourse/helpers/cook-text.js.es6 b/app/assets/javascripts/discourse/helpers/cook-text.js.es6
index ba214983a22..7864f01af80 100644
--- a/app/assets/javascripts/discourse/helpers/cook-text.js.es6
+++ b/app/assets/javascripts/discourse/helpers/cook-text.js.es6
@@ -1,4 +1,4 @@
import { cook } from 'discourse/lib/text';
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
registerUnbound('cook-text', cook);
diff --git a/app/assets/javascripts/discourse/helpers/custom-html.js.es6 b/app/assets/javascripts/discourse/helpers/custom-html.js.es6
index 8dc3be9c380..d862cb02fd8 100644
--- a/app/assets/javascripts/discourse/helpers/custom-html.js.es6
+++ b/app/assets/javascripts/discourse/helpers/custom-html.js.es6
@@ -1,4 +1,4 @@
-import { registerHelper } from 'discourse/lib/helpers';
+import { registerHelper } from 'discourse-common/lib/helpers';
import PreloadStore from 'preload-store';
const _customizations = {};
diff --git a/app/assets/javascripts/discourse/helpers/dash-if-empty.js.es6 b/app/assets/javascripts/discourse/helpers/dash-if-empty.js.es6
index fd694261d37..4b1553735c8 100644
--- a/app/assets/javascripts/discourse/helpers/dash-if-empty.js.es6
+++ b/app/assets/javascripts/discourse/helpers/dash-if-empty.js.es6
@@ -1,3 +1,3 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper(str => Ember.isEmpty(str) ? '—' : str);
diff --git a/app/assets/javascripts/discourse/helpers/discouse-tag.js.es6 b/app/assets/javascripts/discourse/helpers/discouse-tag.js.es6
index 1221da2417e..8c6cbf156e5 100644
--- a/app/assets/javascripts/discourse/helpers/discouse-tag.js.es6
+++ b/app/assets/javascripts/discourse/helpers/discouse-tag.js.es6
@@ -1,4 +1,4 @@
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
import renderTag from 'discourse/lib/render-tag';
export default registerUnbound('discourse-tag', function(name, params) {
diff --git a/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6 b/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6
new file mode 100644
index 00000000000..84f15420258
--- /dev/null
+++ b/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6
@@ -0,0 +1,20 @@
+import { h } from 'virtual-dom';
+import { iconClasses } from 'discourse-common/helpers/fa-icon';
+
+export function iconNode(icon, params) {
+ params = params || {};
+
+ const properties = {
+ className: iconClasses(icon, params),
+ attributes: { "aria-hidden": true }
+ };
+
+ if (params.title) { properties.attributes.title = params.title; }
+
+ if (params.label) {
+ return h('i', properties, h('span.sr-only', I18n.t(params.label)));
+ } else {
+ return h('i', properties);
+ }
+}
+
diff --git a/app/assets/javascripts/discourse/helpers/format-age.js.es6 b/app/assets/javascripts/discourse/helpers/format-age.js.es6
index 69247fb5f53..75119d0c5f2 100644
--- a/app/assets/javascripts/discourse/helpers/format-age.js.es6
+++ b/app/assets/javascripts/discourse/helpers/format-age.js.es6
@@ -1,5 +1,5 @@
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
registerUnbound('format-age', function(dt) {
dt = new Date(dt);
diff --git a/app/assets/javascripts/discourse/helpers/format-date.js.es6 b/app/assets/javascripts/discourse/helpers/format-date.js.es6
index feb207c2a5f..84602bebc82 100644
--- a/app/assets/javascripts/discourse/helpers/format-date.js.es6
+++ b/app/assets/javascripts/discourse/helpers/format-date.js.es6
@@ -1,4 +1,4 @@
-import { registerUnbound } from 'discourse/lib/helpers';
+import { registerUnbound } from 'discourse-common/lib/helpers';
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
/**
diff --git a/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6 b/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6
index 5f4c6cbce8b..f673bb8cda3 100644
--- a/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6
+++ b/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6
@@ -1,4 +1,4 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
export default htmlHelper(function(str) {
if (Ember.isEmpty(str)) { return ""; }
diff --git a/app/assets/javascripts/discourse/helpers/loading-spinner.es6 b/app/assets/javascripts/discourse/helpers/loading-spinner.es6
index 05dfd735bd8..fda8fe6beb1 100644
--- a/app/assets/javascripts/discourse/helpers/loading-spinner.es6
+++ b/app/assets/javascripts/discourse/helpers/loading-spinner.es6
@@ -1,4 +1,4 @@
-import { htmlHelper } from 'discourse/lib/helpers';
+import { htmlHelper } from 'discourse-common/lib/helpers';
function renderSpinner(cssClass) {
var html = "