<commit_before>'use strict';

var zlib_deflate = require('./zlib/deflate.js');
var utils = require('./zlib/utils');
var c = require('./zlib/constants');
var msg = require('./zlib/messages');
var zstream = require('./zlib/zstream');

/**
 * Deflate
 *
 * @param {Object} [options] zlib options
 * @constructor
 */
var Deflate = function(options) {

  this.options = utils.assign({
    level: 6,
    method: c.Z_DEFLATED,
    chunkSize: 16384,
    windowBits: 15,
    memLevel: 8,
    strategy: c.Z_DEFAULT_STRATEGY
  }, options || {});

  var opt = this.options;

  if (opt.raw && (opt.windowBits > 0)) {
    opt.windowBits = -opt.windowBits;
  }

  this.strm = new zstream();
  this.strm.next_out = utils.arrayCreate(opt.chunkSize);

  var status = zlib_deflate.deflateInit2(
    this.strm,
    opt.level,
    opt.method,
    opt.windowBits,
    opt.memLevel,
    opt.strategy
  );

  if (status !== c.Z_OK) {
    throw new Error(msg[status]);
  }
};

/**
 * Compresses the input data and fills output buffer with compressed data.
 */
Deflate.prototype.push = function(data_in) {
  var strm = this.strm;
  var out;

  strm.next_in = data_in;
  strm.next_in_index = 0;
  strm.avail_in = strm.next_in.length;

  /* run deflate() on input until output buffer not full, finish
   compression if all of source has been read in */
  do {
    strm.avail_out = this.options.chunkSize;
    strm.next_out_index = 0;
    zlib_deflate.deflate(strm, c.Z_NO_FLUSH);
    // TODO: check logic & why onEnd called. Check that onEnd not called twice.
    //var ret = zlib_deflate.deflate(strm, c.Z_NO_FLUSH);    /* no bad return value */
    //if (ret !== c.Z_STREAM_END && ret !== c.Z_OK) {
    //  this.onEnd(ret);
    //}
    if(strm.next_out_index) {
      out = utils.arrayCreate(strm.next_out_index);
      utils.arraySet(out, strm.next_out, 0, strm.next_out_index, 0);
      this.onData(out);
    }
  } while (strm.avail_in > 0 || strm.avail_out === 0);
};

Deflate.prototype.flush = function() {
  var strm = this.strm;
  var out, status;

  do {
    strm.avail_out = this.options.chunkSize;
    strm.next_out_index = 0;
    status = zlib_deflate.deflate(strm, c.Z_FINISH);    /* no bad return value */
    if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
      this.onEnd(status);
    }

    if(strm.next_out_index) {
      out = utils.arrayCreate(strm.next_out_index);
      utils.arraySet(out, strm.next_out, 0, strm.next_out_index, 0);
      this.onData(out);
    }
  } while (strm.avail_out === 0);
};

Deflate.prototype.finish = function() {
  this.flush();
  zlib_deflate.deflateEnd(this.strm);
  this.onEnd(c.Z_OK);
};

Deflate.prototype.onData = function(/*data_out*/) {

};

Deflate.prototype.onEnd = function(/*err*/) {

};

exports.Deflate = Deflate;

/**
 * Compresses the input data
 * @param input
 * @param [options]
 * @returns {Array|Uint8Array}
 */
function deflate(input, options) {
  var result;
  var chunks = [];
  var deflator = new Deflate(options);

  deflator.onData = function(data_out) {
    chunks.push(data_out);
  };

  deflator.onEnd = function(error) {
    if (error) { throw error; }
    result = utils.flattenChunks(chunks);
  };

  deflator.push(input);
  deflator.finish();

  return result;
}

function deflateRaw(input, options) {
  options = options || {};
  options.raw = true;
  return deflate(input, options);
}

exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;<commit_msg>Removed output double boffering in deflate wrappermake test<commit_after>'use strict';

var zlib_deflate = require('./zlib/deflate.js');
var utils = require('./zlib/utils');
var c = require('./zlib/constants');
var msg = require('./zlib/messages');
var zstream = require('./zlib/zstream');

/**
 * Deflate
 *
 * @param {Object} [options] zlib options
 * @constructor
 */
var Deflate = function(options) {

  this.options = utils.assign({
    level: 6,
    method: c.Z_DEFLATED,
    chunkSize: 16384,
    windowBits: 15,
    memLevel: 8,
    strategy: c.Z_DEFAULT_STRATEGY
  }, options || {});

  var opt = this.options;

  if (opt.raw && (opt.windowBits > 0)) {
    opt.windowBits = -opt.windowBits;
  }

  this.strm = new zstream();

  var status = zlib_deflate.deflateInit2(
    this.strm,
    opt.level,
    opt.method,
    opt.windowBits,
    opt.memLevel,
    opt.strategy
  );

  if (status !== c.Z_OK) {
    throw new Error(msg[status]);
  }
};

/**
 * Compresses the input data and fills output buffer with compressed data.
 */
Deflate.prototype.push = function(data_in) {
  var strm = this.strm;
  var chunkSize = this.options.chunkSize;
  var sliced;

  strm.next_in = data_in;
  strm.next_in_index = 0;
  strm.avail_in = strm.next_in.length;
  strm.next_out = utils.arrayCreate(chunkSize);

  /* run deflate() on input until output buffer not full, finish
   compression if all of source has been read in */
  do {
    strm.avail_out = this.options.chunkSize;
    strm.next_out_index = 0;
    zlib_deflate.deflate(strm, c.Z_NO_FLUSH);
    // TODO: check logic & why onEnd called. Check that onEnd not called twice.
    //var ret = zlib_deflate.deflate(strm, c.Z_NO_FLUSH);    /* no bad return value */
    //if (ret !== c.Z_STREAM_END && ret !== c.Z_OK) {
    //  this.onEnd(ret);
    //}
    if(strm.next_out_index) {
      if (strm.next_out_index === chunkSize) {
        // out buffer completely filled, push it as is
        this.onData(strm.next_out);
      } else {
        // out buffer partially filled, slice it first
        sliced = utils.arrayCreate(strm.next_out_index);
        utils.arraySet(sliced, strm.next_out, 0, strm.next_out_index, 0);
        this.onData(sliced);
      }
      // Allocate buffer for next chunk
      strm.next_out = utils.arrayCreate(this.options.chunkSize);
    }
  } while (strm.avail_in > 0 || strm.avail_out === 0);
};

Deflate.prototype.flush = function() {
  var strm = this.strm;
  var out, status;

  do {
    strm.avail_out = this.options.chunkSize;
    strm.next_out_index = 0;
    status = zlib_deflate.deflate(strm, c.Z_FINISH);    /* no bad return value */
    if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
      this.onEnd(status);
    }

    if(strm.next_out_index) {
      out = utils.arrayCreate(strm.next_out_index);
      utils.arraySet(out, strm.next_out, 0, strm.next_out_index, 0);
      this.onData(out);
    }
  } while (strm.avail_out === 0);
};

Deflate.prototype.finish = function() {
  this.flush();
  zlib_deflate.deflateEnd(this.strm);
  this.onEnd(c.Z_OK);
};

Deflate.prototype.onData = function(/*data_out*/) {

};

Deflate.prototype.onEnd = function(/*err*/) {

};

exports.Deflate = Deflate;

/**
 * Compresses the input data
 * @param input
 * @param [options]
 * @returns {Array|Uint8Array}
 */
function deflate(input, options) {
  var result;
  var chunks = [];
  var deflator = new Deflate(options);

  deflator.onData = function(data_out) {
    chunks.push(data_out);
  };

  deflator.onEnd = function(error) {
    if (error) { throw error; }
    result = utils.flattenChunks(chunks);
  };

  deflator.push(input);
  deflator.finish();

  return result;
}

function deflateRaw(input, options) {
  options = options || {};
  options.raw = true;
  return deflate(input, options);
}

exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;<|endoftext|>


<commit_before>/* @flow */

import { CompositeDisposable } from 'atom'
import type { TextEditor } from 'atom'
import Editor from './editor'
import { $file, getEditorsMap, filterMessages } from './helpers'
import type { LinterMessage, MessagesPatch } from './types'

class Editors {
  editors: Set<Editor>
  messages: Array<LinterMessage>
  firstRender: boolean
  subscriptions: CompositeDisposable

  constructor() {
    this.editors = new Set()
    this.messages = []
    this.firstRender = true
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(
      atom.workspace.observeTextEditors(textEditor => {
        this.getEditor(textEditor)
      }),
    )
    this.subscriptions.add(
      atom.workspace.getCenter().observeActivePaneItem(paneItem => {
        this.editors.forEach(editor => {
          if (editor.textEditor !== paneItem) {
            editor.removeTooltip()
          }
        })
      }),
    )
  }
  isFirstRender(): boolean {
    return this.firstRender
  }
  update({ messages, added, removed }: MessagesPatch) {
    this.messages = messages
    this.firstRender = false

    const { editorsMap, filePaths } = getEditorsMap(this)
    if (Array.isArray(added)) {
      added.forEach(function(message) {
        const filePath = $file(message)
        if (filePath && editorsMap[filePath]) {
          editorsMap[filePath].added.push(message)
        }
      })
    }
    if (Array.isArray(removed)) {
      removed.forEach(function(message) {
        const filePath = $file(message)
        if (filePath && editorsMap[filePath]) {
          editorsMap[filePath].removed.push(message)
        }
      })
    }

    filePaths.forEach(function(filePath) {
      const value = editorsMap[filePath]
      if (value.added.length || value.removed.length) {
        value.editors.forEach(editor => editor.apply(value.added, value.removed))
      }
    })
  }
  getEditor(textEditor: TextEditor): Editor {
    for (const entry of this.editors) {
      if (entry.textEditor === textEditor) {
        return entry
      }
    }
    const editor = new Editor(textEditor)
    this.editors.add(editor)
    editor.onDidDestroy(() => {
      this.editors.delete(editor)
    })
    editor.subscriptions.add(
      textEditor.onDidChangePath(() => {
        editor.dispose()
        this.getEditor(textEditor)
      }),
    )
    editor.subscriptions.add(
      textEditor.onDidChangeGrammar(() => {
        editor.dispose()
        this.getEditor(textEditor)
      }),
    )
    editor.apply(filterMessages(this.messages, textEditor.getPath()), [])
    return editor
  }
  dispose() {
    for (const entry of this.editors) {
      entry.dispose()
    }
    this.subscriptions.dispose()
  }
}

