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.
214 lines
4.0 KiB
214 lines
4.0 KiB
'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; |
|
} |
|
};
|
|
|