/*jslint node:true, browser:true, nomen:true */
'use strict';
/*
    Copyright 2015 Enigma Marketing Services Limited

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

var fs = require('fs'),
    XLSX = require('xlsx'),
    Q = require('q');

/*
    converts between json and xlsx
        - only the first worksheet gets converted
        - cells are merged as a visual convenience but they hold no special meaning
*/

function colLetterToNumber(letters) {
    var number = 0,
        i = 0;

    for (i = 0; i < letters.length; i += 1) {
        //number += (letters.length - i - 1) * (letters.charCodeAt(i) - 64);
        number += Math.pow(26, i) * (letters.charCodeAt(letters.length - i - 1) - 64);
    }
    return number;
}

function colNumberToLetter(num) {
    if (num <= 0) {
        throw new Error('Invalid number');
    }

    if (num < 27) {
        return String.fromCharCode(64 + num);
    }
    return colNumberToLetter((num - 1) / 26) + colNumberToLetter((num - 1) % 26 + 1);
}

function getLetters(value) {
    return value.replace(/[0-9]+/, '');
}

function getNumbers(value) {
    return +value.replace(/[A-Z]+/, '');
}

function sortCells(firstSheet) {
    return Object.keys(firstSheet).sort(function (a, b) {
        var aLetters = getLetters(a),
            bLetters = getLetters(b),
            aNumbers,
            bNumbers;

        // number part is only relevant if the letters are equal
        if (aLetters === bLetters) {
            aNumbers = getNumbers(a);
            bNumbers = getNumbers(b);
            return aNumbers - bNumbers;
        }

        return colLetterToNumber(aLetters) - colLetterToNumber(bLetters);
    });
}

function datenum(v, date1904) {
    var epoch;

    if (date1904) {
        v += 1462;
    }
    epoch = Date.parse(v);
    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}

function convertArrayToLiteralObject(arr) {
    var obj = {},
        i = 0;

    for (i = 0; i < arr.length; i += 1) {
        obj[i] = (arr[i] === undefined ? 'NULL' : arr[i]);
    }

    return obj;
}

function convertNestedArrayToLiteralObject(obj) {
    if (typeof obj === 'object' && obj.length) {
        obj = convertArrayToLiteralObject(obj);
    }
    Object.keys(obj).forEach(function (key) {
        if (typeof obj[key] === 'object') {
            obj[key] = convertNestedArrayToLiteralObject(obj[key]);
        }
    });
    return obj;
}
/*
 * Workbook management
 */
function Workbook() {
    if (!(this instanceof Workbook)) {
        return new Workbook();
    }
    this.SheetNames = [];
    this.Sheets = {};
}

Workbook.prototype.addWorkSheet = function (name, obj) {
    var self = this,
        ws = {},
        range = {
            s: {
                c: 0,
                r: 0
            },
            e: {
                c: 0,
                r: 0
            }
        },
        merges = [],
        dimensions;

    self.SheetNames.push(name);
    self.Sheets[name] = ws;

    function addCell(col, row, val) {
        var cell = {
                v: val
            },
            cell_ref;

        if (cell.v === null) {
            return cell;
        }

        cell_ref = XLSX.utils.encode_cell({
            c: col,
            r: row
        });

        if (typeof cell.v === 'number') {
            cell.t = 'n';
        } else if (typeof cell.v === 'boolean') {
            cell.t = 'b';
        } else if (cell.v instanceof Date) {
            cell.t = 'n';
            cell.z = XLSX.SSF._table[14];
            cell.v = datenum(cell.v);
        } else {
            cell.t = 's';
        }

        ws[cell_ref] = cell;
    }

    function parse(obj, c, r) {
        var col = +c || 0,
            row = +r || 0,
            objKeys = Object.keys(obj),
            totalDimensions = {
                numCols: 0, //min columns for key/value assuming the obj isn't invalid
                numRows: 0
            };

        objKeys.forEach(function (key) {
            var val = obj[key],
                dim,
                isObject = (typeof val === 'object'),
                isDate = (val instanceof Date),
                isBSON = (typeof val === 'object' && val._bsontype); // ObjectId

            addCell(col, row + totalDimensions.numRows, key);

            if (isObject && !isDate && !isBSON) {
                dim = parse(val, col + 1, row + totalDimensions.numRows);

                if (dim.numRows > 1) {
                    merges.push({
                        s: {
                            c: col,
                            r: row + totalDimensions.numRows
                        },
                        e: {
                            c: col,
                            r: row + totalDimensions.numRows + dim.numRows - 1
                        }
                    });
                }

                if (dim.numCols >= totalDimensions.numCols) {
                    totalDimensions.numCols = dim.numCols + 1;
                }
                totalDimensions.numRows += dim.numRows;
            } else {
                addCell(col + 1, row + totalDimensions.numRows, val);
                if (totalDimensions.numCols < 2) {
                    totalDimensions.numCols = 2;
                }
                totalDimensions.numRows += 1;
            }
        });

        return totalDimensions;
    }

    // we need to convert arrays to literal objects so
    // the exported excel file shows the array indexes as
    // numbers in their own columns
    dimensions = parse(convertNestedArrayToLiteralObject(obj));
    range.e.c = dimensions.numCols - 1;
    range.e.r = dimensions.numRows - 1;

    ws['!ref'] = XLSX.utils.encode_range(range);

    if (merges.length) {
        ws['!merges'] = merges;
    }

    return ws;
};

/*
 * JSON to XSLX converted
 * and back...
 */
var Obj = function () {
    var self = this;
    return self;
};

Obj.prototype.workbook = null;
Obj.prototype.json = null;
Obj.prototype.xlsx = null;

Obj.prototype.readXlsxFile = function (filePath) {
    var self = this,
        deferred = Q.defer();

    fs.readFile(filePath, function (err, data) {
        if (err) {
            deferred.reject(err);
        }

        self.workbook = XLSX.read(data, {
            type: "binary"
        });
        deferred.resolve(self);
    });

    return deferred.promise;
};

Obj.prototype.saveXlsxFile = function (filename) {
    var self = this;
    fs.writeFile(filename, self.xlsx, 'binary', function (err) {
        if (err) {
            throw err;
        }
    });
};

Obj.prototype.convertToJson = function () {
    var self = this,
        worksheets = self.workbook.SheetNames,
        firstSheet = self.workbook.Sheets[worksheets[0]],
        cellsIndex = sortCells(firstSheet);

    function processColumn(obj, columnLetter, columnIndex, maxColumnIndex) {
        var initIndex = cellsIndex.indexOf(columnLetter + columnIndex),
            cellIndex,
            cellLetters,
            cellNumbers,
            data,
            nextColumn,
            nextData,
            afterNextColumn,
            afterNextData,
            nextRowData,
            i;

        if (initIndex === -1) {
            throw new Error('Cell not found ' + columnLetter + ':' + columnIndex);
        }

        for (i = initIndex; i < cellsIndex.length; i += 1) {
            cellIndex = cellsIndex[i];
            cellLetters = getLetters(cellIndex);
            cellNumbers = getNumbers(cellIndex);

            if (columnLetter !== cellLetters) {
                break;
            }

            if (maxColumnIndex && cellNumbers >= maxColumnIndex) {
                break;
            }

            data = firstSheet[cellIndex];

            // create an object if none provided.
            // for number keys, it's an array, otherwise and object
            obj = obj || (/^[0-9]+$/.test(data.v) ? [] : {});

            nextColumn = colNumberToLetter(colLetterToNumber(columnLetter) + 1);
            nextData = firstSheet[nextColumn + cellNumbers];

            afterNextColumn = colNumberToLetter(colLetterToNumber(columnLetter) + 2);
            afterNextData = firstSheet[afterNextColumn + cellNumbers];

            if (!nextData) {
                throw new Error('Undefined cell value');
            }

            if (obj[data.v] !== undefined) {
                throw new Error('Duplicated cell value');
            }

            if (afterNextData) {
                // if the key is a number it's an array, otherwise
                // it's an object
                obj[data.v] = (/^[0-9]+$/.test(nextData.v) ? [] : {});

                nextRowData = cellsIndex[i + 1];
                processColumn(obj[data.v], nextColumn, cellNumbers, (getLetters(nextRowData) === columnLetter ? getNumbers(nextRowData) : null));
            } else {
                obj[data.v] = nextData.v;
            }
        }

        return obj;
    }

    self.json = processColumn(null, 'A', 1);

    return self;
};

Obj.prototype.convertToXlsx = function () {
    var self = this,
        wb = new Workbook(),
        opts;
    // export workbook
    wb.addWorkSheet('Exported Data', self.json);
    self.workbook = wb;
    // export xlsx binary object
    opts = {
        bookType: 'xlsx',
        bookSST: false,
        type: 'binary'
    };
    self.xlsx = XLSX.write(wb, opts);

    return self;
};