module.exports = Editors
<commit_msg>Revert "fix: add isArray check for removed"<commit_after>/* @flow */

import { CompositeDisposable } from 'atom'
import type { TextEditor } from 'atom'
import Editor from './editor'
import { $file, getEditorsMap, filterMessages } from './helpers'
import type { LinterMessage, MessagesPatch } from './types'

class Editors {
  editors: Set<Editor>
  messages: Array<LinterMessage>
  firstRender: boolean
  subscriptions: CompositeDisposable

  constructor() {
    this.editors = new Set()
    this.messages = []
    this.firstRender = true
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(
      atom.workspace.observeTextEditors(textEditor => {
        this.getEditor(textEditor)
      }),
    )
    this.subscriptions.add(
      atom.workspace.getCenter().observeActivePaneItem(paneItem => {
        this.editors.forEach(editor => {
          if (editor.textEditor !== paneItem) {
            editor.removeTooltip()
          }
        })
      }),
    )
  }
  isFirstRender(): boolean {
    return this.firstRender
  }
  update({ messages, added, removed }: MessagesPatch) {
    this.messages = messages
    this.firstRender = false

    const { editorsMap, filePaths } = getEditorsMap(this)
    removed.forEach(function(message) {
      const filePath = $file(message)
      if (filePath && editorsMap[filePath]) {
        editorsMap[filePath].removed.push(message)
      }
    })
    if (Array.isArray(added)) {
      added.forEach(function(message) {
        const filePath = $file(message)
        if (filePath && editorsMap[filePath]) {
          editorsMap[filePath].added.push(message)
        }
      })
    }

    filePaths.forEach(function(filePath) {
      const value = editorsMap[filePath]
      if (value.added.length || value.removed.length) {
        value.editors.forEach(editor => editor.apply(value.added, value.removed))
      }
    })
  }
  getEditor(textEditor: TextEditor): Editor {
    for (const entry of this.editors) {
      if (entry.textEditor === textEditor) {
        return entry
      }
    }
    const editor = new Editor(textEditor)
    this.editors.add(editor)
    editor.onDidDestroy(() => {
      this.editors.delete(editor)
    })
    editor.subscriptions.add(
      textEditor.onDidChangePath(() => {
        editor.dispose()
        this.getEditor(textEditor)
      }),
    )
    editor.subscriptions.add(
      textEditor.onDidChangeGrammar(() => {
        editor.dispose()
        this.getEditor(textEditor)
      }),
    )
    editor.apply(filterMessages(this.messages, textEditor.getPath()), [])
    return editor
  }
  dispose() {
    for (const entry of this.editors) {
      entry.dispose()
    }
    this.subscriptions.dispose()
  }
}

module.exports = Editors
<|endoftext|>


<commit_before>import {
    TYPE_STRING,
    TYPE_FUNCTION,
    DEFAULT_PREFIX,
    DEFAULT_NAMESPACE_URI,
    NAMESPACE_SEPARATOR,
    NO_PARENT_INDEX
} from './const'
import { Attr, Document, Element, document } from './dom'
import { AttrAssembler } from './attr'
import { DocumentAssembler } from './document'
import { ParentNodeAssembler } from './parentnode'

const { keys } = Object
const { isArray, prototype : { indexOf, map, reduce } } = Array

function attrset(attrset, { name, value }) {
    attrset[name] = value
    return attrset
}

/**
 * Element DOM node assembler
 */
export class ElementAssembler extends ParentNodeAssembler {
    /**
     * this.setAttribute(AttrAssembler, 'foobar')
     * this.setAttribute('attr', 'foobar')
     *
     * @param {Function|String} attr
     * @param {String} value
     */
    setAttribute(attr, value) {
        const instance = this.getAttributeNode(attr)
        if(instance) instance.value = value
        else if(typeof attr === TYPE_FUNCTION) {
            this.setAttributeNode(new attr({ value }))
        }
        else this.node.setAttribute(attr, value)
    }

