You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
4.0 KiB

4 years ago
'use strict';
let cloneNode = function (obj, parent) {
let cloned = new obj.constructor();
for (let i in obj) {
if (!obj.hasOwnProperty(i)) continue;
let value = obj[i],
type = typeof value;
if (i === 'parent' && type === 'object') {
if (parent) cloned[i] = parent;
}
else if (i === 'source') {
cloned[i] = value;
}
else if (value instanceof Array) {
cloned[i] = value.map(j => cloneNode(j, cloned));
}
else if (i !== 'before' && i !== 'after' && i !== 'between' && i !== 'semicolon') {
if (type === 'object' && value !== null) value = cloneNode(value);
cloned[i] = value;
}
}
return cloned;
};
module.exports = class Node {
constructor (defaults) {
defaults = defaults || {};
this.raws = { before: '', after: '' };
for (let name in defaults) {
this[name] = defaults[name];
}
}
remove () {
if (this.parent) {
this.parent.removeChild(this);
}
this.parent = undefined;
return this;
}
toString () {
return [
this.raws.before,
String(this.value),
this.raws.after
].join('');
}
clone (overrides) {
overrides = overrides || {};
let cloned = cloneNode(this);
for (let name in overrides) {
cloned[name] = overrides[name];
}
return cloned;
}
cloneBefore (overrides) {
overrides = overrides || {};
let cloned = this.clone(overrides);
this.parent.insertBefore(this, cloned);
return cloned;
}
cloneAfter (overrides) {
overrides = overrides || {};
let cloned = this.clone(overrides);
this.parent.insertAfter(this, cloned);
return cloned;
}
replaceWith () {
let nodes = Array.prototype.slice.call(arguments);
if (this.parent) {
for (let node of nodes) {
this.parent.insertBefore(this, node);
}
this.remove();
}
return this;
}
moveTo (container) {
this.cleanRaws(this.root() === container.root());
this.remove();
container.append(this);
return this;
}
moveBefore (node) {
this.cleanRaws(this.root() === node.root());
this.remove();
node.parent.insertBefore(node, this);
return this;
}
moveAfter (node) {
this.cleanRaws(this.root() === node.root());
this.remove();
node.parent.insertAfter(node, this);
return this;
}
next () {
let index = this.parent.index(this);
return this.parent.nodes[index + 1];
}
prev () {
let index = this.parent.index(this);
return this.parent.nodes[index - 1];
}
toJSON () {
let fixed = { };
for (let name in this) {
if (!this.hasOwnProperty(name)) continue;
if (name === 'parent') continue;
let value = this[name];
if (value instanceof Array) {
fixed[name] = value.map(i => {
if (typeof i === 'object' && i.toJSON) {
return i.toJSON();
}
else {
return i;
}
});
}
else if (typeof value === 'object' && value.toJSON) {
fixed[name] = value.toJSON();
}
else {
fixed[name] = value;
}
}
return fixed;
}
root () {
let result = this;
while (result.parent) result = result.parent;
return result;
}
cleanRaws (keepBetween) {
delete this.raws.before;
delete this.raws.after;
if (!keepBetween) delete this.raws.between;
}
positionInside (index) {
let string = this.toString(),
column = this.source.start.column,
line = this.source.start.line;
for (let i = 0; i < index; i++) {
if (string[i] === '\n') {
column = 1;
line += 1;
}
else {
column += 1;
}
}
return { line, column };
}
positionBy (opts) {
let pos = this.source.start;
if (Object(opts).index) {
pos = this.positionInside(opts.index);
}
else if (Object(opts).word) {
let index = this.toString().indexOf(opts.word);
if (index !== -1) pos = this.positionInside(index);
}
return pos;
}
};