module.exports = Obj;


/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

/** @module main */

var TaskPlanner            = require('taskplanner');
var _                      = require('lodash');
var assert                 = require('assert');
var xtend                  = require('xtend');
var linkFilter             = require('./lib/linkUnlinkFilter');
var generateCommands       = require('./lib/generateCommands');
var generateConfigureTasks = require('./lib/generateConfigureTasks');
var generateDetachTasks    = require('./lib/generateDetachTasks');

/**
 * Defaults for the planner() function.
 *
 * See the code code for the actual values
 *
 */
var defaults               = {
  mode: 'quick',
  noLinkUnlinkRemove: false
};

/**
 * Creates a new planner
 *
 * @param {object} origin - The origin system definition
 * @param {object} dest - The destination system definition
 * @param {object} opts - The options
 */
function planner(origin, dest, opts)  {

  var tasks   = new TaskPlanner();
  var cmds    = generateCommands(origin, dest);
  var state   = _.cloneDeep(origin);
  var result;

  opts = xtend(defaults, opts);

  assert(opts.mode === 'quick' || opts.mode === 'safe', 'unknown mode');

  tasks.addTask({ cmd: 'nop' }, {});
  generateDetachTasks(tasks, origin, opts);
  generateDetachTasks(tasks, dest, opts); // needed because of safe mode
  generateConfigureTasks(tasks, origin, dest, opts);

  _.forIn(state.topology.containers, function(container) {
    container.running = true;
    container.started = true;
    container.added = true;
  });

  _.forIn(dest.topology.containers, function(container) {
    var containers = state.topology.containers;

    if (!containers[container.id]) {
      containers[container.id] = {
        id: container.id,
        containedBy: container.containedBy,
        running: false,
        started: false,
        added: false
      };
    }
  });

  result = cmds.reduce(function(acc, cmd) {
    var plan = tasks.plan(state, cmd);
    if (!plan) {
      throw new Error('unable to generate ' + cmd.cmd + ' for id ' + cmd.id);
    }
    return acc.concat(plan);
  }, []).filter(function(cmd) {
    return cmd && cmd.cmd !== 'nop';
  });

  if (!opts.noLinkUnlinkRemove) {
    result = linkFilter(result);
  }

  return result;
}

module.exports = planner;


"use strict";

const del = require("del");
const {readFile, writeFile} = require("./files");

let maybeCompatible = true;

// Location of the module
const fileLoc = "node_modules/stylelint/";

// Files to delete; to ensure browserify doesn't bundle them
const remove = [
  "bin",
  "docs",
  "*.md",
  "lib/**/__tests__",
  "lib/rules/**/__tests__",
  "lib/testUtils",
  "lib/utils/isAutoprefixable.js",
  "lib/rules/at-rule-no-vendor-prefix",
  "lib/rules/media-feature-name-no-vendor-prefix",
  "lib/rules/property-no-vendor-prefix",
  "lib/rules/selector-no-vendor-prefix",
  "lib/rules/value-no-vendor-prefix",
  "lib/formatters/needlessDisablesStringFormatter.js",
  "lib/formatters/stringFormatter.js",
  "lib/formatters/verboseFormatter.js"
];

// Specific file modifications
// Remove use of "fs", "path" and
// "autoprefixer" - which includes prefixes downloaded from caniuse
const modify = {
  "lib/formatters/index.js": file => {
    return replaceBlocks(file, [
      [
        // Replace https://github.com/stylelint/stylelint/blob/master/lib/formatters/index.js#L5-L6
        // with empty functions
        /(string: require\(\"\.\/stringFormatter"\),\s*verbose: require\("\.\/verboseFormatter"\))/,
        "  string: () => {},\n  verbose: () => {}"
      ]
    ]);
  },
  "lib/index.js": file => {
    return commentOut(file, [
      "const createRuleTester =",
      "api.createRuleTester ="
    ]);
  },
  "lib/rules/index.js": file => {
    return commentOut(file, [
      "\"at-rule-no-vendor-prefix\"",
      "\"media-feature-name-no-vendor-prefix\"",
      "\"property-no-vendor-prefix\"",
      "\"selector-no-vendor-prefix\"",
      "\"value-no-vendor-prefix\""
    ]);
  },
  "lib/standalone.js": file => {
    file = commentOut(file, [
      "const fs = require(\"fs\");",
      "const path = require(\"path\");",
      "ignoreText = fs.",
      "if (readError.code !=="
    ]);
    return replaceBlocks(
      file,
      [
        [
          // Replace https://github.com/stylelint/stylelint/blob/master/lib/standalone.js#L60-L62
          // without using path
          /(const absoluteIgnoreFilePath = path\.isAbsolute\(ignoreFilePath\)\s*\? ignoreFilePath\s*: path\.resolve\(process.cwd\(\), ignoreFilePath\);)/,
          "  const absoluteIgnoreFilePath = ignoreFilePath;"
        ], [
          // Replace https://github.com/stylelint/stylelint/blob/master/lib/standalone.js#L110-L113
          // without using path
          /(const absoluteCodeFilename =\s*codeFilename \!== undefined && \!path\.isAbsolute\(codeFilename\)\s*\? path\.join\(process\.cwd\(\), codeFilename\)\s*: codeFilename;)/,
          "    const absoluteCodeFilename = codeFilename;"
        ], [
          // Replace https://github.com/stylelint/stylelint/blob/master/lib/standalone.js#L127-L133
          // immediately resolve
          /(fs\.stat\(absoluteCodeFilename, err => {\s*if \(err\) {\s*reject\(\);\s*} else {\s*resolve\(\);\s*}\s*}\);)/,
          "          resolve();"
        ], [
          // Replace https://github.com/stylelint/stylelint/blob/master/lib/standalone.js#L178-L253
          // return empty string
          /(if \(useCache\) \{\s*const stylelintVersion =[\s\S]+function prepareReturnValue)/,
          "  return \"\";\n\n  function prepareReturnValue"
        ]
      ]
    );
  },
  "package.json": file => {
    return file
      .replace(/"autoprefixer": ".+",/, "")
      .replace(/"chalk": ".+",/, "");
  }
};

function commentOut(file, lines) {
  lines.forEach(line => {
    const index = file.indexOf(line);
    if (index > -1) {
      file = file
        .replace(line, `// ${line}`)
        // Don't let these accumulate
        .replace("// //", "//");
    } else {
      maybeCompatible = false;
      console.log(`*** Error: Could not comment out "${line}"`);
    }
  });
  return file;
}

// Non-destructive replacement. The block is commented out by adding an
// opening /* and closing */. It does not account for internal block comments!
function replaceBlocks(file, blocks) {
  blocks.forEach(block => {
    const index = file.search(block[0]);
    if (index > -1) {
      // Don't comment out blocks that have already been processed
      if (file.substring(index - 3, index) !== "/* ") {
        file = file.replace(block[0], "/* $1 */\n" + block[1]);
      }
    } else {
      maybeCompatible = false;
      console.log(`*** Error: RegExp ${block[0].toString()} did not match anything`);
    }
  });
  return file;
}

Promise.all(remove.map(file => del(fileLoc + file)))
  .then(
    Promise.all(
      Object.keys(modify).map(file => {
        readFile(fileLoc + file)
          .then(data => modify[file](data))
          .then(data => writeFile(fileLoc + file, data));
      })
    )
  )


/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

/** @module lib/linkUnlinkFilter */

/**
 * Removes consequent link/unlink (or unlink/link) commands with the same id
 *
 * @param {array} list - the list of commands
 */
function linkUnlinkFilter(list) {
  var skipNext;
  var needsFiltering = true;

  function doFilter(cmd, i, cmds) {
    if (skipNext) {
      skipNext = false;
      return false;
    }

    if (!cmds[i + 1]) {
      return true;
    }

    if (cmds[i + 1].id !== cmd.id) {
      return true;
    }

    var unlinkLink = (cmd.cmd === 'unlink' && cmds[i + 1].cmd === 'link');
    var linkUnlink = (cmd.cmd === 'link' && cmds[i + 1].cmd === 'unlink');

    if (linkUnlink || unlinkLink) {
      needsFiltering = true;
      skipNext = true;
      return false;
    }

    return true;
  }

  while (needsFiltering) {
    needsFiltering = false;
    skipNext = false;

    list = list.filter(doFilter);
  }

  return list;
}

module.exports = linkUnlinkFilter;


var five = require('johnny-five'),
    pixel = require('node-pixel'),
    Promise = require('bluebird'),
    pattern = require('./patterns/led-matrix.js'),
    messagingTexts = require('./texts/messaging.js'),
    board = new five.Board();

var interval = null;

module.exports = {

    /**
     * Instantiates a new pixel strip, which is the collection
     * of LEDs. Resolves this strip within a Promise.
     * See: https://github.com/ajfisher/node-pixel#strip
     * @returns {Promise} resolves with the pixel strip instance
     */
    getStripAsync: function() {
        return new Promise(function(resolve, reject) {
            board.on('ready', function() {
                var strip = new pixel.Strip({
                    data: 6,
                    length: 12,
                    board: this,
                    controller: "FIRMATA"
                });
                strip.on('ready', function() {
                    resolve(strip);
                });
            });
        });
    },

    /**
     * Set the strip to a loading pattern.
     * Triggered when the tests have started.
     */
    setStripToLoading: function(strip) {

        if (!strip) {
            console.log(messagingTexts.noStrip);
        }

        if (!(strip instanceof pixel.Strip)) {
            console.log(messagingTexts.wrongStrip);
        }

        pattern.reset(strip, interval);
        interval = pattern.domino(strip, 'white');
    },

    /**
     * Flash the strip with green lights,
     * indicating tests were successful.
     */
    setStripToSuccessful: function(strip) {

        if (!strip) {
            console.log(messagingTexts.noStrip);
        }

        if (!(strip instanceof pixel.Strip)) {
            console.log(messagingTexts.wrongStrip);
        }

        pattern.reset(strip, interval);
        setTimeout(function() {
            pattern.flash(strip, 'green', 2);
        }, 10);
    },

    /**
     * Flash the strip with red lights,
     * indicating there are failed tests.
     */
    setStripToFailed: function(strip) {

        if (!strip) {
            console.log(messagingTexts.noStrip);
        }

        if (!(strip instanceof pixel.Strip)) {
            console.log(messagingTexts.wrongStrip);
        }

        pattern.reset(strip, interval);
        setTimeout(function() {
            pattern.flash(strip, 'red', 2);
        }, 10);
    }
};


'use strict';

var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];