    /**
     * this.getAttribute(AttrAssembler)
     * this.getAttribute('attr')
     *
     * @param {Function|String} attr
     * @returns {String}
     */
    getAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespaceURI, qualifiedName, localName } = attr
            return namespaceURI?
                node.getAttributeNS(namespaceURI, localName) :
                node.getAttribute(qualifiedName)
        }
        else return node.getAttribute(attr)
    }

    /**
     * @param {Function|String} attr
     * @returns {Boolean}
     */
    hasAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespaceURI, qualifiedName, localName } = attr
            return namespaceURI?
                node.hasAttributeNS(namespaceURI, localName) :
                node.hasAttribute(qualifiedName)
        }
        else return node.hasAttribute(attr)
    }

    /**
     * @param {Function|String} attr
     */
    removeAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespaceURI, qualifiedName, localName } = attr
            if(namespaceURI) {
                node.removeAttributeNS(namespaceURI, localName)
            }
            else node.removeAttribute(qualifiedName)
        }
        else node.removeAttribute(attr)
    }

    /**
     * this.setAttributeNode(new AttrAssembler)
     * this.setAttributeNode(document.createAttribute('attr'))
     *
     * @param {AttrAssembler|Attr} attr
     * @returns {AttrAssembler|ElementAssembler.attrAssembler|null}
     */
    setAttributeNode(attr) {
        const node = this.node
        let replacedNode
        if(attr instanceof AttrAssembler) {
            const { namespaceURI, name } = attr.node
            replacedNode = namespaceURI?
                node.getAttributeNodeNS(namespaceURI, name) :
                node.getAttributeNode(name)
            attr.ownerElement = node
        }
        else {
            replacedNode = attr.namespaceURI?
                node.setAttributeNodeNS(attr) :
                node.setAttributeNode(attr)
        }
        if(replacedNode) {
            const { attrAssembler } = this.constructor
            return this.getInstance(replacedNode, attrAssembler)
        }
        else return null
    }

    /**
     * this.getAttributeNode(AttrAssembler)
     * this.getAttributeNode('attr')
     *
     * @param {Function|String} attr
     * @returns {AttrAssembler|null}
     */
    getAttributeNode(attr) {
        const node = this.node
        const isFunctionType = typeof attr === TYPE_FUNCTION
        let attrNode
        if(isFunctionType) {
            const { namespaceURI, qualifiedName, localName } = attr
            attrNode = namespaceURI?
                node.getAttributeNodeNS(namespaceURI, localName) :
                node.getAttributeNode(qualifiedName)
        }
        else attrNode = node.getAttributeNode(attr)
        if(attrNode) {
            const assembler = isFunctionType?
                attr :
                this.constructor.attrAssembler
            return this.getInstance(attrNode, assembler)
        }
        else return null
    }

    /**
     * this.removeAttributeNode('attr')
     * this.removeAttributeNode(AttrAssembler)
     * this.removeAttributeNode(this.getAttributeNode('attr'))
     * this.removeAttributeNode(this.node.getAttributeNode('attr'))
     *
     * @param {AttrAssembler|Attr|Function|String} attr
     * @returns {AttrAssembler|ElementAssembler.attrAssembler|null}
     */
    removeAttributeNode(attr) {
        const node = this.node
        const type = typeof attr
        if(type === TYPE_STRING || type === TYPE_FUNCTION) {
            const instance = this.getAttributeNode(attr)
            if(instance) {
                node.removeAttributeNode(instance.node)
                return instance
            }
            else return null
        }
        else {
            if(attr instanceof AttrAssembler) {
                node.removeAttributeNode(attr.node) // todo attr.remove()
                return attr
            }
            else {
                const attrNode = node.removeAttributeNode(attr)
                const { attrAssembler } = this.constructor
                return this.getInstance(attrNode, attrAssembler)
            }
        }
    }

    /**
     * Set the unique identifier on the element
     * @param {String} id
     */
    set id(id) {
        this.node.id = id
    }

    /**
     * Get the unique identifier of the element
     * @returns {String}
     */
    get id() {
        return this.node.id
    }

    /**
     * Set the class name of the element
     * @param {String} className
     */
    set className(className) {
        this.node.className = className
    }

    /**
     * Get the class name of the element
     * @returns {String}
     */
    get className() {
        return this.node.className
    }

    /**
     * Set the class list of the element
     * @param {*} classList token / token list / token-boolean dict {String|Array|{}}
     */
    set classList(classList) {
        if(classList) {
            const list = this.node.classList
            if(isArray(classList)) {
                classList.forEach(token => this.classList = token)
            }
            else if(classList.constructor === Object) {
                keys(classList).forEach(token => {
                    if(classList[token]) list.add(token)
                })
            }
            else list.add(classList)
        }
    }

    /**
     * Get the class list of the element as an array
     * @returns {DOMTokenList} classList interface
     */
    get classList() {
        return this.node.classList
    }

    /**
     * Set content attributes on the element
     * @param {*} attrset dictionary object
     */
    set attrset(attrset) {
        const node = this.node
        for(let name in attrset) {
            node.setAttribute(name, attrset[name])
        }
    }

    /**
     * Get all content attributes of the element as a dictionary-object
     * @returns {{}}
     */
    get attrset() {
        return reduce.call(this.node.attributes, attrset, {})
    }

    /**
     * Set content attributes on the element
     * @param {*} attributes
     */
    set attributes(attributes) {
        if(attributes instanceof AttrAssembler) {
            attributes.ownerElement = this.node
        }
        else if(isArray(attributes)) {
            attributes.forEach(attr => this.attributes = attr)
        }
        else this.setAttributeNode(attributes)
    }

    /**
     * Get all attributes of the element as an array
     * @returns {Array}
     */
    get attributes() {
        const { attrAssembler } = this.constructor
        return map.call(this.node.attributes, node => {
            return this.getInstance(node, attrAssembler)
        })
    }

    /**
     * Append children to the element
     * @param {*} children
     */
    set children(children) {
        this.append(children)
    }

    /**
     * Get all children of the element as an array
     * @returns {Array}
     */
    get children() {
        const { elementAssembler } = this.constructor
        return map.call(this.node.children, node => {
            return this.getInstance(node, elementAssembler)
        })
    }

    /**
     * @returns {ChildNodeAssembler|*}
     */
    get firstElementChild() {
        return this.getInstance(
            this.node.firstElementChild,
            this.constructor.elementAssembler)
    }

    /**
     * @returns {ChildNodeAssembler|*}
     */
    get lastElementChild() {
        return this.getInstance(
            this.node.lastElementChild,
            this.constructor.elementAssembler)
    }

    /**
     * @param {ParentNodeAssembler|*} parentNode
     */
    set parentNode(parentNode) {
        if(parentNode instanceof DocumentAssembler) {
            parentNode.documentElement = this.node
        }
        else {
            if(parentNode instanceof Document) {
                const element = parentNode.documentElement
                if(element) parentNode.removeChild(element)
            }
            super.parentNode = parentNode
        }
    }

    /**
     * @returns {ParentNodeAssembler|*}
     */
    get parentNode() {
        return super.parentNode
    }

    /**
     * Get a next sibling of the element
     * @returns {ChildNodeAssembler|null|*}
     */
    get nextElementSibling() {
        return this.getInstance(
            this.node.nextElementSibling,
            this.constructor.elementAssembler)
    }

    /**
     * Get a previous sibling of the element
     * @returns {ChildNodeAssembler|null|*}
     */
    get previousElementSibling() {
        return this.getInstance(
            this.node.previousElementSibling,
            this.constructor.elementAssembler)
    }

    /**
     * Get the element index among its sibling elements or -1 if it has no parent
     * @returns {Number}
     */
    get elementIndex() {
        const parentNode = this.node.parentNode
        return parentNode?
            indexOf.call(parentNode.children, this.node) :
            NO_PARENT_INDEX
    }

    /**
     * Create the specified Element node
     * @param {*} [init]
     * @returns {Element|*} created element
     */
    static create(init) {
        const {
            namespaceURI = this.namespaceURI,
            prefix = this.prefix,
            localName = this.localName
        } = init || this
        let qualifiedName = init && init.qualifiedName
        if(!qualifiedName) {
            qualifiedName = prefix?
                prefix + NAMESPACE_SEPARATOR + localName :
                localName
        }
        return document.createElementNS(namespaceURI, qualifiedName)
    }

    /**
     * Default namespace URI
     * @returns {String}
     */
    static get namespaceURI() {
        return DEFAULT_NAMESPACE_URI
    }

    /**
     * The XML namespace prefix
     * @returns {String}
     */
    static get prefix() {
        return DEFAULT_PREFIX
    }

    /**
     * The local name of the element node
     * @returns {String}
     */
    static get localName() {
        return this === ElementAssembler? element.name : this.name.toLowerCase()
    }

    /**
     * The qualified name of the element node
     * @returns {String}
     */
    static get qualifiedName() {
        const { prefix, localName } = this
        return prefix?
            prefix + NAMESPACE_SEPARATOR + localName :
            localName
    }

    /**
     * @returns {Function}
     */
    static get attrAssembler() {
        return AttrAssembler
    }

    /**
     * @returns {Function}
     */
    static get elementAssembler() {
        return ElementAssembler
    }

    /**
     * @returns {Function}
     */
    static get interface() {
        return Element
    }
}

/**
 * Element assembler factory
 * @param {*} init
 * @returns {ElementAssembler}
 */
export function element(...init) {
    return new ElementAssembler(...init)
}
<commit_msg>ElementAssembler.removeAttributeNode: use AttrAssembler.remove<commit_after>import {
    TYPE_STRING,
    TYPE_FUNCTION,
    DEFAULT_PREFIX,
    DEFAULT_NAMESPACE_URI,
    NAMESPACE_SEPARATOR,
    NO_PARENT_INDEX
} from './const'
import { Attr, Document, Element, document } from './dom'
import { AttrAssembler } from './attr'
import { DocumentAssembler } from './document'
import { ParentNodeAssembler } from './parentnode'

const { keys } = Object
const { isArray, prototype : { indexOf, map, reduce } } = Array

function attrset(attrset, { name, value }) {
    attrset[name] = value
    return attrset
}

/**
 * Element DOM node assembler
 */
export class ElementAssembler extends ParentNodeAssembler {
    /**
     * this.setAttribute(AttrAssembler, 'foobar')
     * this.setAttribute('attr', 'foobar')
     *
     * @param {Function|String} attr
     * @param {String} value
     */
    setAttribute(attr, value) {
        const instance = this.getAttributeNode(attr)
        if(instance) instance.value = value
        else if(typeof attr === TYPE_FUNCTION) {
            this.setAttributeNode(new attr({ value }))
        }
        else this.node.setAttribute(attr, value)
    }

