import { keyDirty } from 'discourse/widgets/widget'; import { diff, patch } from 'virtual-dom'; import { WidgetClickHook } from 'discourse/widgets/hooks'; import { renderedKey, queryRegistry } from 'discourse/widgets/widget'; const _cleanCallbacks = {}; export function addWidgetCleanCallback(widgetName, fn) { _cleanCallbacks[widgetName] = _cleanCallbacks[widgetName] || []; _cleanCallbacks[widgetName].push(fn); } export default Ember.Component.extend({ _tree: null, _rootNode: null, _timeout: null, _widgetClass: null, _renderCallback: null, _childEvents: null, _dispatched: null, init() { this._super(); const name = this.get('widget'); (this.get('delegated') || []).forEach(m => this.set(m, m)); this._widgetClass = queryRegistry(name) || this.container.lookupFactory(`widget:${name}`); if (!this._widgetClass) { console.error(`Error: Could not find widget: ${name}`); } this._childEvents = []; this._connected = []; this._dispatched = []; }, didInsertElement() { WidgetClickHook.setupDocumentCallback(); this._rootNode = document.createElement('div'); this.element.appendChild(this._rootNode); this._timeout = Ember.run.scheduleOnce('render', this, this.rerenderWidget); }, willClearRender() { const callbacks = _cleanCallbacks[this.get('widget')]; if (callbacks) { callbacks.forEach(cb => cb()); } this._connected.forEach(v => v.destroy()); this._connected.length = 0; }, willDestroyElement() { this._dispatched.forEach(evt => { const [eventName, caller] = evt; this.appEvents.off(eventName, caller); }); Ember.run.cancel(this._timeout); }, afterRender() { }, beforePatch() { }, afterPatch() { }, eventDispatched(eventName, key, refreshArg) { const onRefresh = Ember.String.camelize(eventName.replace(/:/, '-')); keyDirty(key, { onRefresh, refreshArg }); this.queueRerender(); }, dispatch(eventName, key) { this._childEvents.push(eventName); const caller = refreshArg => this.eventDispatched(eventName, key, refreshArg); this._dispatched.push([eventName, caller]); this.appEvents.on(eventName, caller); }, queueRerender(callback) { if (callback && !this._renderCallback) { this._renderCallback = callback; } Ember.run.scheduleOnce('render', this, this.rerenderWidget); }, buildArgs() { }, rerenderWidget() { Ember.run.cancel(this._timeout); if (this._rootNode) { if (!this._widgetClass) { return; } const t0 = new Date().getTime(); const args = this.get('args') || this.buildArgs(); const opts = { model: this.get('model') }; const newTree = new this._widgetClass(args, this.container, opts); newTree._emberView = this; const patches = diff(this._tree || this._rootNode, newTree); this.beforePatch(); this._rootNode = patch(this._rootNode, patches); this.afterPatch(); this._tree = newTree; if (this._renderCallback) { this._renderCallback(); this._renderCallback = null; } this.afterRender(); renderedKey('*'); if (this.profileWidget) { console.log(new Date().getTime() - t0); } } } });