var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];

Object.defineProperty(exports, '__esModule', {
    value: true
});

var _Node = require('../Node');

var _Node2 = _interopRequireDefault(_Node);

var _Text = require('../Text');

var _Text2 = _interopRequireDefault(_Text);

var _DOMException = require('../DOMException');

var _DOMException2 = _interopRequireDefault(_DOMException);

/** @function
* @param source
* @param dest */

function cloneArguments(source, dest) {
    for (var _iterator = source.attributes, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) {
        var _ref;

        if (_isArray) {
            if (_i >= _iterator.length) break;
            _ref = _iterator[_i++];
        } else {
            _i = _iterator.next();
            if (_i.done) break;
            _ref = _i.value;
        }

        var attr = _ref;

        dest.setAttribute(attr.name, attr.value);
    }
}

/** @function
* @param element
* @param deep */function cloneElementNode(element, deep) {
    var clone = element.ownerDocument.createElement(element.nodeName);
    clone._ownerDocument = element._ownerDocument;
    clone._parentNode = null;
    cloneArguments(element, clone);

    if (deep) {
        element.childNodes.forEach(function (child) {
            return clone.appendChild(cloneAnyNode(child, deep));
        });
    }

    return clone;
}

/** @function
* @param fragment
* @param deep */function cloneDocumentFragment(fragment, deep) {
    var clone = fragment.ownerDocument.createDocumentFragment();

    if (deep) {
        fragment.childNodes.forEach(function (child) {
            return clone.appendChild(cloneAnyNode(child, deep));
        });
    }

    return clone;
}

/** @function
* @param element */function cloneTextNode(element) {
    return new _Text2['default'](element.value);
}

/**
 * Function return copy of element and if request deep copy of element
 *
 * @param {ParentNode|Node} element
 * @param {boolean} deep
 * @ignore

* @function
* @param element
* @param deep */
function cloneAnyNode(element, deep) {
    switch (element.nodeType) {
        case _Node2['default'].DOCUMENT_FRAGMENT_NODE:
            return cloneDocumentFragment(element, deep);
        case _Node2['default'].ELEMENT_NODE:
            return cloneElementNode(element, deep);
        case _Node2['default'].TEXT_NODE:
            return cloneTextNode(element);
        default:
            throw new _DOMException2['default'](_DOMException2['default'].DATA_CLONE_ERR);
    }
}

exports.cloneAnyNode = cloneAnyNode;
exports.cloneElementNode = cloneElementNode;
exports.cloneTextNode = cloneTextNode;
//# sourceMappingURL=cloneNodeHelper.js.map


/**
 * Character Processor for the CellScript Tokenizer.
 */

/// includes ///
var fs   = require("fs"),
    path = require("path");

/// private variables ///
var processors = [],
    processorIndex = [];

/// private methods ///
function tryInit() {
    if(!processors.length) {
        var processorsPath = path.resolve(__dirname, "processor");
        var files = fs.readdirSync(processorsPath);
        for (var i = 0; i < files.length; ++i) {
            var processor = require(path.resolve(processorsPath, files[i]));
            //processor.name = processor.name || files[i];
            if(processor.priority) processors.unshift(processor);
            else processors.push(processor);
            processorIndex[processor.name] = processor;
        }
    }
}

function includeTokens(tokenDefinitions, log) {
    if(tokenDefinitions) {
        for(var tokenDefinition in tokenDefinitions) {
            if(tokenDefinitions.hasOwnProperty(tokenDefinition)) {
                var processor = processorIndex[tokenDefinition];
                var token = tokenDefinitions[tokenDefinition];

                if(typeof token.process == "function") {
                    if(processor && !token.forceOverride) throw "A token parser with this name is already included: " + tokenDefinition;
                    if(token.priority) processors.unshift(token);
                    else processors.push(token);
                    processorIndex[tokenDefinition] = token;
                    if(processor) {
                        processors.splice(processors.indexOf(processor), 1);
                        if(log) console.log("[EXTENSION] Overridden token: " + tokenDefinition);
                    }
                    else {
                        if(log){
                            if(token.priority) console.log("[EXTENSION] Loaded new token with priority: " + tokenDefinition);
                            else console.log("[EXTENSION] Loaded new token: " + tokenDefinition);
                        }
                    }
                }
                else {
                    if(!processor || typeof processor.include != "function") throw "Unsupported token definition type: " + tokenDefinition;
                    processor.include(token);
                    if(log) console.log("[EXTENSION] Configured token: " + tokenDefinition);
                }
            }
        }
    }
}

function end(state) {
    var processor = null;
    for(var i = 0; i < processors.length; ++i) {
        processor = processors[i];
        if(typeof processor.end === "function") processor.end(state);
    }
}

/// public interface ///
module.exports = {

    init: tryInit,

    include: includeTokens,

    process: function(state) {
        var processor = null;
        main: while (true) {
            if (!state.char) {
                end(state);
                state.end(true);
                return;
            }

            for (var i = 0; i < processors.length; ++i) {
                processor = processors[i];

                if (processor.canProcess(state)) {
                    processor.process(state);
                    continue main;
                }
            }

            break;
        }

        state.error("Invalid character found: <" + state.char + ">");
    }

};


import Node from '../Node';
import Text from '../Text';
import DOMException from '../DOMException';

function cloneArguments(source, dest) {
    for (const attr of source.attributes) {
        dest.setAttribute(attr.name, attr.value);
    }
}

function cloneElementNode(element, deep) {
    const clone = element.ownerDocument.createElement(element.nodeName);
    clone._ownerDocument = element._ownerDocument;
    clone._parentNode = null;
    cloneArguments(element, clone);

    if (deep) {
        element.childNodes.forEach(child => clone.appendChild(cloneAnyNode(child, deep)));
    }

    return clone;
}

function cloneDocumentFragment(fragment, deep) {
    const clone = fragment.ownerDocument.createDocumentFragment();

    if (deep) {
        fragment.childNodes.forEach(child => clone.appendChild(cloneAnyNode(child, deep)));
    }

    return clone;
}

function cloneTextNode(element) {
    return new Text(element.value);
}

/**
 * Function return copy of element and if request deep copy of element
 *
 * @param {ParentNode|Node} element
 * @param {boolean} deep
 * @ignore
 */
function cloneAnyNode(element, deep) {
    switch (element.nodeType) {
        case Node.DOCUMENT_FRAGMENT_NODE:
            return cloneDocumentFragment(element, deep);
        case Node.ELEMENT_NODE:
            return cloneElementNode(element, deep);
        case Node.TEXT_NODE:
            return cloneTextNode(element);
        default:
            throw new DOMException(DOMException.DATA_CLONE_ERR);
    }
}