    /**
     * this.getAttribute(AttrAssembler)
     * this.getAttribute('attr')
     *
     * @param {Function|String} attr
     * @returns {String}
     */
    getAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespaceURI, qualifiedName, localName } = attr
            return namespaceURI?
                node.getAttributeNS(namespaceURI, localName) :
                node.getAttribute(qualifiedName)
        }
        else return node.getAttribute(attr)
    }

    /**
     * @param {Function|String} attr
     * @returns {Boolean}
     */
    hasAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespaceURI, qualifiedName, localName } = attr
            return namespaceURI?
                node.hasAttributeNS(namespaceURI, localName) :
                node.hasAttribute(qualifiedName)
        }
        else return node.hasAttribute(attr)
    }

    /**
     * @param {Function|String} attr
     */
    removeAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespaceURI, qualifiedName, localName } = attr
            if(namespaceURI) {
                node.removeAttributeNS(namespaceURI, localName)
            }
            else node.removeAttribute(qualifiedName)
        }
        else node.removeAttribute(attr)
    }

    /**
     * this.setAttributeNode(new AttrAssembler)
     * this.setAttributeNode(document.createAttribute('attr'))
     *
     * @param {AttrAssembler|Attr} attr
     * @returns {AttrAssembler|ElementAssembler.attrAssembler|null}
     */
    setAttributeNode(attr) {
        const node = this.node
        let replacedNode
        if(attr instanceof AttrAssembler) {
            const { namespaceURI, name } = attr.node
            replacedNode = namespaceURI?
                node.getAttributeNodeNS(namespaceURI, name) :
                node.getAttributeNode(name)
            attr.ownerElement = node
        }
        else {
            replacedNode = attr.namespaceURI?
                node.setAttributeNodeNS(attr) :
                node.setAttributeNode(attr)
        }
        if(replacedNode) {
            const { attrAssembler } = this.constructor
            return this.getInstance(replacedNode, attrAssembler)
        }
        else return null
    }

    /**
     * this.getAttributeNode(AttrAssembler)
     * this.getAttributeNode('attr')
     *
     * @param {Function|String} attr
     * @returns {AttrAssembler|null}
     */
    getAttributeNode(attr) {
        const node = this.node
        const isFunctionType = typeof attr === TYPE_FUNCTION
        let attrNode
        if(isFunctionType) {
            const { namespaceURI, qualifiedName, localName } = attr
            attrNode = namespaceURI?
                node.getAttributeNodeNS(namespaceURI, localName) :
                node.getAttributeNode(qualifiedName)
        }
        else attrNode = node.getAttributeNode(attr)
        if(attrNode) {
            const assembler = isFunctionType?
                attr :
                this.constructor.attrAssembler
            return this.getInstance(attrNode, assembler)
        }
        else return null
    }

    /**
     * this.removeAttributeNode('attr')
     * this.removeAttributeNode(AttrAssembler)
     * this.removeAttributeNode(this.getAttributeNode('attr'))
     * this.removeAttributeNode(this.node.getAttributeNode('attr'))
     *
     * @param {AttrAssembler|Attr|Function|String} attr
     * @returns {AttrAssembler|ElementAssembler.attrAssembler|null}
     */
    removeAttributeNode(attr) {
        const node = this.node
        const type = typeof attr
        if(type === TYPE_STRING || type === TYPE_FUNCTION) {
            const instance = this.getAttributeNode(attr)
            if(instance) {
                node.removeAttributeNode(instance.node)
                return instance
            }
            else return null
        }
        else {
            if(attr instanceof AttrAssembler) return attr.remove()
            else {
                const attrNode = node.removeAttributeNode(attr)
                const { attrAssembler } = this.constructor
                return this.getInstance(attrNode, attrAssembler)
            }
        }
    }

    /**
     * Set the unique identifier on the element
     * @param {String} id
     */
    set id(id) {
        this.node.id = id
    }

    /**
     * Get the unique identifier of the element
     * @returns {String}
     */
    get id() {
        return this.node.id
    }

    /**
     * Set the class name of the element
     * @param {String} className
     */
    set className(className) {
        this.node.className = className
    }

    /**
     * Get the class name of the element
     * @returns {String}
     */
    get className() {
        return this.node.className
    }

    /**
     * Set the class list of the element
     * @param {*} classList token / token list / token-boolean dict {String|Array|{}}
     */
    set classList(classList) {
        if(classList) {
            const list = this.node.classList
            if(isArray(classList)) {
                classList.forEach(token => this.classList = token)
            }
            else if(classList.constructor === Object) {
                keys(classList).forEach(token => {
                    if(classList[token]) list.add(token)
                })
            }
            else list.add(classList)
        }
    }

    /**
     * Get the class list of the element as an array
     * @returns {DOMTokenList} classList interface
     */
    get classList() {
        return this.node.classList
    }

    /**
     * Set content attributes on the element
     * @param {*} attrset dictionary object
     */
    set attrset(attrset) {
        const node = this.node
        for(let name in attrset) {
            node.setAttribute(name, attrset[name])
        }
    }

    /**
     * Get all content attributes of the element as a dictionary-object
     * @returns {{}}
     */
    get attrset() {
        return reduce.call(this.node.attributes, attrset, {})
    }

    /**
     * Set content attributes on the element
     * @param {*} attributes
     */
    set attributes(attributes) {
        if(attributes instanceof AttrAssembler) {
            attributes.ownerElement = this.node
        }
        else if(isArray(attributes)) {
            attributes.forEach(attr => this.attributes = attr)
        }
        else this.setAttributeNode(attributes)
    }

    /**
     * Get all attributes of the element as an array
     * @returns {Array}
     */
    get attributes() {
        const { attrAssembler } = this.constructor
        return map.call(this.node.attributes, node => {
            return this.getInstance(node, attrAssembler)
        })
    }

    /**
     * Append children to the element
     * @param {*} children
     */
    set children(children) {
        this.append(children)
    }

    /**
     * Get all children of the element as an array
     * @returns {Array}
     */
    get children() {
        const { elementAssembler } = this.constructor
        return map.call(this.node.children, node => {
            return this.getInstance(node, elementAssembler)
        })
    }

    /**
     * @returns {ChildNodeAssembler|*}
     */
    get firstElementChild() {
        return this.getInstance(
            this.node.firstElementChild,
            this.constructor.elementAssembler)
    }

    /**
     * @returns {ChildNodeAssembler|*}
     */
    get lastElementChild() {
        return this.getInstance(
            this.node.lastElementChild,
            this.constructor.elementAssembler)
    }

    /**
     * @param {ParentNodeAssembler|*} parentNode
     */
    set parentNode(parentNode) {
        if(parentNode instanceof DocumentAssembler) {
            parentNode.documentElement = this.node
        }
        else {
            if(parentNode instanceof Document) {
                const element = parentNode.documentElement
                if(element) parentNode.removeChild(element)
            }
            super.parentNode = parentNode
        }
    }

    /**
     * @returns {ParentNodeAssembler|*}
     */
    get parentNode() {
        return super.parentNode
    }

    /**
     * Get a next sibling of the element
     * @returns {ChildNodeAssembler|null|*}
     */
    get nextElementSibling() {
        return this.getInstance(
            this.node.nextElementSibling,
            this.constructor.elementAssembler)
    }

    /**
     * Get a previous sibling of the element
     * @returns {ChildNodeAssembler|null|*}
     */
    get previousElementSibling() {
        return this.getInstance(
            this.node.previousElementSibling,
            this.constructor.elementAssembler)
    }

    /**
     * Get the element index among its sibling elements or -1 if it has no parent
     * @returns {Number}
     */
    get elementIndex() {
        const parentNode = this.node.parentNode
        return parentNode?
            indexOf.call(parentNode.children, this.node) :
            NO_PARENT_INDEX
    }

    /**
     * Create the specified Element node
     * @param {*} [init]
     * @returns {Element|*} created element
     */
    static create(init) {
        const {
            namespaceURI = this.namespaceURI,
            prefix = this.prefix,
            localName = this.localName
        } = init || this
        let qualifiedName = init && init.qualifiedName
        if(!qualifiedName) {
            qualifiedName = prefix?
                prefix + NAMESPACE_SEPARATOR + localName :
                localName
        }
        return document.createElementNS(namespaceURI, qualifiedName)
    }

    /**
     * Default namespace URI
     * @returns {String}
     */
    static get namespaceURI() {
        return DEFAULT_NAMESPACE_URI
    }

    /**
     * The XML namespace prefix
     * @returns {String}
     */
    static get prefix() {
        return DEFAULT_PREFIX
    }

    /**
     * The local name of the element node
     * @returns {String}
     */
    static get localName() {
        return this === ElementAssembler? element.name : this.name.toLowerCase()
    }

    /**
     * The qualified name of the element node
     * @returns {String}
     */
    static get qualifiedName() {
        const { prefix, localName } = this
        return prefix?
            prefix + NAMESPACE_SEPARATOR + localName :
            localName
    }

    /**
     * @returns {Function}
     */
    static get attrAssembler() {
        return AttrAssembler
    }

    /**
     * @returns {Function}
     */
    static get elementAssembler() {
        return ElementAssembler
    }

    /**
     * @returns {Function}
     */
    static get interface() {
        return Element
    }
}

/**
 * Element assembler factory
 * @param {*} init
 * @returns {ElementAssembler}
 */
export function element(...init) {
    return new ElementAssembler(...init)
}
<|endoftext|>


<commit_before>import {
    TYPE_STRING,
    TYPE_FUNCTION,
    EMPTY_STRING,
    NAMESPACE_SEPARATOR,
    NO_PARENT_INDEX
} from './common'
import { AttrAssembler } from './attr'
import { NodeAssembler } from './node'
import { ParentNodeAssembler } from './parentnode'

const { keys } = Object
const { isArray, prototype : { indexOf, map, forEach } } = Array
const { Attr, Document, Element, Node, document } = window

const DEFAULT_PROPERTY_NAME = 'children'
const DEFAULT_SELECTOR = '*'
const ID_PREFIX = 'ID'
const IGNORED_PROPERTY_NAMES = ['namespace', 'localName', 'qualifiedName', 'prefix']
const resolver = {}
const { getNodeOf } = NodeAssembler

let counter = Date.now()

/**
 * @see https://www.w3.org/TR/dom/#interface-element
 */
export class ElementAssembler extends ParentNodeAssembler {
    /**
     * @param {class|string} object - ElementAssembler or AttrAssembler subclass or selector
     * @returns {ElementAssembler|AttrAssembler|*|null}
     */
    closest(object) {
        if(typeof object === TYPE_FUNCTION) {
            const node = this.node.closest(object.selector)
            if(node) {
                const isAttr = AttrAssembler.isPrototypeOf(object)
                const element = this.getInstanceOf(node, !isAttr && object)
                return isAttr?
                    element.getAttributeNode(object) :
                    element
            }
            return null
        }
        return this.getInstanceOf(this.node.closest(object))
    }

    /**
     * this.getAttribute(AttrAssembler)
     * this.getAttribute('attr')
     *
     * @param {class|string} attr
     * @returns {string|*|null}
     */
    getAttribute(attr) {
        if(typeof attr === TYPE_FUNCTION) {
            const instance = this.getAttributeNode(attr)
            return instance? instance.value : attr.defaultValue
        }
        return this.node.getAttribute(attr)
    }

    /**
     * this.getAttributeNode(AttrAssembler)
     * this.getAttributeNode('attr')
     *
     * @param {class|string} attr
     * @returns {AttrAssembler|*|null}
     */
    getAttributeNode(attr) {
        const node = this.node
        const isFunction = typeof attr === TYPE_FUNCTION
        let attrNode
        if(isFunction) {
            const { namespace, qualifiedName, localName } = attr
            attrNode = namespace?
                node.getAttributeNodeNS(namespace, localName) :
                node.getAttributeNode(qualifiedName)
        }
        else {
            attrNode = node.getAttributeNode(attr)
        }
        return this.getInstanceOf(attrNode, isFunction && attr)
    }

