Skip to content

Commit

Permalink
Ability to change attributes/properties before Object.seal()
Browse files Browse the repository at this point in the history
  • Loading branch information
nettybun committed Sep 8, 2020
1 parent c4d1c4b commit e52f503
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 47 deletions.
7 changes: 4 additions & 3 deletions public/web.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ document.body.appendChild(element);
fetch('data.txt')
.then(data => data.text())
.then(text => {
const element = document.createElement('p');
element.appendChild(document.createTextNode(text.trim()));
document.body.appendChild(element);
const a = document.createElement('a');
a.href = `http://localhost/${text.trim().replace(/ /g, '-').toLowerCase()}`;
a.appendChild(document.createTextNode(text.trim()));
document.body.appendChild(a);
});
84 changes: 40 additions & 44 deletions softdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,16 @@ const encodeTextSafe = s => s.replace(/[&<>'"]/g, m => escapeMap[m]);

// To throw errors on property read/write the object is sealed. However, this
// needs to happen at the top of the super() chain.
let sealLock = '';
let preventInstanceObjectSeal = false;

class Node {
constructor(nodeType, nodeName) {
if (!sealLock) sealLock = 'Node';
this.nodeType = nodeType;
this.nodeName = nodeName;
this.childNodes = [];
this.parentNode = null;
// This isn't in the spec but I use it a bit. This implementation is wrong
this.dataset = {};
if (sealLock === 'Node') {
Object.seal(this);
sealLock = '';
}
Object.assign(this, classProperties.Node);
if (!preventInstanceObjectSeal) Object.seal(this);
}
get nextSibling() {
let p = this.parentNode;
Expand Down Expand Up @@ -145,13 +140,14 @@ class Node {

class Text extends Node {
constructor(text) {
if (!sealLock) sealLock = 'Text';
const prev = preventInstanceObjectSeal;
preventInstanceObjectSeal = true;
super(NODE_TYPES.TEXT_NODE, '#text');
preventInstanceObjectSeal = prev;

this.nodeValue = text;
if (sealLock === 'Text') {
Object.seal(this);
sealLock = '';
}
Object.assign(this, classProperties.Text);
if (!preventInstanceObjectSeal) Object.seal(this);
}
set textContent(text) { this.nodeValue = text; }
get textContent() { return this.nodeValue; }
Expand All @@ -163,22 +159,16 @@ class Text extends Node {

class Element extends Node {
constructor(nodeType, nodeName) {
if (!sealLock) sealLock = 'Element';
const prev = preventInstanceObjectSeal;
preventInstanceObjectSeal = true;
super(nodeType || NODE_TYPES.ELEMENT_NODE, nodeName);
preventInstanceObjectSeal = prev;
this.attributes = [];
this.__handlers = {};
this.style = {};
this.namespace = null;

// Sinuous-specific unfortunately...
// Naturally el._listeners = {} but minified. Ugh.
this.t = {};

initializeAttributeAccessors(this, nodeName);
if (sealLock === 'Element') {
Object.seal(this);
sealLock = '';
}
Object.assign(this, classProperties.Element);
initializeAttributes(this, nodeName);
if (!preventInstanceObjectSeal) Object.seal(this);
}
get tagName() { return this.nodeName; }

Expand Down Expand Up @@ -237,7 +227,6 @@ class Element extends Node {
} while (event.bubbles && !(c && event._stop) && (t = t.parentNode));
return Boolean(l);
}

get outerHTML() {
return serialize(this);
}
Expand All @@ -248,27 +237,26 @@ class Element extends Node {

class DocumentFragment extends Node {
constructor() {
if (!sealLock) sealLock = 'DocumentFragment';
const prev = preventInstanceObjectSeal;
preventInstanceObjectSeal = true;
super(NODE_TYPES.DOCUMENT_FRAGMENT_NODE, '#document-fragment');
if (sealLock === 'DocumentFragment') {
Object.seal(this);
sealLock = '';
}
preventInstanceObjectSeal = prev;
if (!preventInstanceObjectSeal) Object.seal(this);
}
}

class Document extends Element {
constructor() {
if (!sealLock) sealLock = 'Document';
const prev = preventInstanceObjectSeal;
preventInstanceObjectSeal = true;
super(NODE_TYPES.DOCUMENT_NODE, '#document');
preventInstanceObjectSeal = prev;
this.defaultView = null;
this.documentElement = null;
this.head = null;
this.body = null;
if (sealLock === 'Document') {
Object.seal(this);
sealLock = '';
}
Object.assign(this, classProperties.Document);
if (!preventInstanceObjectSeal) Object.seal(this);
}
createElement(type) {
return new Element(null, String(type).toUpperCase());
Expand All @@ -288,14 +276,11 @@ class Document extends Element {

class Event {
constructor(type, opts) {
if (!sealLock) sealLock = 'Event';
this.type = type;
this.bubbles = Boolean(opts && opts.bubbles);
this.cancelable = Boolean(opts && opts.cancelable);
if (sealLock === 'Event') {
Object.seal(this);
sealLock = '';
}
Object.assign(this, classProperties.Event);
if (!preventInstanceObjectSeal) Object.seal(this);
}
stopPropagation() {
this._stop = true;
Expand All @@ -315,10 +300,10 @@ const elementAttributes = {
a: ['href'],
img: ['src'],
label: ['htmlFor'],
input: ['type', 'placeholder'],
button: ['type'],
input: ['type', 'value', 'placeholder'],
button: ['type', 'value'],
};
function initializeAttributeAccessors(instance, nodeName) {
function initializeAttributes(instance, nodeName) {
const attributes = elementAttributes[nodeName.toLowerCase()] ?? [];
for (const attr of attributes) {
Object.defineProperty(instance, attr, {
Expand All @@ -328,4 +313,15 @@ function initializeAttributeAccessors(instance, nodeName) {
}
}

// Allow people to add properties to classes before their instance is sealed
const classProperties = {
Node: {},
Text: {},
Element: {},
DocumentFragment: {},
Document: {},
Event: {},
}

export { Node, Text, Element, DocumentFragment, Document, Event };
export { classProperties, elementAttributes, preventInstanceObjectSeal }

0 comments on commit e52f503

Please sign in to comment.