export {
    cloneAnyNode,
    cloneElementNode,
    cloneTextNode,
};


import { assert, object, func, string, array, any } from 'joi';
import { InternalLogger, Logger, Store, Bootstrapable } from '@eventific/core';
import chalk from 'chalk';
import { get } from 'node-emoji';

var commandManagerOptionsSchema = object().keys({
    aggregate: func().unknown().keys({
        Name: string().required(),
        Type: string().required(),
        _InstantiateAggregate: func().required()
    }).required(),
    extensions: array().items(any()).optional(),
    providers: array().items(any()).optional(),
    store: func().unknown().keys({
        _CreateStore: func().required()
    }).required(),
    transports: array().min(1).items(func().unknown().keys({
        _CreateTransport: func().required()
    })).required()
});

var __extends = (undefined && undefined.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (undefined && undefined.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [0, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
// tslint:disable-next-line
var pascalCase = require('pascal-case');
/**
 *
 * @param {CommandManagerOptions} options
 * @returns T The decorated class
 * @Annotation
 */
function CommandManager(options) {
    assert(options, commandManagerOptionsSchema);
    return function (Class) {
        return _a = /** @class */ (function (_super) {
                __extends(class_1, _super);
                /* istanbul ignore next */
                function class_1() {
                    var args = [];
                    for (var _i = 0; _i < arguments.length; _i++) {
                        args[_i] = arguments[_i];
                    }
                    var _this = _super.apply(this, args[0].injector.args(Class)) || this;
                    var params = args[0];
                    _this._injector = params.injector;
                    _this._store = params.store;
                    _this._transports = params.transports;
                    _this._aggregate = params.aggregate;
                    _this._logger = _this._injector.get(Logger);
                    return _this;
                }
                class_1._Instantiate = function (parentInjector) {
                    var injector = parentInjector.newChildInjector();
                    var store = options.store._CreateStore(injector);
                    injector.set({ provide: Store, useConstant: store });
                    injector.set({ provide: Logger, useConstant: new InternalLogger(chalk.green(pascalCase('CommandManager'))) });
                    return new this({
                        aggregate: options.aggregate._InstantiateAggregate(injector),
                        injector: injector,
                        store: store,
                        transports: options.transports.map(function (t) { return t._CreateTransport(injector); })
                    });
                };
                class_1.prototype._start = function () {
                    return __awaiter(this, void 0, void 0, function () {
                        var _this = this;
                        var _i, _a, transport;
                        return __generator(this, function (_b) {
                            switch (_b.label) {
                                case 0:
                                    if (!this.onInit) return [3 /*break*/, 2];
                                    return [4 /*yield*/, this.onInit()];
                                case 1:
                                    _b.sent();
                                    _b.label = 2;
                                case 2: return [4 /*yield*/, this._store.start()];
                                case 3:
                                    _b.sent();
                                    _i = 0, _a = this._transports;
                                    _b.label = 4;
                                case 4:
                                    if (!(_i < _a.length)) return [3 /*break*/, 7];
                                    transport = _a[_i];
                                    return [4 /*yield*/, transport.start()];
                                case 5:
                                    _b.sent();
                                    if (transport.onCommand) {
                                        transport.onCommand(this._aggregate.name, function (cmd) { return __awaiter(_this, void 0, void 0, function () {
                                            return __generator(this, function (_a) {
                                                switch (_a.label) {
                                                    case 0: return [4 /*yield*/, this._handleCommand(cmd)];
                                                    case 1:
                                                        _a.sent();
                                                        return [2 /*return*/];
                                                }
                                            });
                                        }); });
                                    }
                                    _b.label = 6;
                                case 6:
                                    _i++;
                                    return [3 /*break*/, 4];
                                case 7: return [4 /*yield*/, this._aggregate._start()];
                                case 8:
                                    _b.sent();
                                    this._logger.info("All setup and ready " + get('sparkles'));
                                    return [2 /*return*/];
                            }
                        });
                    });
                };
                class_1.prototype._handleCommand = function (commandMessage) {
                    return __awaiter(this, void 0, void 0, function () {
                        return __generator(this, function (_a) {
                            switch (_a.label) {
                                case 0: return [4 /*yield*/, this._aggregate._handleCommand(commandMessage)];
                                case 1:
                                    _a.sent();
                                    return [2 /*return*/];
                            }
                        });
                    });
                };
                return class_1;
            }(Class)), _a.Type = 'CommandManager', _a;
        var _a;
    };
}

var __extends$1 = (undefined && undefined.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var ICommandManager = /** @class */ (function (_super) {
    __extends$1(ICommandManager, _super);
    function ICommandManager() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    return ICommandManager;
}(Bootstrapable));

export { CommandManager, ICommandManager };


'use strict';

var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];

Object.defineProperty(exports, '__esModule', {
    value: true
});
exports.querySelector = querySelector;
exports.querySelectorAll = querySelectorAll;

var _cssSelectorParser = require('css-selector-parser');

var cssParser = new _cssSelectorParser.CssSelectorParser();
cssParser.registerAttrEqualityMods('^', '$', '*', '~', '|');
cssParser.registerNestingOperators('>', '+', '~');

/**
 * querySelectorHelper interface provides simple processing
 * of Element.querySelector method.
 */

/**
 *
 * @param {Element} element
 * @param {string} query
 * @return {Element|null}
 */
/** @function
* @param element
* @param query */
function querySelector(element, query) {
    var rules = cssParser.parse(query);

    var iterator = processElementDeep(element, rules);
    var next = iterator.next();

    if (next && next.value) {
        return next.value;
    }

    return null;
}

/**
  *
  * @param {Element} element
  * @param {string} query
  * @return {Element[]}
  */
/** @function
* @param element
* @param query */
function querySelectorAll(element, query) {
    var rules = cssParser.parse(query);

    var result = [];
    for (var _iterator = processElementDeep(element, rules), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) {
        var _ref;

        if (_isArray) {
            if (_i >= _iterator.length) break;
            _ref = _iterator[_i++];
        } else {
            _i = _iterator.next();
            if (_i.done) break;
            _ref = _i.value;
        }

        var _element = _ref;

        result.push(_element);
    }

    return result;
}

/**
 * Function processes one element using current rule
 *
 * @param {HTMLElement} element - element to check
 * @param {*} rules - current rules to check against element
 * @return {boolean}

* @function
* @param element
* @param rules */
function* processElement(element, rules) {
    for (var _iterator2 = element.children, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _getIterator(_iterator2);;) {
        var _ref2;

        if (_isArray2) {
            if (_i2 >= _iterator2.length) break;
            _ref2 = _iterator2[_i2++];
        } else {
            _i2 = _iterator2.next();
            if (_i2.done) break;
            _ref2 = _i2.value;
        }

        var child = _ref2;

        yield* processRules(child, rules);
    }
}

/**
 * Function processes one element using current rule
 *
 * @param {HTMLElement} element - element to check
 * @param {*} rules - current rules to check against element
 * @return {boolean}

* @function
* @param element
* @param rules */
function* processElementDeep(element, rules) {
    for (var _iterator3 = element.children, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _getIterator(_iterator3);;) {
        var _ref3;

        if (_isArray3) {
            if (_i3 >= _iterator3.length) break;
            _ref3 = _iterator3[_i3++];
        } else {
            _i3 = _iterator3.next();
            if (_i3.done) break;
            _ref3 = _i3.value;
        }

        var child = _ref3;

        yield* processRules(child, rules);
        yield* processElementDeep(child, rules);
    }
}

/**
 * @param {Element} element
 * @param {Object} rules
 * @return {boolean}

* @function
* @param element
* @param rules */
function processRules(element, rules) {
    if (rules.type === 'selectors') {
        return processSelectors(element, rules.selectors);
    } else if (rules.type === 'ruleSet') {
        return processRule(element, rules.rule);
    }
}

/**
 * @param {Element} element
 * @param {Object} rule
 * @return {boolean}

* @function
* @param element
* @param rule */
function processRule(element, rule) {
    return processSelectors(element, [{ rule: rule }]);
}

/**
 * @param {Element} element
 * @param {Object[]} selectors
 * @return {boolean}

* @function
* @param element
* @param selectors */
function* processSelectors(element, selectors) {
    for (var _iterator4 = selectors, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _getIterator(_iterator4);;) {
        var _ref4;

        if (_isArray4) {
            if (_i4 >= _iterator4.length) break;
            _ref4 = _iterator4[_i4++];
        } else {
            _i4 = _iterator4.next();
            if (_i4.done) break;
            _ref4 = _i4.value;
        }

        var selector = _ref4;

        var rule = selector.rule;

        if (!matchRule(element, rule)) {
            continue;
        }

        if (!rule.hasOwnProperty('rule')) {
            yield element;
            return;
        }

        switch (rule.rule.nestingOperator) {
            case '+':
                var nextElementSibling = element.nextElementSibling;
                if (nextElementSibling && matchRule(nextElementSibling, rule.rule)) {
                    yield nextElementSibling;
                }

                break;
            case '~':
                yield* processElement(element.parentNode, { type: 'ruleSet', rule: rule.rule });
                break;
            case '>':
                yield* processElement(element, { type: 'ruleSet', rule: rule.rule });
                break;
            default:
                yield* processElementDeep(element, { type: 'ruleSet', rule: rule.rule });
        }

        return;
    }
}

/** @function
* @param element
* @param rule */function matchRule(element, rule) {
    if (rule.hasOwnProperty('tagName') && element.tagName !== rule.tagName) {
        return false;
    }

    if (rule.hasOwnProperty('id') && element.getAttribute('id') !== rule.id) {
        return false;
    }

    if (rule.hasOwnProperty('classNames')) {
        if (!rule.classNames.every(function (name) {
            return element.classList.contains(name);
        })) {
            return false;
        }
    }

    if (rule.hasOwnProperty('attrs')) {
        if (!rule.attrs.some(function (attr) {
            if (!element.hasAttribute(attr.name)) {
                return false;
            } else if (attr.operator === undefined) {
                return true;
            }

            var value = element.getAttribute(attr.name);

            switch (attr.operator) {
                case '=':
                    return value === attr.value;
                case '^=':
                    return value.startsWith(attr.value);
                case '$=':
                    return value.endsWith(attr.value);
                case '~=':
                    var words = value.split(' ');
                    return words.some(function (word) {
                        return word === attr.value;
                    });
                case '*=':
                    return value.indexOf(attr.value) !== -1;
                case '|=':
                    return value === attr.value || value.startsWith(attr.value + '-');
                default:
                    throw new Error('Unsupported attribute operator ' + attr.operator);
            }
        })) {
            return false;
        }
    }

    return true;
}
//# sourceMappingURL=querySelectorHelper.js.map


"use strict"
var Url = require('url')
var _ = require('underscore')

module.exports = function(ngin) {
  var Model = ngin.NginModel
  var Super = Model.prototype
  var config = ngin.config

  /**
   * Scopes the url to the tournament or flight
   *
   * @param {Object} options
   * @returns {String}
   * @api public
   */

  function scopeUrl(options, inst) {
    options = _.extend(_.clone(options || {}), inst)
    if (!options.venue_id)
      throw new Error('venue_id required to make subvenue api calls')
    return ngin.Venue.urlRoot() + '/' + options.venue_id + '/subvenues'
  }

  /**
   * Subvenue Class
   *
   * @param {Object} attr
   * @param {Object} options
   * @api public
   */

  var Subvenue = Model.extend({

    fetch: function(options, callback) {
      var url = scopeUrl(options, this) + '/' + this.id
      return Super.fetch.call(this, url, options, callback)
    },

    save: function(options, callback) {
      var url = scopeUrl(options, this) + (this.id ? '/' + this.id : '')
      return Super.save.call(this, url, options, callback)
    },

    destroy: function(options, callback) {
      var url = scopeUrl(options, this) + '/' + this.id
      return Super.destroy.call(this, url, options, callback)
    },

    addReservation: function(options, callback) {
      var url = Subvenue.urlRoot() + '/' + this.id + '/reservations'
      return Super.save.call(this, url, options, callback)
    },

    removeReservation: function(options, callback) {
      var url = Subvenue.urlRoot() + '/' + this.id + '/reservations'
      return Super.destroy.call(this, url, options, callback)
    }

  }, {

    urlRoot: function(options) {
      var base = config.urls && config.urls.venues || config.url
      return Url.resolve(base, '/subvenues')
    },

    list: function(options, callback) {
      var opts = _.extend({}, options, options.query)

      if (!opts.venue_id && !opts.tournament_id && !opts.org_id)
        throw new Error('venue_id, tournament_id, or org_id are required to make subvenue api calls')

      var params = _.pick(opts, 'page', 'per_page')
      params.query = _.omit(opts, 'page', 'per_page')
      var url = Subvenue.urlRoot()

      return Model.list.call(Subvenue, url, params, callback)
    }

  })

  return Subvenue

}


var fs = require('fs');
var path = require('path');
var ConfigStore = require('./config-store');
var globalConfig;

//
// global config
//

function getGlobalConfig(){
  if (!globalConfig)
    globalConfig = new ConfigStore('basisjs-tools');

  return globalConfig;
}

//
// project config
//

function fetchConfig(filename){
  var data;

  filename = path.resolve(filename);

  try {
    data = fs.readFileSync(filename, 'utf-8');
  } catch(e) {
    console.error('Config read error: ' + e);
    process.exit(2);
  }

  try {
    data = JSON.parse(data);
  } catch(e) {
    console.error('Config parse error: ' + e);
    process.exit(2);
  }

  return {
    filename: filename,
    path: path.dirname(filename),
    data: data
  };
}

function fetchFromPackageJson(filename) {
  var data = require(filename).basisjsConfig;

  if (!data)
    return;

  return {
    filename: filename,
    path: path.dirname(filename),
    data: data
  };
}

function searchConfig(){
  var currentDir = process.env.PWD || process.cwd(); // use PWD if possible as on *nix process.cwd()
                                                     // returns real path instead of symlink-path;
  var pathParts = path.normalize(currentDir).split(path.sep);

  while (pathParts.length)
  {
    var cfgFile = pathParts.join(path.sep) + path.sep + 'basis.config';
    var packageJsonFile = pathParts.join(path.sep) + path.sep + 'package.json';
    var config;

    if (fs.existsSync(cfgFile))
      config = fetchConfig(cfgFile);
    else
      if (fs.existsSync(packageJsonFile))
        config = fetchFromPackageJson(packageJsonFile);

    if (config)
      return config;

    pathParts.pop();
  }
}

module.exports = function(command){
  command
    .option('-n, --no-config', 'Don\'t use basis.config', { hot: true, beforeInit: true })
    .option('-c, --config-file <filename>', 'Specify path to config filename', { hot: true, beforeInit: true });

  Object.defineProperty(command, 'globalConfig', {
    enumerable: true,
    get: getGlobalConfig
  });

  command.getConfig = function(options){
    if (!options)
      options = this.values || {};

    if ('config' in options == false || options.config)
    {
      var config = options.configFile
        ? fetchConfig(options.configFile)
        : searchConfig();

      if (config)
        return config;
    }
  };

  return command;
};


// NPM dependencies
// ----------------
// [type-of-is](https://github.com/stephenhandley/type-of-is)
var Type = require('type-of-is');

// Local dependencies
// ------------------
// [Route](./Route.html)
// [NotFound](./NotFound.html)
var Route    = require('./Route');
var NotFound = require('./NotFound');

// Router
// ======
// constructor
// -----------
// create a router
// ### optional arguments
// **routes**  : routes to add
function Router (routes) {
  this._routes         = [];
  this._not_found      = null;
  this._not_found_path = '/404';

  if (arguments.length === 1) {
    this.addRoutes(routes);
  }
}

// Regular expression for matching HTTP verb prefix in routes
var _METHOD_PREFIX_REGEXP = /^(GET|HEAD|POST|UPDATE|DELETE) /;

// addRoute
// --------
// Add a single route
Router.prototype.addRoute = function addRoute (route_name, route_pattern) {
  if (arguments.length !== 2) {
    throw new TypeError('addRoute expects two arguments: (route_name, route_pattern)');
  }

  var routes = {};
  routes[route_name] = route_pattern;
  this.addRoutes(routes);
};

// addRoutes
// ---------
// Add multiple routes. Routes can be specified as plain string
// or as object with pattern and method attributes
Router.prototype.addRoutes = function addRoutes (routes) {
  if (arguments.length !== 1) {
    throw new TypeError('addRoutes expects argument with { routename1 : routepattern 1, ...}');
  }

  for (var route_name in routes) {
    if (!routes.hasOwnProperty(route_name)) {
      continue;
    }

    var route = routes[route_name];

    var pattern;
    var method;

    if (Type(route, String)) {
      pattern = route;
      method  = null;

      // check for a HTTP verb prefix
      var method_prefix = pattern.match(_METHOD_PREFIX_REGEXP);
      if (method_prefix) {
        pattern = pattern.slice(method_prefix[0].length).trim();
        method  = method_prefix[1];
      }

    } else if (Type(route, Object) && route.hasOwnProperty('pattern')) {
      pattern = route.pattern;
      method  = ('method' in route) ? route.method : null;

    } else {
      throw new TypeError('Route pattern must be String or Object with pattern property');
    }

    route = new Route({
      pattern : pattern,
      method  : method,
      name    : route_name
    });

    this._routes.push(route);
  }
};

// route
// -----
// Find a route by its name
Router.prototype.route = function route (name) {
  var num_routes = this._routes.length;

  // Look through the routes for a route with matching name
  for (var i = 0;  i < num_routes; i++) {
    var route = this._routes[i];
    if (route.name === name) {
      return route;
    }
  }

  return null;
};

// notFound
// --------
// Optionally register a function to be called when no route
// is matched by handle
Router.prototype.notFound = function notFound (not_found) {
  this._not_found = not_found;
};

// notFoundPath
// -----------
// Optionally set the path that is returned in the [NotFound](./NotFound.html)
// route. This is only set when a route-based match fails,
// for path-based matches, the specified path is used.
Router.prototype.notFoundPath = function notFoundPath (path) {
  this._not_found_path = path;
}

// handle
// ------
// Process incoming request and augment request object with route
//
// This function can be called directly when using basic http, and
// also invoked by [connect](https://github.com/senchalabs/connect) middleware.
Router.prototype.handle = function (req, res, next) {
  req.route = this.match({
    path   : req.url,
    method : req.method
  });

  // call the not found handler if its been set
  if (req.route.notFound() && this._not_found) {
    return this._not_found(req, res, next);
  }

  // Connect looks for a function named "handle" that is expected to
  // call next when done processing. Next is not needed when calling
  // handle directly
  if (!!next) {
    next();
  }
};

// match
// -----
// Checks whether there is a route matching the passed path or route
// data. If there is a match, it returns a [MatchedRoute](./MatchedRoute.html)
// otherwise returns [NotFound](./NotFound.html).
Router.prototype.match = function match (args) {
  for (var i = 0; i < this._routes.length; i++) {
    var route = this._routes[i];
    var matched_route = route.match(args);
    if (matched_route) {
      return matched_route;
    }
  }

  // if a route arg was passed and it didn't match anything then there's
  // no way to construct a path so set it to _not_found_path, which defaults
  // to /404 and can be set via notFoundPath
  var path;
  if ('path' in args) {
    // get just the path of path
    path = '/' + args.path.split('/').slice(3).join('/')
  } else {
    path = this._not_found_path;
  }
  var not_found = new NotFound(path);

  return not_found;
};

module.exports = Router;


var pkg =           require('./package.json');
var async =         require('async');
var fs =            require('fs');
var path =          require('path');
var http =          require('http');
var xmlrpc =        require('homematic-xmlrpc');
var binrpc =        require('binrpc');
var util =          require('util');
var EventEmitter =  require('events').EventEmitter;
var pjson =         require('persist-json')('hmif');
var checkservice =  require('checkservice');

// http://www.eq-3.de/Downloads/eq3/download%20bereich/hm_web_ui_doku/HM_XmlRpc_API.pdf
// http://www.eq-3.de/Downloads/eq3/download%20bereich/hm_web_ui_doku/HMIP_XmlRpc_API_Addendum.pdf

function Hmif(config, status, log) {
    if (!(this instanceof Hmif)) return new Hmif(config, status, log);

    if (!log) {
        log = {};
        log.debug = log.info = log.warn = log.error = log.setLevel = function () {};
    }

    var that = this;
    this.status = status;
    this.status.homematic = {interfaces: {}};
    if (!config) return;

    this.config = config;

    log.info(pkg.name + ' ' + pkg.version + ' starting');

    this._iface = {};
    this._values = {};
    this._paramsetDescriptions = pjson.load('paramsetDescriptions.json') || {};
    this._names = {};

    var xmlrpcServer;
    var binrpcServer;

    function meta(params) {
        var iface = params[0];
        var address = params[1];
        var datapoint = params[2];
        var value = params[3];
        var dev = that._iface[iface].devices[address];
        var ident = paramsetIdent(dev, 'VALUES');
        var desc = that._paramsetDescriptions[ident][datapoint];
        var meta = {

        };
        if (address.indexOf(':') !== -1) {
            meta.channelName = that._names[address];
            meta.deviceName = that._names[address.replace(/:[0-9]+$/, '')];
        } else {
            meta.deviceName = that._names[address.replace(/:[0-9]+$/, '')];
        }
        if (desc.TYPE === 'ENUM') meta.enumValue = desc.VALUE_LIST[value];
        return meta;
    }

    function paramsetIdent(dev, paramset) {
        var ident = '';
        if (dev.PARENT_TYPE) ident = ident + dev.PARENT_TYPE + '/';
        ident = ident + dev.TYPE;
        if (dev.SUBTYPE) ident = ident + '/' + dev.SUBTYPE;
        ident = ident + '/' + dev.VERSION + '/' + paramset;
        return ident;
    }

    function getParamsetDescriptions(iface) {
        var calls = [];
        var requests = [];
        Object.keys(that._iface[iface].devices).forEach(function (address) {
            var dev = that._iface[iface].devices[address];
            dev.PARAMSETS.forEach(function (paramset) {
                var ident = paramsetIdent(dev, paramset);
                if ((!that._paramsetDescriptions[ident]) && (requests.indexOf(ident) === -1)) {
                    requests.push(ident);
                    calls.push(function (cb) {
                        log.debug('getParamsetDescription', ident);
                        that._iface[iface].rpc.methodCall('getParamsetDescription', [dev.ADDRESS, paramset], function (err, res) {
                            if (!err) {
                                that._paramsetDescriptions[ident] = res;
                            } else {
                                log.error(err);
                            }
                            cb();
                        });
                    });
                }
            })
        });
        async.series(calls, function () {
            log.debug('getParamsetDescriptions', iface, 'done');
            pjson.save('paramsetDescriptions.json', that._paramsetDescriptions);
        });
    }

    this.methods = {

        'system.multicall': function multicall(err, params, callback) {
            log.debug('rpc < system.multicall', err, '(' + params[0].length + ')');
            var res = [];
            params[0].forEach(function (c) {
                that.methods.event(null, c.params);
                res.push('');
            });
            log.debug('re  >', null, res);
            callback(null, res);
        },

        'system.listMethods': function listMethods(err, params, callback) {
            log.debug('rpc < system.listMethods', err, params);
            log.debug('re  >', null, JSON.stringify(Object.keys(that.methods)));
            callback(null, Object.keys(that.methods));
        },

        'event': function event(err, params, callback) {
            log.debug('rpc < event', err, params);
            that._iface[params[0]].lastEvent = (new Date()).getTime();
            if (params[1] === 'CENTRAL' && params[2] === 'PONG') return;
            if (!that._values[params[0]][params[1]]) {
                that._values[params[0]][params[1]] = {};
            }
            if (params[3] !== that._values[params[0]][params[1]][params[2]]) {
                log.debug('rpc < change', err, params, that._names[params[1]]);

                //if (params[1].indexOf(':') === -1) {
                    that.emit('change', params, meta(params));
                //} else {
                //    that.emit('change', params, meta(params));
                //}
            }
            that._values[params[0]][params[1]][params[2]] = params[3];
            if (params[1].indexOf(':') === -1) {
                that.emit('rpc', 'event', params, meta(params));
            } else {
                that.emit('rpc', 'event', params, meta(params));
            }
            if (typeof callback === 'function') {
                log.debug('re  >', null, JSON.stringify(''));
                callback(null, '');
            }
        },

        'listDevices': function listDevices(err, params, callback) {
            log.debug('rpc < listDevices', err, params);
            var re = [];

            Object.keys(that._iface[params[0]].devices).forEach(function (d) {
                var dev = that._iface[params[0]].devices[d];
                if (that._iface[params[0]].type === 'hmip') {
                    re.push({'ADDRESS': dev.ADDRESS, 'VERSION': dev.VERSION});
                } else {
                    re.push({'ADDRESS': dev.ADDRESS, 'VERSION': dev.VERSION});
                }
            });

            log.debug('re  >', null, re.length);
            callback(null, re);
        },

        'newDevices': function newDevices(err, params, callback) {
            log.debug('rpc < newDevices', err, params[0], params[1]); //.length);
            that.emit('rpc', 'newDevices', params);

            params[1].forEach(function (dev) {
                that._iface[params[0]].devices[dev.ADDRESS] = dev;
            });

            pjson.save(that._iface[params[0]].host + '-' + that._iface[params[0]].port + '-devices.json', that._iface[params[0]].devices);
            log.debug('re  >', null, JSON.stringify(''));
            callback(null, '');
            getParamsetDescriptions(params[0]);
        },

        'deleteDevices': function deleteDevices(err, params, callback) {
            log.debug('rpc < deleteDevices', err, params[0], params[1].length);
            params[1].forEach(function (dev) {
                delete that._iface[params[0]].devices[dev.ADDRESS];
            });
            pjson.save(that._iface[params[0]].host + '-' + that._iface[params[0]].port + '-devices.json', that._iface[params[0]].devices);
            that.emit('rpc', 'deleteDevices', params);
            log.debug('re  >', null, JSON.stringify(''));
            callback(null, '');
        }

    };

    function getRegaNames() {
        log.debug('rega > reganames.fn');
        that.regaFile('regascripts/reganames.fn', function (err, res) {
            if (!err) {
                that._names = res;
                log.debug('rega < ' + Object.keys(res).length);
            } else {
                log.error(err);
            }
        });
    }

    function createClients(callback) {
        if (!that.config.type) return;
        switch (that.config.type.toLowerCase()) {
            case 'ccu':
            case 'ccu2':

                getRegaNames();
                createBinrpcServer();
                createInterface('rf', that.config.address, 2001, 'rf', 'binrpc', 90000);
                checkservice(that.config.address, 2000, function (err) {
                    if (!err) createInterface('wired', that.config.address, 2000, 'wired', 'binrpc', 90000);
                });
                checkservice(that.config.address, 2010, function (err) {
                    createXmlrpcServer();
                    if (!err) createInterface('hmip', that.config.address, 2010, 'hmip', 'xmlrpc', 0);
                });
                checkservice(that.config.address, 8701, function (err) {
                    if (!err) createInterface('cux', that.config.address, 8701, 'cux', 'binrpc', 0);
                });
                break;

            case 'hmipserver':
            case 'hmip':
                that._names = pjson.load(this.config.address + '-names.json') || {};
                createXmlrpcServer();
                createInterface(that.config.type, that.config.address, that.config.port || 2010, 'hmip', 'xmlrpc', 0);
                break;

            case 'hs485d':
            case 'wired':
                that._names = pjson.load(this.config.address + '-names.json') || {};

                if (that.config.protocol === 'binrpc') {
                    createBinrpcServer();
                } else {
                    createXmlrpcServer();
                }
                createInterface(that.config.type, that.config.address, that.config.port || 2000, 'wired', that.config.protocol, that.config.iface.checkEventTime);
                break;

            case 'rfd':
            case 'rf':
                that._names = pjson.load(this.config.address + '-names.json') || {};

                if (that.config.protocol === 'binrpc') {
                    createBinrpcServer();
                } else {
                    createXmlrpcServer();
                }
                createInterface(that.config.type, that.config.address, that.config.port || 2001, 'rf', that.config.protocol, that.config.iface.checkEventTime);
                break;

            case 'cuxd':
            case 'cux':
                that._names = pjson.load(this.config.address + '-names.json') || {};

                createBinrpcServer();
                createInterface(that.config.type, that.config.address, that.config.port || 8701, 'cux', 'binrpc', that.config.iface.checkEventTime);
                break;

            default:
                log.error('unknown interface type ' + that.config.interfaces[i].type + ' for ')
        }
        setTimeout(callback, 2500);
    }

    function createInterface(id, host, port, type, protocol, checkEventTime) {
        log.debug('creating interface', id, host + ':' + port);
        that._iface[id] = {
            init: false,
            host: host,
            port: port,
            protocol: protocol,
            type: type,
            devices: pjson.load(host + '-' + port + '-devices.json') || {},
            values: {},
            lastEvent: (new Date()).getTime(),
            checkEventTime: (typeof checkEventTime === 'undefined' ? 30000 : checkEventTime)
        };
        that.status.homematic.interfaces[id] = {
            init: false,
            host: host,
            port: port,
            protocol: protocol,
            type: type,
            checkEventTime: (typeof checkEventTime === 'undefined' ? 30000 : checkEventTime)
        };
        that._values[id] = {};

        switch (protocol) {
            case 'binrpc':
            case 'xmlrpc_bin':
                log.debug('binrpc.createClient', host + ':' + port);
                that._iface[id].rpc = binrpc.createClient({
                    host: host,
                    port: port
                });
                break;
            default:
                log.debug('xmlrpc.createClient', host + ':' + port);
                that._iface[id].rpc = xmlrpc.createClient({
                    host: host,
                    port: port,
                    path: '/'
                });
                break;
        }
    }

    function getIfaceInfos(callback) {
        var calls = [];
        Object.keys(that._iface).forEach(function (i) {
            var url = 'http://' + that.config.listenAddress + ':' + that.config.listenPort;

            log.debug('rpc >', i, 'system.listMethods', JSON.stringify([]));
            calls.push(function (cb) {
                that._iface[i].rpc.methodCall('system.listMethods', [], function (err, res) {
                    log.debug('re  <', i, err, JSON.stringify(res));
                    that._iface[i].methods = res;

                    if (res.indexOf('getVersion') !== -1) {
                        log.debug('rpc >', i, 'getVersion', []);
                        that._iface[i].rpc.methodCall('getVersion', [], function (err, res) {
                            log.debug('re  <', i, err, JSON.stringify(res));
                            that._iface[i].version = res;
                            cb();
                        });
                    } else {
                        cb();
                    }
                });
            });
        });
        async.series(calls, callback);
    }

    function subscribe(callback) {
        var calls = [];
        Object.keys(that._iface).forEach(function (i) {
            var url = 'http://' + that.config.listenAddress + ':' + that.config.listenPort;
            var params = [url, i];

            calls.push(function (cb) {
                log.debug('rpc >', i, 'init', params);
                that._iface[i].rpc.methodCall('init', params, function (err, res) {
                    log.debug('re  <', i, err, JSON.stringify(res));
                    if (!err) {
                        log.info('init succesful ' + i + ' (' + that._iface[i].host + ':' + that._iface[i].port + ')');
                        that.status.homematic.interfaces[i].init = true;
                    }
                    checkEvents(i);
                    cb();
                });
            });
        });
        async.series(calls, callback);
    }

    function checkEvents(iface) {
        if (!that._iface[iface].checkEventTime) {
            log.warn('no checkEventTime for ' + iface);
            that.status.homematic.interfaces[iface].checkEventTime = null;
            return;
        }
        that.status.homematic.interfaces[iface].checkEventTime = that._iface[iface].checkEventTime;
        that._iface[iface].checkEventInterval = setInterval(function () {
            var now = (new Date()).getTime();
            var le = that._iface[iface].lastEvent;
            var elapsed = now - le;
            log.debug('checkEvents', now, le, elapsed);

            if (elapsed > (2 * that._iface[iface].checkEventTime)) {
                that.status.homematic.interfaces[iface].init = false;
                var url = 'http://' + that.config.listenAddress + ':' + that.config.listenPort;
                var params = [url, iface];
                log.debug('rpc >', iface, 'init', params);
                that._iface[iface].rpc.methodCall('init', params, function (err, res) {
                    that._iface[iface].lastEvent = (new Date()).getTime();
                    log.debug('re  <', iface, err, JSON.stringify(res));
                });
            } else if ((now - that._iface[iface].lastEvent) > that._iface[iface].checkEventTime) {
                if (that._iface[iface].methods.indexOf('ping') !== -1) {
                    log.debug('rpc >', iface, 'ping', [iface]);
                    that._iface[iface].rpc.methodCall('ping', [iface], function (err, res) {
                        log.debug('re  <', iface, err, JSON.stringify(res));
                    });
                } else {
                    // how to provoke event without ping?
                }
            }
        }, (that._iface[iface].checkEventTime / 2));
    }

    function createXmlrpcServer() {
        xmlrpcServer = xmlrpc.createServer({host: that.config.listenAddress, port: that.config.listenPort});
        log.info('xmlrpc server listening on ' + that.config.listenAddress + ':' + that.config.listenPort);

        xmlrpcServer.on('NotFound', function (method, params) {
            log.warn('rpc < Method ' + method + ' does not exist', params);
        });
        that.status.homematic.xmlrpcServer = true;
        that.status.homematic.xmlrpcServerPort = that.config.listenPort;
        that.status.homematic.xmlrpcServerAddress = that.config.listenAddress;

        createHandlers(xmlrpcServer);

    }

    function createBinrpcServer() {
        binrpcServer = binrpc.createServer({host: that.config.listenAddress, port: that.config.listenPortBin});
        log.info('binrpc server listening on ' + that.config.listenAddress + ':' + that.config.listenPortBin);
        that.status.homematic.binrpcServer = true;
        that.status.homematic.binrpcServerPort = that.config.listenPort;
        that.status.homematic.binrpcServerAddress = that.config.listenAddress;
        createHandlers(binrpcServer);
    }

    function createHandlers(server) {
        Object.keys(that.methods).forEach(function (m) {
            server.on(m, that.methods[m]);
        });
    }

    this.rpc = function rpc(iface, method, params, callback) {
        this._iface[iface].rpc.methodCall(method, params, function (err, res) {
            if (typeof callback === 'function') callback(err, res);
        });
    };

    this.rega = function rega(script, callback) {

        var post_options = {
            host: this.config.address,
            port: '8181',
            path: '/rega.exe',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': script.length
            }
        };
        var post_req = http.request(post_options, function(res) {
            var data = '';
            res.setEncoding('utf8');
            res.on('data', function (chunk) {
                data += chunk.toString();
            });
            res.on('end', function () {
                var pos = data.lastIndexOf("<xml>");
                var stdout = unescape(data.substring(0, pos));
                try {
                    var result = stdout;
                    callback(null, result);
                } catch (e) {
                    callback(e)

                }
            });
        });

        post_req.on('error', function (e) {
            callback(e);
        });

        post_req.write(script);
        post_req.end();

    };

    this.regaFile = function regaFile(file, callback) {
        var that = this;
        fs.readFile(path.join(__dirname, file), 'utf8', function (err, script) {
            if (err) {
                callback(err);
                return false;
            }

            that.rega(script, function (err, res) {
                if (!err) {
                    try {
                        callback(null, JSON.parse(res));
                    } catch (e) {
                        callback(e);
                    }
                } else {
                    callback(err);
                }
            });

        });
    };

    this._findIface = function findIface(address) {
        for (var i in this._iface) {
            for (var a in this._iface[i].devices) {
                if (a === address) return i;
            }
        }
    };

    this.setValue = function rpc(address, datapoint, value, callback) {

        var iface = this._findIface(address);
        if (!iface) {
            callback(new Error('no suitable interface found for address ' + address));
        } else {
            this._iface[iface].rpc.methodCall('setValue', [address, datapoint, value], function (err, res) {
                if (typeof callback === 'function') callback(err, res);
            });
        }
    };

    this.unsubscribe = function unsubscribe(callback) {
        var that = this;
        var calls = [];

        if (that._iface) {

            Object.keys(that._iface).forEach(function (i) {
                var url = 'http://' + that.config.listenAddress + ':' + that.config.listenPort;
                var params = [url, ''];

                log.debug('rpc >', i, 'init', params);
                calls.push(function (cb) {
                    that._iface[i].rpc.methodCall('init', params, function (err, res) {
                        log.debug('re  <', i, err, JSON.stringify(res));
                        cb();
                    });
                });
            });
            async.series(calls, callback);
        } else {
            callback();
        }
    };

    createClients(function () {
        getIfaceInfos(function () {
            log.debug('getIfaces done');
            subscribe(function () {
                log.debug('subscriptions done');
            });
        });
    });

}

util.inherits(Hmif, EventEmitter);

module.exports = Hmif;


/*!
 * Module dependencies
 */

/*!
 * Expose `create()`.
 */
exports = module.exports = create;

/**
 * Creates a new resource.
 *
 * @param {Function} parent The parent this resource should be attached to.
 * @param {String} baseUrl That's needed for sub resources (/api/v2/customers/1/cases).
 * @param {Object} params The params to use for the resource.
 * @param {Function} callback The callback called with the new resource or error.
 * @api public
 */
function create(parent, baseUrl, params, callback) {
  parent.getClient().post(baseUrl, params, function(err, definition, response) {
    if (err) return callback(err);
    callback(null, new this(parent, definition));
  }.bind(this));
}


'use strict';

const fs = require('fs');
const css = require('css');
const hash = require('hash-sum');
const cssWhat = require('css-what');
const stripBom = require('strip-bom');
const compiler = require('vue-template-compiler');

/**
 * @fork https://github.com/fb55/css-what/blob/master/stringify.js
 * @type {Function} stringify
 */
cssWhat.stringify = require('./css-what/stringify');

/**
 * In Browser Environment
 * @type {Boolean} browserEnv
 */
let browserEnv = false;
try {
    document;
    browserEnv = true;
} catch (e) {}

/**
 * Save Loaders of different Language
 * @type {Object} store
 */
let store = {
    style : {
        defaults : '',
        langs : {},
        exports ( style ) {
            document.head.appendChild(style);
        },
    },
    script : {
        defaults : '',
        langs : {},
    },
    template : {
        defaults : '',
        langs : {},
    },
};

/**
 * Set default Loader of different Language
 * @param {String} type | style | script | template |
 * @param {String} lang scss, ts, jade etc.
 */
function set ( type, lang ) {
    store[type].defaults = lang;
}

/**
 * Register Loader of different Language
 * @param {String | String[]} type | style | script | template |
 * @param {String} lang scss, ts, jade etc.
 * @param {Function} handler
 */
function register ( type, lang, handler ) {
    if (Array.isArray(lang)) {
        lang.forEach((v) => store[type].langs[v] = handler);
        return;
    }
    store[type].langs[lang] = handler;
}

/**
 * Require extension vue hook
 * @param {Module} module
 * @param {String} filePath file path
 * @export {Vue} Vue Component after compile
 */
function loader ( module, filePath ) {

    let content = fs.readFileSync(filePath, 'utf8');
    let moduleId = `data-v-${ hash(filePath) }`;

    let vueTemplate = '';
    let vueComponent = compiler.parseComponent(stripBom(content));

    let script = vueComponent.script;
    let styles = vueComponent.styles;
    let template = vueComponent.template;

    let scoped = styles.some(({ attrs }) => attrs.scoped);

    [].concat(script, template, styles).forEach(( tag, index ) => {
        if (tag) {
            let type = tag.type;
            let content = tag.content;
            let lang = tag.attrs.lang || store[type].defaults;
            let handler = store[type].langs[lang];
            if (handler) {
                content = handler(content, filePath, index, module);
            }
            switch (type) {
                case 'style':
                    if (browserEnv) {
                        /**
                         * Only in Browser Environment, append style to head
                         */
                        if (tag.attrs.scoped) {
                            let ast = css.parse(content);
                            ast.stylesheet.rules.forEach(( rule ) => {
                                rule.selectors = rule.selectors.map(( selector ) => {
                                    let [ patterns ] = cssWhat(selector);
                                    let index = patterns.length - 1;
                                    for (; index >= 0; index--) {
                                        let { type } = patterns[index];
                                        if (type !== 'pseudo' && type !== 'pseudo-element') {
                                            break;
                                        }
                                    }
                                    patterns.splice(index + 1, 0, {
                                        value : '',
                                        name : moduleId,
                                        action : 'exists',
                                        type : 'attribute',
                                        ignoreCase : false,
                                    });
                                    return cssWhat.stringify([patterns]);
                                });
                            });
                            content = css.stringify(ast);
                        }
                        let style = document.createElement('style');
                        style.innerHTML = content;
                        store.style.exports.call(module.exports, style, {
                            index,
                            styles,
                            filePath,
                        });
                    }
                    break;
                case 'script':
                    module._compile(content, filePath);
                    break;
                case 'template':
                    if (browserEnv) {
                        /**
                         * Only in Browser Environment, set Attribute for each element
                         */
                        if (scoped) {
                            let div = document.createElement('div');
                            div.innerHTML = content;
                            let root = div.firstElementChild;
                            let walk = function walk ( element, handler ) {
                                handler(element);
                                let children = element.children || [];
                                [].forEach.call(children, ( child ) => {
                                    walk(child, handler);
                                });
                            };
                            walk(root, ( element ) => {
                                element.setAttribute(moduleId, '');
                            });
                            content = div.innerHTML;
                        }
                    }
                    vueTemplate = content;
                    break;
            }
        }
    });

    module.exports.vueComponent = vueComponent;
    module.exports.template = vueTemplate;
}

/**
 * Exports API to expand
 */
['style', 'script', 'template'].forEach(( type ) => {
    loader[type] = {
        set ( lang ) {
            set(type, lang);
            return this;
        },
        register ( lang, handler ) {
            register(type, lang, handler);
            return this;
        },
    };
});

/**
 * Handler of creating styles
 * @param {Function} handler
 */
loader.style.exports = ( handler ) => {
    store.style.exports = handler;
    return this;
};

/**
 * Register Loader as default .vue hook
 */
require.extensions['.vue'] = require.extensions['.vue'] || loader;

/**
 * @export {Function} loader
 */
module.exports = loader;