    /**
     * @param {class|string} attr
     * @returns {boolean}
     */
    hasAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespace, qualifiedName, localName } = attr
            return namespace?
                node.hasAttributeNS(namespace, localName) :
                node.hasAttribute(qualifiedName)
        }
        return node.hasAttribute(attr)
    }

    /**
     * @returns {boolean}
     */
    hasAttributes() {
        return this.node.hasAttributes()
    }

    /**
     * @param {string} selectors
     * @returns {boolean}
     */
    matches(selectors) {
        return this.node.matches(selectors)
    }

    /**
     * @param {class|string} attr
     */
    removeAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespace, qualifiedName, localName } = attr
            if(namespace) {
                node.removeAttributeNS(namespace, localName)
            }
            else node.removeAttribute(qualifiedName)
        }
        else node.removeAttribute(attr)
    }

    /**
     * this.removeAttributeNode('attr')
     * this.removeAttributeNode(AttrAssembler)
     * this.removeAttributeNode(this.getAttributeNode('attr'))
     * this.removeAttributeNode(this.node.getAttributeNode('attr'))
     *
     * @param {AttrAssembler|Attr|class|string} attr
     * @returns {AttrAssembler|null}
     */
    removeAttributeNode(attr) {
        const type = typeof attr
        const isFunction = type === TYPE_FUNCTION
        if(isFunction || type === TYPE_STRING) {
            const instance = this.getAttributeNode(attr)
            if(instance) {
                instance.remove()
                return instance
            }
            return null
        }
        if(attr instanceof AttrAssembler) {
            attr.remove()
            return attr
        }
        return this.getInstanceOf(this.node.removeAttributeNode(attr), isFunction && attr)
    }

    /**
     * this.setAttribute(AttrAssembler, 'foobar')
     * this.setAttribute('attr', 'foobar')
     *
     * @param {class|string} attr
     * @param {function} [attr.removeOnValue]
     * @param {string|*} value
     */
    setAttribute(attr, value) {
        const instance = this.getAttributeNode(attr)
        if(instance) {
            instance.value = value
        }
        else {
            if(typeof attr === TYPE_FUNCTION) {
                if(!attr.removeOnValue(value)) {
                    const instance = new attr
                    this.setAttributeNode(instance)
                    instance.value = value
                }
            }
            else if(!this.constructor.attrAssembler.removeOnValue(value)) {
                this.node.setAttribute(attr, value)
            }
        }
    }

    /**
     * this.setAttributeNode(new AttrAssembler)
     * this.setAttributeNode(document.createAttribute('attr'))
     *
     * @param {AttrAssembler|Attr} attr
     * @returns {AttrAssembler|null}
     */
    setAttributeNode(attr) {
        const node = this.node
        let replacedNode
        if(attr instanceof AttrAssembler) {
            const { namespaceURI, name } = attr
            replacedNode = namespaceURI?
                node.getAttributeNodeNS(namespaceURI, name) :
                node.getAttributeNode(name)
            attr.ownerElement = node
        }
        else {
            replacedNode = attr.namespaceURI?
                node.setAttributeNodeNS(attr) :
                node.setAttributeNode(attr)
        }
        return this.getInstanceOf(replacedNode)
    }

    /**
     * @param {string} name
     * @param {string} value
     */
    setProperty(name, value) {
        if(!IGNORED_PROPERTY_NAMES.includes(name)) {
            super.setProperty(name, value)
        }
    }

    /**
     * Set content attributes on the element
     * @param {*} attributes
     */
    set attributes(attributes) {
        if(attributes instanceof AttrAssembler) {
            attributes.ownerElement = this.node
        }
        else if(isArray(attributes)) {
            attributes.forEach(attr => this.attributes = attr)
        }
        else this.setAttributeNode(attributes)
    }

    /**
     * Get all attributes of the element as an array
     * @returns {AttrAssembler[]}
     */
    get attributes() {
        return map.call(this.node.attributes, node => this.getInstanceOf(node))
    }

    /**
     * Set content attributes on the element
     * @param {*} attrset dictionary object
     */
    set attrset(attrset) {
        const node = this.node
        for(let name in attrset) {
            if(attrset.hasOwnProperty(name)) {
                node.setAttribute(name, attrset[name])
            }
        }
    }

    /**
     * Get all content attributes of the element as a dictionary-object
     * @returns {{}}
     */
    get attrset() {
        const attrset = {}
        forEach.call(this.node.attributes, ({ name, value }) => {
            attrset[name] = value
        })
        return attrset
    }

    /**
     * Set the class list of the element
     * @param {*} classList token / token list / token-boolean dict {string|string[]|{}}
     */
    set classList(classList) {
        if(classList) {
            const list = this.node.classList
            if(isArray(classList)) {
                classList.forEach(token => this.classList = token)
            }
            else if(classList.constructor === Object) {
                keys(classList).forEach(token => {
                    if(classList[token]) {
                        list.add(token)
                    }
                })
            }
            else list.add(classList)
        }
    }

    /**
     * Get the class list of the element as an array
     * @returns {DOMTokenList} classList interface
     */
    get classList() {
        return this.node.classList
    }

    /**
     * Set the class name of the element
     * @param {string} className
     */
    set className(className) {
        if(className) {
            this.node.className = className
        }
        else this.node.removeAttribute('class')
    }

    /**
     * Get the class name of the element
     * @returns {string}
     */
    get className() {
        return this.node.className
    }

    /**
     * Get the element index among its sibling elements or -1 if it has no parent
     * @returns {number}
     */
    get elementIndex() {
        const parentNode = this.node.parentNode
        return parentNode?
            indexOf.call(parentNode.children, this.node) :
            NO_PARENT_INDEX
    }

    /**
     * Set the unique identifier on the element
     * @param {string} id
     */
    set id(id) {
        this.node.id = id
    }

    /**
     * Get the unique identifier of the element
     * @returns {string}
     */
    get id() {
        return this.node.id
    }

    /**
     * @returns {string}
     */
    get localName() {
        return this.node.localName
    }

    /**
     * @returns {string|null}
     */
    get namespaceURI() {
        return this.node.namespaceURI
    }

    /**
     * @return {string}
     */
    get prefix() {
        return this.node.prefix
    }

    /**
     * @return {string}
     */
    get tagName() {
        return this.node.tagName
    }

    /**
     * Create the specified Element node
     * @param {*} [init]
     * @returns {Element|*} created element
     * @override
     */
    static create(init) {
        const {
            namespace = this.namespace,
            prefix = this.prefix,
            localName = this.localName
        } = init || this
        let qualifiedName = init && init.qualifiedName
        if(!qualifiedName) {
            qualifiedName = prefix?
                prefix + NAMESPACE_SEPARATOR + localName :
                localName
        }
        return document.createElementNS(namespace, qualifiedName)
    }

    /**
     * Generate a unique identifier among the document's tree
     * @returns {String}
     */
    static generateId() {
        let id
        do id = ID_PREFIX + counter++
        while(document.getElementById(id))
        return id
    }

    /**
     * @param {string|Element|Document|DocumentFragment} [selectorOrContext=this.selector]
     * @param {Element|Document|DocumentFragment} [context=window.document]
     * @returns {ElementAssembler[]} array of initialized instances
     */
    static init(selectorOrContext = this.selector, context = document) {
        if(selectorOrContext instanceof Node) {
            context = selectorOrContext
            selectorOrContext = this.selector
        }
        return map.call(context.querySelectorAll(selectorOrContext), node => {
            return new this({ node })
        })
    }

    /**
     * Register assembler by it's namespace and localName
     */
    static register() {
        this.elementResolver[this.namespace + this.localName] = this
    }

    /**
     * @param {Element|*} node
     * @returns {ElementAssembler|*}
     */
    static resolveElement(node) {
        const resolver = this.elementResolver
        const { namespaceURI, localName } = node
        const assembler = namespaceURI?
            resolver[namespaceURI + localName] || resolver[namespaceURI] :
            resolver[localName]
        return assembler || ElementAssembler
    }

    /**
     * @returns {string}
     * @override
     */
    static get defaultPropertyName() {
        return DEFAULT_PROPERTY_NAME
    }

    /**
     * @returns {{}}
     */
    static get elementResolver() {
        return resolver
    }

    /**
     * @returns {class} Element
     * @override
     */
    static get interface() {
        return Element
    }

    /**
     * The local name of the element node
     * @returns {string}
     */
    static get localName() {
        return this === this.superAssembler?
            EMPTY_STRING :
            this.name.toLowerCase()
    }

    /**
     * Default namespace URI
     * @returns {string}
     */
    static get namespace() {
        return EMPTY_STRING
    }

    /**
     * The XML namespace prefix
     * @returns {string}
     */
    static get prefix() {
        return EMPTY_STRING
    }

    /**
     * The qualified name of the element node
     * @returns {string}
     */
    static get qualifiedName() {
        const { prefix, localName } = this
        return prefix?
            prefix + NAMESPACE_SEPARATOR + localName :
            localName
    }

    /**
     * @returns {string}
     */
    static get selector() {
        return this.localName || DEFAULT_SELECTOR
    }

    /**
     * @returns {class} ElementAssembler
     */
    static get superAssembler() {
        return ElementAssembler
    }
}

NodeAssembler.ElementAssembler = ElementAssembler

/**
 * Element assembler factory
 * @param {*} [init]
 * @returns {ElementAssembler|*}
 */
export function element(init) {
    return new ElementAssembler(init)
}
<commit_msg>ElementAssembler.id: throw on setting a non unique id<commit_after>import {
    TYPE_STRING,
    TYPE_FUNCTION,
    EMPTY_STRING,
    NAMESPACE_SEPARATOR,
    NO_PARENT_INDEX
} from './common'
import { AttrAssembler } from './attr'
import { NodeAssembler } from './node'
import { ParentNodeAssembler } from './parentnode'

const { keys } = Object
const { isArray, prototype : { indexOf, map, forEach } } = Array
const { Attr, Document, Element, Node, document } = window

