marko
Version:
UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.
336 lines (287 loc) • 9.08 kB
JavaScript
"use strict";
/* jshint newcap:false */
var BaseState;
var BaseComponent;
var inherit;
var req = require("@internal/require");
var registry = require("@internal/components-registry");
var jQuery = require("../../../runtime/components/legacy/jquery");
var ready = require("../../../runtime/components/legacy/ready");
// eslint-disable-next-line no-constant-binary-expression
var complain = "MARKO_DEBUG" && require("complain");
var hasHowOwnProperty = {}.hasOwnProperty;
var stateToJSONDef = {
enumerable: false,
value: function returnSelf() {
return this;
},
};
function noop() {}
module.exports = function defineWidget(def, renderer) {
def = def.Widget || def;
if (def.___isComponent) {
return def;
}
var ComponentClass = function () {};
var proto;
var legacyInit;
if (typeof def === "function") {
proto = def.prototype;
legacyInit = def;
} else if (typeof def === "object") {
proto = def;
legacyInit = def.init;
} else {
throw TypeError();
}
ComponentClass.prototype = proto;
// We don't use the constructor provided by the user
// since we don't invoke their constructor until
// we have had a chance to do our own initialization.
// Instead, we store their constructor in the "initComponent"
// property and that method gets called later inside
// init-components-browser.js
function Component(id, doc) {
BaseComponent.call(this, id, doc);
}
if (!proto.___isComponent) {
// Inherit from Component if they didn't already
ComponentClass.prototype = Object.create(BaseComponent.prototype);
for (var propName in proto) {
if (hasOwnProperty.call(proto, propName)) {
ComponentClass.prototype[propName] = proto[propName];
}
}
}
// The same prototype will be used by our constructor after
// we he have set up the prototype chain using the inherit function
proto = Component.prototype = ComponentClass.prototype;
proto.___isLegacy = true;
proto.constructor = def.constructor = Component;
Object.defineProperty(proto, "state", {
get: function () {
var raw = this.___state && this.___state.___raw;
if (raw && !raw.toJSON) {
Object.defineProperty(this.___state.___raw, "toJSON", stateToJSONDef);
}
return raw;
},
set: function (newState) {
newState = newState || {};
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
if (
Object.keys(newState).sort().join("") !==
Object.keys((this.___state && this.___state.___raw) || {})
.sort()
.join("")
)
complain(
"'widget.state = newState' has changed from merging the newState to replacing the old state."
);
}
this.setState(newState);
},
});
Object.defineProperty(proto, "__document", {
get: function () {
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
complain("__document is deprecated");
}
return this.___host;
},
});
Object.defineProperty(proto, "el", {
get: function () {
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
if (
this.___currentLegacyBindEl !==
(this.___rootNode && this.___rootNode.firstChild)
) {
complain(
"this.el no longer returns the `w-bind` element and instead returns the first node in the template. Assign a key to the w-bind element and use getEl(key) instead."
);
}
}
return this.___currentLegacyBindEl;
},
});
// get legacy methods
proto.___legacyOnRender = proto.onRender;
Object.defineProperty(proto, "onRender", {
get: noop,
set: function (v) {
proto.___legacyOnRender = v;
},
});
proto.___legacyOnUpdate = proto.onUpdate;
Object.defineProperty(proto, "onUpdate", {
get: function () {
return modernMountOrUpdate;
},
set: function (v) {
proto.___legacyOnUpdate = v;
},
});
proto.___legacyOnDestroy = proto.onDestroy;
Object.defineProperty(proto, "onDestroy", {
get: function () {
return modernOnDestory;
},
set: function (v) {
proto.___legacyOnDestroy = v;
},
});
proto.getWidget = proto.getComponent;
proto.getWidgets = proto.getComponents;
proto.onMount = modernMountOrUpdate;
if (legacyInit) {
proto.___legacyInit = legacyInit;
}
// convert legacy to modern
proto.___modernUpdate = proto.update;
proto.update = function () {
if (this.___destroyed) {
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
complain(
"widget was updated after it was destroyed, if this widget is migrated to a modern component this will become a noop.",
{
location: this.___type,
}
);
}
this.destroy = modernOnDestory;
this.___destroyed = false;
}
this.___legacyExplicitUpdate = true;
if (this.___currentLegacyBindEl) {
this.onBeforeUpdate && this.onBeforeUpdate();
}
this.___modernUpdate();
this.___legacyExplicitUpdate = false;
};
function modernMountOrUpdate() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
var self = this;
var el = this.___keyedElements["@_wbind"];
var prevEl = this.___currentLegacyBindEl;
if (prevEl !== el) {
this.___currentLegacyBindEl = el;
if (prevEl) {
this.onBeforeDestroy && this.onBeforeDestroy();
this.___legacyOnDestroy && this.___legacyOnDestroy();
this.___legacyRender = undefined;
this.removeAllListeners();
}
if (el) {
this.___legacyInit && this.___legacyInit(this.widgetConfig || {});
this.___legacyOnRender && this.___legacyOnRender({ firstRender: true });
this.___legacyRender = legacyRender;
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
Object.defineProperty(el, "__widget", {
configurable: true,
get: function () {
complain("__widget is deprecated");
return self;
},
});
} else {
el.__widget = this;
}
}
} else if (el) {
if (prevEl) {
this.___legacyOnUpdate && this.___legacyOnUpdate();
}
if (this.___didUpdate) {
this.___legacyOnRender &&
this.___legacyOnRender({ firstRender: false });
}
}
this.___widgetProps = this.___input;
this.___input = null;
this.___didUpdate = false;
}
function legacyRender() {
if (!this.___legacyExplicitUpdate) {
this.onBeforeUpdate && this.onBeforeUpdate();
}
this.___didUpdate = true;
}
function modernOnDestory() {
if (this.___currentLegacyBindEl) {
this.onBeforeDestroy && this.onBeforeDestroy();
this.___legacyOnDestroy && this.___legacyOnDestroy();
this.___currentLegacyBindEl = null;
}
}
// Set a flag on the constructor function to make it clear this is
// a component so that we can short-circuit this work later
Component.___isComponent = true;
function State() {
BaseState.apply(this, arguments);
}
inherit(State, BaseState);
proto.___State = State;
jQuery.patchComponent(
window.$,
proto,
true /* don't throw error until used if `$` is missing*/
);
ready.patchComponent(proto);
if (!renderer) {
renderer = ComponentClass.renderer || ComponentClass.prototype.renderer;
if (renderer) {
// Legacy support
var createOut = renderer.createOut;
if (typeof renderer !== "function") {
var rendererObject = renderer;
renderer = function (input, out) {
var rendererFunc = rendererObject.renderer || rendererObject.render;
rendererFunc(input, out);
};
renderer.createOut = createOut;
}
renderer.render = function (input) {
var out = createOut();
renderer(input, out);
return out.end();
};
}
}
if (renderer) {
// Add the rendering related methods as statics on the
// new component constructor function
Component.renderer = proto.___renderer = renderer;
Component.render = renderer.render;
Component.renderSync = renderer.renderSync;
}
// eslint-disable-next-line no-constant-condition
if ("MARKO_DEBUG") {
Object.defineProperty(Component, "_isWidget", {
get: function () {
complain("_isWidget is deprecated");
return true;
},
});
} else {
Component._isWidget = true;
}
var template = def.template;
if (template) {
if (typeof template === "string") {
template = req(template);
}
registry.r((template.default || template).___typeName, function () {
return Component;
});
}
return Component;
};
BaseState = require("../../../runtime/components/State");
BaseComponent = require("../../../runtime/components/Component");
inherit = require("raptor-util/inherit");