const DEFAULT_PROPERTY_NAME = 'children'
const DEFAULT_SELECTOR = '*'
const ID_PREFIX = 'ID'
const IGNORED_PROPERTY_NAMES = ['namespace', 'localName', 'qualifiedName', 'prefix']
const resolver = {}
const { getNodeOf } = NodeAssembler

let counter = Date.now()

/**
 * @see https://www.w3.org/TR/dom/#interface-element
 */
export class ElementAssembler extends ParentNodeAssembler {
    /**
     * @param {class|string} object - ElementAssembler or AttrAssembler subclass or selector
     * @returns {ElementAssembler|AttrAssembler|*|null}
     */
    closest(object) {
        if(typeof object === TYPE_FUNCTION) {
            const node = this.node.closest(object.selector)
            if(node) {
                const isAttr = AttrAssembler.isPrototypeOf(object)
                const element = this.getInstanceOf(node, !isAttr && object)
                return isAttr?
                    element.getAttributeNode(object) :
                    element
            }
            return null
        }
        return this.getInstanceOf(this.node.closest(object))
    }

    /**
     * this.getAttribute(AttrAssembler)
     * this.getAttribute('attr')
     *
     * @param {class|string} attr
     * @returns {string|*|null}
     */
    getAttribute(attr) {
        if(typeof attr === TYPE_FUNCTION) {
            const instance = this.getAttributeNode(attr)
            return instance? instance.value : attr.defaultValue
        }
        return this.node.getAttribute(attr)
    }

    /**
     * this.getAttributeNode(AttrAssembler)
     * this.getAttributeNode('attr')
     *
     * @param {class|string} attr
     * @returns {AttrAssembler|*|null}
     */
    getAttributeNode(attr) {
        const node = this.node
        const isFunction = typeof attr === TYPE_FUNCTION
        let attrNode
        if(isFunction) {
            const { namespace, qualifiedName, localName } = attr
            attrNode = namespace?
                node.getAttributeNodeNS(namespace, localName) :
                node.getAttributeNode(qualifiedName)
        }
        else {
            attrNode = node.getAttributeNode(attr)
        }
        return this.getInstanceOf(attrNode, isFunction && attr)
    }

    /**
     * @param {class|string} attr
     * @returns {boolean}
     */
    hasAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespace, qualifiedName, localName } = attr
            return namespace?
                node.hasAttributeNS(namespace, localName) :
                node.hasAttribute(qualifiedName)
        }
        return node.hasAttribute(attr)
    }

    /**
     * @returns {boolean}
     */
    hasAttributes() {
        return this.node.hasAttributes()
    }

    /**
     * @param {string} selectors
     * @returns {boolean}
     */
    matches(selectors) {
        return this.node.matches(selectors)
    }

    /**
     * @param {class|string} attr
     */
    removeAttribute(attr) {
        const node = this.node
        if(typeof attr === TYPE_FUNCTION) {
            const { namespace, qualifiedName, localName } = attr
            if(namespace) {
                node.removeAttributeNS(namespace, localName)
            }
            else node.removeAttribute(qualifiedName)
        }
        else node.removeAttribute(attr)
    }

    /**
     * this.removeAttributeNode('attr')
     * this.removeAttributeNode(AttrAssembler)
     * this.removeAttributeNode(this.getAttributeNode('attr'))
     * this.removeAttributeNode(this.node.getAttributeNode('attr'))
     *
     * @param {AttrAssembler|Attr|class|string} attr
     * @returns {AttrAssembler|null}
     */
    removeAttributeNode(attr) {
        const type = typeof attr
        const isFunction = type === TYPE_FUNCTION
        if(isFunction || type === TYPE_STRING) {
            const instance = this.getAttributeNode(attr)
            if(instance) {
                instance.remove()
                return instance
            }
            return null
        }
        if(attr instanceof AttrAssembler) {
            attr.remove()
            return attr
        }
        return this.getInstanceOf(this.node.removeAttributeNode(attr), isFunction && attr)
    }

    /**
     * this.setAttribute(AttrAssembler, 'foobar')
     * this.setAttribute('attr', 'foobar')
     *
     * @param {class|string} attr
     * @param {function} [attr.removeOnValue]
     * @param {string|*} value
     */
    setAttribute(attr, value) {
        const instance = this.getAttributeNode(attr)
        if(instance) {
            instance.value = value
        }
        else {
            if(typeof attr === TYPE_FUNCTION) {
                if(!attr.removeOnValue(value)) {
                    const instance = new attr
                    this.setAttributeNode(instance)
                    instance.value = value
                }
            }
            else if(!this.constructor.attrAssembler.removeOnValue(value)) {
                this.node.setAttribute(attr, value)
            }
        }
    }

    /**
     * this.setAttributeNode(new AttrAssembler)
     * this.setAttributeNode(document.createAttribute('attr'))
     *
     * @param {AttrAssembler|Attr} attr
     * @returns {AttrAssembler|null}
     */
    setAttributeNode(attr) {
        const node = this.node
        let replacedNode
        if(attr instanceof AttrAssembler) {
            const { namespaceURI, name } = attr
            replacedNode = namespaceURI?
                node.getAttributeNodeNS(namespaceURI, name) :
                node.getAttributeNode(name)
            attr.ownerElement = node
        }
        else {
            replacedNode = attr.namespaceURI?
                node.setAttributeNodeNS(attr) :
                node.setAttributeNode(attr)
        }
        return this.getInstanceOf(replacedNode)
    }

    /**
     * @param {string} name
     * @param {string} value
     */
    setProperty(name, value) {
        if(!IGNORED_PROPERTY_NAMES.includes(name)) {
            super.setProperty(name, value)
        }
    }

    /**
     * Set content attributes on the element
     * @param {*} attributes
     */
    set attributes(attributes) {
        if(attributes instanceof AttrAssembler) {
            attributes.ownerElement = this.node
        }
        else if(isArray(attributes)) {
            attributes.forEach(attr => this.attributes = attr)
        }
        else this.setAttributeNode(attributes)
    }

    /**
     * Get all attributes of the element as an array
     * @returns {AttrAssembler[]}
     */
    get attributes() {
        return map.call(this.node.attributes, node => this.getInstanceOf(node))
    }

    /**
     * Set content attributes on the element
     * @param {*} attrset dictionary object
     */
    set attrset(attrset) {
        const node = this.node
        for(let name in attrset) {
            if(attrset.hasOwnProperty(name)) {
                node.setAttribute(name, attrset[name])
            }
        }
    }

    /**
     * Get all content attributes of the element as a dictionary-object
     * @returns {{}}
     */
    get attrset() {
        const attrset = {}
        forEach.call(this.node.attributes, ({ name, value }) => {
            attrset[name] = value
        })
        return attrset
    }

    /**
     * Set the class list of the element
     * @param {*} classList token / token list / token-boolean dict {string|string[]|{}}
     */
    set classList(classList) {
        if(classList) {
            const list = this.node.classList
            if(isArray(classList)) {
                classList.forEach(token => this.classList = token)
            }
            else if(classList.constructor === Object) {
                keys(classList).forEach(token => {
                    if(classList[token]) {
                        list.add(token)
                    }
                })
            }
            else list.add(classList)
        }
    }

    /**
     * Get the class list of the element as an array
     * @returns {DOMTokenList} classList interface
     */
    get classList() {
        return this.node.classList
    }

    /**
     * Set the class name of the element
     * @param {string} className
     */
    set className(className) {
        if(className) {
            this.node.className = className
        }
        else this.node.removeAttribute('class')
    }

    /**
     * Get the class name of the element
     * @returns {string}
     */
    get className() {
        return this.node.className
    }

    /**
     * Get the element index among its sibling elements or -1 if it has no parent
     * @returns {number}
     */
    get elementIndex() {
        const parentNode = this.node.parentNode
        return parentNode?
            indexOf.call(parentNode.children, this.node) :
            NO_PARENT_INDEX
    }

    /**
     * Set the unique identifier on the element
     * @param {string} id
     */
    set id(id) {
        if(document.getElementById(id)) {
            throw Error(`Failed to set 'id' on '${ this.tagName }': the same id already exists in the document.`)
        }
        this.node.id = id
    }

    /**
     * Get the unique identifier of the element
     * @returns {string}
     */
    get id() {
        return this.node.id
    }

    /**
     * @returns {string}
     */
    get localName() {
        return this.node.localName
    }

    /**
     * @returns {string|null}
     */
    get namespaceURI() {
        return this.node.namespaceURI
    }

    /**
     * @return {string}
     */
    get prefix() {
        return this.node.prefix
    }

    /**
     * @return {string}
     */
    get tagName() {
        return this.node.tagName
    }

    /**
     * Create the specified Element node
     * @param {*} [init]
     * @returns {Element|*} created element
     * @override
     */
    static create(init) {
        const {
            namespace = this.namespace,
            prefix = this.prefix,
            localName = this.localName
        } = init || this
        let qualifiedName = init && init.qualifiedName
        if(!qualifiedName) {
            qualifiedName = prefix?
                prefix + NAMESPACE_SEPARATOR + localName :
                localName
        }
        return document.createElementNS(namespace, qualifiedName)
    }

    /**
     * Generate a unique identifier among the document's tree
     * @returns {String}
     */
    static generateId() {
        let id
        do id = ID_PREFIX + counter++
        while(document.getElementById(id))
        return id
    }

    /**
     * @param {string|Element|Document|DocumentFragment} [selectorOrContext=this.selector]
     * @param {Element|Document|DocumentFragment} [context=window.document]
     * @returns {ElementAssembler[]} array of initialized instances
     */
    static init(selectorOrContext = this.selector, context = document) {
        if(selectorOrContext instanceof Node) {
            context = selectorOrContext
            selectorOrContext = this.selector
        }
        return map.call(context.querySelectorAll(selectorOrContext), node => {
            return new this({ node })
        })
    }

    /**
     * Register assembler by it's namespace and localName
     */
    static register() {
        this.elementResolver[this.namespace + this.localName] = this
    }

    /**
     * @param {Element|*} node
     * @returns {ElementAssembler|*}
     */
    static resolveElement(node) {
        const resolver = this.elementResolver
        const { namespaceURI, localName } = node
        const assembler = namespaceURI?
            resolver[namespaceURI + localName] || resolver[namespaceURI] :
            resolver[localName]
        return assembler || ElementAssembler
    }

    /**
     * @returns {string}
     * @override
     */
    static get defaultPropertyName() {
        return DEFAULT_PROPERTY_NAME
    }

    /**
     * @returns {{}}
     */
    static get elementResolver() {
        return resolver
    }

    /**
     * @returns {class} Element
     * @override
     */
    static get interface() {
        return Element
    }

    /**
     * The local name of the element node
     * @returns {string}
     */
    static get localName() {
        return this === this.superAssembler?
            EMPTY_STRING :
            this.name.toLowerCase()
    }

    /**
     * Default namespace URI
     * @returns {string}
     */
    static get namespace() {
        return EMPTY_STRING
    }

    /**
     * The XML namespace prefix
     * @returns {string}
     */
    static get prefix() {
        return EMPTY_STRING
    }

    /**
     * The qualified name of the element node
     * @returns {string}
     */
    static get qualifiedName() {
        const { prefix, localName } = this
        return prefix?
            prefix + NAMESPACE_SEPARATOR + localName :
            localName
    }

    /**
     * @returns {string}
     */
    static get selector() {
        return this.localName || DEFAULT_SELECTOR
    }

    /**
     * @returns {class} ElementAssembler
     */
    static get superAssembler() {
        return ElementAssembler
    }
}

NodeAssembler.ElementAssembler = ElementAssembler

/**
 * Element assembler factory
 * @param {*} [init]
 * @returns {ElementAssembler|*}
 */
export function element(init) {
    return new ElementAssembler(init)
}
<|endoftext|>


<commit_before>var ei = require('./const')
    , util = require('util')
    , EiRef = require('./ref')
    , EiPid = require('./pid')
    , EiPort = require('./port')
    , Buffers = require('buffers')
    , Readable = require('stream').Readable
    , BUFFER_SIZE = ('undefined' === typeof(process.env.EI_ENCODER_SIZE)?
        256 : parseInt(process.env.EI_ENCODER_SIZE, 10))
;

if (!Readable) {
    Readable = require('readable-stream').Readable;
}

function EiEncoder(options) {
    'use strict';

    if (!(this instanceof EiEncoder)) {
        return new EiEncoder(options);
    }

    Readable.call(this, options);

    this.buffers = new Buffers();
    this.buf = new Buffer(BUFFER_SIZE);
    //this.buf.fill(0);
    this.index = 0;
    this.offset = 0;

    Object.defineProperty(this, 'length', {
        enumerable: true,
        get: function() {
            return ((this.index - this.offset) + this.buffers.length);
        }
    });
}

util.inherits(EiEncoder, Readable);

EiEncoder.prototype._extend = function(len) {
    'use strict';

    len = len || BUFFER_SIZE;

    if ((len + (this.index - this.offset)) > this.buf.length) {
        console.log('EXTEND:', this.index, this.buf.length);
        this.buffers.push(this.buf.slice(this.offset, this.index));

        this.buf = new Buffer(len);
        //this.buf.fill(0);
        this.index = 0;
        this.offset = 0;
    }
};

EiEncoder.prototype._read = function(len) {
    'use strict';

    var i = 0, cut;

    if (isNaN(len) || 0 >= len) {
        len = this.length;
    }

    if (0 >= (this.index - this.offset)) {
        return this.push(null);
    }

    console.log('_read: push (off=%d, idx=%d)', this.offset, this.index);
    this.buffers.push(this.buf.slice(this.offset, this.index));
    this.offset = this.index;

    cut = this.buffers.splice(0, len);

    for (; i < cut.buffers.length; i++) {
        console.log('push:', cut.buffers[i]);
        this.push(cut.buffers[i]);
    }
};

EiEncoder.prototype.encodeVersion = function() {
    'use strict';

    this.appendUInt8(ei.VERSION_MAGIC);
};

EiEncoder.prototype.encodeLong = function(val) {
    'use strict';

    if ((val < 256) && (val >= 0)) {
        this.appendUInt8(ei.SMALL_INTEGER_EXT);
        this.appendInt8(val);
    } else if (val <= ei.MAX && val >= ei.MIN) {
        this.appendUInt8(ei.INTEGER_EXT);
        this.appendInt32BE(val);
    } else {
        throw new Error('BIG not implemented');
    }
};

EiEncoder.prototype.encodeULong = function(val) {
    'use strict';

    if (val < 0) {
        throw new TypeError('Cannot encode less than 0');
    } else if ((val < 256) && (val >= 0)) {
        this.appendUInt8(ei.SMALL_INTEGER_EXT);
        this.appendUInt8(val);
    } else if (val <= ei.MAX) {
        this.appendUInt8(ei.INTEGER_EXT);
        this.appendUInt32BE(val);
    } else {
        throw new Error('BIG not implemented');
    }
};

EiEncoder.prototype.encodeDouble = function(val) {
    'use strict';

    if (typeof(val) !== 'number') {
        throw new TypeError('Only numbers are accepted');
    } else {
        this.appendUInt8(ei.NEW_FLOAT_EXT);
        this.appendDoubleBE(val);
    }
};

EiEncoder.prototype.encodeString = function(val, encoding) {
    'use strict';

    if (typeof(val) !== 'string') {
        throw new TypeError('Only strings are accepted');
    } else if (0 === val.length) {
        this.appendUInt8(ei.NIL_EXT);
    } else if (65535 >= val.length) {
        var buf = new Buffer(val, encoding);

        this.appendUInt8(ei.STRING_EXT);
        this.appendUInt16BE(buf.length);
        this.appendBuffer(buf);
    } else {
        throw new Error('Cannot encode strings as lists yet');
    }
};

EiEncoder.prototype.encodeAtom = function(val, encoding) {
    'use strict';

    var type = typeof(val);

    encoding = (encoding || 'ascii').toLowerCase();

    if (type !== 'string' && type !== 'number') {
        throw new TypeError('Only strings & numbers are accepted');
    } else if (0 === (val = val.toString()).length) {
        throw new Error('Length must be greater than zero');
    } else {
        var buf = new Buffer(val, encoding);

        if (256 < val.length) {
            if ('ascii' === encoding) {
                this.appendUInt8(ei.ATOM_EXT);
            } else {
                this.appendUInt8(ei.ATOM_UTF8_EXT);
            }

            this.appendUInt16BE(buf.length);
        } else {
            if ('ascii' === encoding) {
                this.appendUInt8(ei.SMALL_ATOM_EXT);
            } else {
                this.appendUInt8(ei.SMALL_ATOM_UTF8_EXT);
            }

            this.appendUInt8(buf.length);
        }

        this.appendBuffer(buf);
    }
};

EiEncoder.prototype.encodeBool = function(val) {
    'use strict';

    if (typeof(val) !== 'boolean') {
        throw new TypeError('Only booleans are allowed');
    }

    this.encodeAtom(val.toString());
};

EiEncoder.prototype.encodeBinary = function(val, len) {
    'use strict';

    len = len || val.length;

    if (!Buffer.isBuffer(val)) {
        throw new TypeError('Only Buffers are accepted');
    } else {
        this.appendUInt8(ei.BINARY_EXT);
        this.appendUInt32BE(len);
        this.appendBuffer(val, len);
    }
};

EiEncoder.prototype.encodeListHeader = function(cnt) {
    'use strict';

    if (typeof(cnt) !== 'number') {
        throw new TypeError('Only numbers are accepted');
    } else if (0 > cnt) {
        throw new TypeError('Arity must be >= 0');
    } else if (0 === cnt) {
        this.appendUInt8(ei.NIL_EXT);
    } else {
        this.appendUInt8(ei.LIST_EXT);
        this.appendUInt32BE(cnt);
    }
};

EiEncoder.prototype.encodeTupleHeader = function(cnt) {
    'use strict';

    if (typeof(cnt) !== 'number') {
        throw new TypeError('Only numbers are accepted');
    } else if (0 > cnt) {
        throw new TypeError('Arity must be >= 0');
    } else if (255 >= cnt) {
        this.appendUInt8(ei.SMALL_TUPLE_EXT);
        this.appendUInt8(cnt);
    } else {
        this.appendUInt8(ei.LARGE_TUPLE_EXT);
        this.appendUInt32BE(cnt);
    }
};

function appender(encoder, len) {
    'use strict';

    return function(data) {
        this._extend(len);
        encoder.call(this.buf, data, (this.index - this.offset), false);
        this.index += len;
        return true;
    };
}

EiEncoder.prototype.appendUInt8 = appender(Buffer.prototype.writeUInt8, 1);
EiEncoder.prototype.appendUInt16BE = appender(Buffer.prototype.writeUInt16BE, 2);
EiEncoder.prototype.appendUInt32BE = appender(Buffer.prototype.writeUInt32BE, 4);
EiEncoder.prototype.appendInt8 = appender(Buffer.prototype.writeInt8, 1);
EiEncoder.prototype.appendInt16BE = appender(Buffer.prototype.writeInt16BE, 2);
EiEncoder.prototype.appendInt32BE = appender(Buffer.prototype.writeInt32BE, 4);
EiEncoder.prototype.appendFloatBE = appender(Buffer.prototype.writeFloatBE, 4);
EiEncoder.prototype.appendDoubleBE = appender(Buffer.prototype.writeDoubleBE, 8);
EiEncoder.prototype.appendBuffer = function(val, len) {
    if (!Buffer.isBuffer(val)) {
        throw new Type('Only Buffers are accepted');
    }

    len = len || val.length;

    if (0 < len) {
        this._extend(len);
        val.copy(this.buf, (this.index - this.offset), 0, len);
        this.index += len;
    }
}

module.exports = function(options) {
    'use strict';

    return new EiEncoder(options || {});
};
<commit_msg>encoder.js :: create buffer that are atleast the size we need<commit_after>var ei = require('./const')
    , util = require('util')
    , EiRef = require('./ref')
    , EiPid = require('./pid')
    , EiPort = require('./port')
    , Buffers = require('buffers')
    , Readable = require('stream').Readable
    , BUFFER_SIZE = ('undefined' === typeof(process.env.EI_ENCODER_SIZE)?
        256 : parseInt(process.env.EI_ENCODER_SIZE, 10))
;

if (!Readable) {
    Readable = require('readable-stream').Readable;
}

function EiEncoder(options) {
    'use strict';

    if (!(this instanceof EiEncoder)) {
        return new EiEncoder(options);
    }

    Readable.call(this, options);

    this.buffers = new Buffers();
    this.buf = new Buffer(BUFFER_SIZE);
    //this.buf.fill(0);
    this.index = 0;
    this.offset = 0;

    Object.defineProperty(this, 'length', {
        enumerable: true,
        get: function() {
            return ((this.index - this.offset) + this.buffers.length);
        }
    });
}

util.inherits(EiEncoder, Readable);

EiEncoder.prototype._extend = function(len) {
    'use strict';

    len = len || BUFFER_SIZE;

    if ((len + (this.index - this.offset)) > this.buf.length) {
        console.log('EXTEND:', this.index, this.buf.length, Math.max(len, BUFFER_SIZE));

        this.buffers.push(this.buf.slice(this.offset, this.index));
        this.buf = new Buffer(Math.max(len, BUFFER_SIZE));
        //this.buf.fill(0);
        this.index = 0;
        this.offset = 0;
    }
};

EiEncoder.prototype._read = function(len) {
    'use strict';

    var i = 0, cut;

    if (isNaN(len) || 0 >= len) {
        len = this.length;
    }

    if (0 >= (this.index - this.offset)) {
        return this.push(null);
    }

    console.log('_read: push (off=%d, idx=%d)', this.offset, this.index);
    this.buffers.push(this.buf.slice(this.offset, this.index));
    this.offset = this.index;

    cut = this.buffers.splice(0, len);

    for (; i < cut.buffers.length; i++) {
        console.log('push:', cut.buffers[i]);
        this.push(cut.buffers[i]);
    }
};

EiEncoder.prototype.encodeVersion = function() {
    'use strict';

    this.appendUInt8(ei.VERSION_MAGIC);
};

EiEncoder.prototype.encodeLong = function(val) {
    'use strict';

    if ((val < 256) && (val >= 0)) {
        this.appendUInt8(ei.SMALL_INTEGER_EXT);
        this.appendInt8(val);
    } else if (val <= ei.MAX && val >= ei.MIN) {
        this.appendUInt8(ei.INTEGER_EXT);
        this.appendInt32BE(val);
    } else {
        throw new Error('BIG not implemented');
    }
};

EiEncoder.prototype.encodeULong = function(val) {
    'use strict';

    if (val < 0) {
        throw new TypeError('Cannot encode less than 0');
    } else if ((val < 256) && (val >= 0)) {
        this.appendUInt8(ei.SMALL_INTEGER_EXT);
        this.appendUInt8(val);
    } else if (val <= ei.MAX) {
        this.appendUInt8(ei.INTEGER_EXT);
        this.appendUInt32BE(val);
    } else {
        throw new Error('BIG not implemented');
    }
};

EiEncoder.prototype.encodeDouble = function(val) {
    'use strict';

    if (typeof(val) !== 'number') {
        throw new TypeError('Only numbers are accepted');
    } else {
        this.appendUInt8(ei.NEW_FLOAT_EXT);
        this.appendDoubleBE(val);
    }
};

EiEncoder.prototype.encodeString = function(val, encoding) {
    'use strict';

    if (typeof(val) !== 'string') {
        throw new TypeError('Only strings are accepted');
    } else if (0 === val.length) {
        this.appendUInt8(ei.NIL_EXT);
    } else if (65535 >= val.length) {
        var buf = new Buffer(val, encoding);

        this.appendUInt8(ei.STRING_EXT);
        this.appendUInt16BE(buf.length);
        this.appendBuffer(buf);
    } else {
        throw new Error('Cannot encode strings as lists yet');
    }
};

EiEncoder.prototype.encodeAtom = function(val, encoding) {
    'use strict';

    var type = typeof(val);

    encoding = (encoding || 'ascii').toLowerCase();

    if (type !== 'string' && type !== 'number') {
        throw new TypeError('Only strings & numbers are accepted');
    } else if (0 === (val = val.toString()).length) {
        throw new Error('Length must be greater than zero');
    } else {
        var buf = new Buffer(val, encoding);

        if (256 < val.length) {
            if ('ascii' === encoding) {
                this.appendUInt8(ei.ATOM_EXT);
            } else {
                this.appendUInt8(ei.ATOM_UTF8_EXT);
            }

            this.appendUInt16BE(buf.length);
        } else {
            if ('ascii' === encoding) {
                this.appendUInt8(ei.SMALL_ATOM_EXT);
            } else {
                this.appendUInt8(ei.SMALL_ATOM_UTF8_EXT);
            }

            this.appendUInt8(buf.length);
        }

        this.appendBuffer(buf);
    }
};

EiEncoder.prototype.encodeBool = function(val) {
    'use strict';

    if (typeof(val) !== 'boolean') {
        throw new TypeError('Only booleans are allowed');
    }

    this.encodeAtom(val.toString());
};

EiEncoder.prototype.encodeBinary = function(val, len) {
    'use strict';

    len = len || val.length;

    if (!Buffer.isBuffer(val)) {
        throw new TypeError('Only Buffers are accepted');
    } else {
        this.appendUInt8(ei.BINARY_EXT);
        this.appendUInt32BE(len);
        this.appendBuffer(val, len);
    }
};

EiEncoder.prototype.encodeListHeader = function(cnt) {
    'use strict';

    if (typeof(cnt) !== 'number') {
        throw new TypeError('Only numbers are accepted');
    } else if (0 > cnt) {
        throw new TypeError('Arity must be >= 0');
    } else if (0 === cnt) {
        this.appendUInt8(ei.NIL_EXT);
    } else {
        this.appendUInt8(ei.LIST_EXT);
        this.appendUInt32BE(cnt);
    }
};

EiEncoder.prototype.encodeTupleHeader = function(cnt) {
    'use strict';

    if (typeof(cnt) !== 'number') {
        throw new TypeError('Only numbers are accepted');
    } else if (0 > cnt) {
        throw new TypeError('Arity must be >= 0');
    } else if (255 >= cnt) {
        this.appendUInt8(ei.SMALL_TUPLE_EXT);
        this.appendUInt8(cnt);
    } else {
        this.appendUInt8(ei.LARGE_TUPLE_EXT);
        this.appendUInt32BE(cnt);
    }
};

function appender(encoder, len) {
    'use strict';

    return function(data) {
        this._extend(len);
        encoder.call(this.buf, data, (this.index - this.offset), false);
        this.index += len;
        return true;
    };
}

EiEncoder.prototype.appendUInt8 = appender(Buffer.prototype.writeUInt8, 1);
EiEncoder.prototype.appendUInt16BE = appender(Buffer.prototype.writeUInt16BE, 2);
EiEncoder.prototype.appendUInt32BE = appender(Buffer.prototype.writeUInt32BE, 4);
EiEncoder.prototype.appendInt8 = appender(Buffer.prototype.writeInt8, 1);
EiEncoder.prototype.appendInt16BE = appender(Buffer.prototype.writeInt16BE, 2);
EiEncoder.prototype.appendInt32BE = appender(Buffer.prototype.writeInt32BE, 4);
EiEncoder.prototype.appendFloatBE = appender(Buffer.prototype.writeFloatBE, 4);
EiEncoder.prototype.appendDoubleBE = appender(Buffer.prototype.writeDoubleBE, 8);
EiEncoder.prototype.appendBuffer = function(val, len) {
    if (!Buffer.isBuffer(val)) {
        throw new Type('Only Buffers are accepted');
    }

    len = len || val.length;

    if (0 < len) {
        this._extend(len);
        val.copy(this.buf, (this.index - this.offset), 0, len);
        this.index += len;
    }
}

module.exports = function(options) {
    'use strict';

    return new EiEncoder(options || {});
};
<|endoftext|>