/**
 * @module rect
 */
'use strict';

import { cursorState } from './cursor';
import { getViewportWidth } from './globals';

const defaultRect = {
    min_w: 122,
    min_h: 27,
    border_w: 2
};

/**
 * Returns true if (left <= p <= right).
 * False otherwise.
 */
function isBetween(p, left, right) {
    if (p >= left && p <= right)
        return true;

    return false;
}

/**
 * Represents a rectangular area that supports translation (i.e. resizing/moving).
 * It's used internally to represent the Win32Dialog's border.
 * @package
 */
export default class DialogRect {
    constructor(x, y, w, h, min_w, min_h, border_w) {
        this.borderWidth = (border_w && border_w >= defaultRect.border_w) ? border_w : defaultRect.border_w;

        this.minWidth = (min_w && min_w > defaultRect.min_w) ? min_w : defaultRect.min_w;
        this.minHeight = (min_h && min_h > defaultRect.min_h) ? min_h : defaultRect.min_h;

        //adapt minWidth and minHeight to the border width
        if (this.borderWidth !== defaultRect.border_w) {
            this.minWidth += 2 * this.borderWidth;
            this.minHeight += 2 * this.borderWidth;
        }

        this.left = x || 1;
        this.top = y || 1;
        this.width = (w && w > this.minWidth) ? w : this.minWidth;
        this.height = (h && h > this.minHeight) ? h : this.minHeight;

        this.right = this.width + this.left;
        this.bottom = this.top + this.height;

        this.cursorOffset = { x: 0, y: 0 };
    }

    /**
     * Updates the rect's dimensions.
     * @package
     */
    update(x, y, w, h) {
        if (x !== undefined)
            this.left = x;

        if (y !== undefined)
            this.top = y;

        if (w !== undefined)
            this.width = w;

        if (h !== undefined)
            this.height = h;

        this.right = this.width + this.left;
        this.bottom = this.top + this.height;
    }

    /**
     * Resizes the rect from the top edge.
     *               /\
     * ┌────────────────────────────┐
     * │             \/             │
     * │                            │
     * │                            │
     * │                            │
     * │                            │
     * └────────────────────────────┘
     * @private
     */
    _resizeTop(cursor_y) {
        if (cursor_y <= 0) {
            cursor_y = 1;
        }

        let new_height = this.height - (cursor_y - this.top),
            new_top = cursor_y;

        if (new_height < this.minHeight) {
            new_top -= this.minHeight - new_height;
            new_height = this.minHeight;
        }

        return [new_height, new_top];
    }

    /**
     * Resizes the rect from the left edge.
     *  ┌────────────────────────────┐
     *  │                            │
     *  │                            │
     * <│>                           │
     *  │                            │
     *  │                            │
     *  └────────────────────────────┘
     * @private
     */
    _resizeLeft(cursor_x) {
        if (cursor_x <= 0) {
            cursor_x = 1;
        }

        let diff_x = cursor_x - this.left,
            new_width = this.width - diff_x,
            new_left = this.left + diff_x;

        if (new_width < this.minWidth) {
            new_left -= this.minWidth - new_width;
            new_width = this.minWidth;
        }

        return [new_width, new_left];
    }

    /**
     * Resizes the rect from the bottom edge.
     * ┌────────────────────────────┐
     * │                            │
     * │                            │
     * │                            │
     * │                            │
     * │             /\             │
     * └────────────────────────────┘
     *               \/
     * @private
     */
    _resizeBottom(cursor_y) {
        let new_height = this.height + cursor_y - this.bottom;

        if (new_height < this.minHeight) {
            new_height = this.minHeight;
        }

        return new_height;
    }

    /**
     * Resizes the rect from the right edge.
     * ┌────────────────────────────┐
     * │                            │
     * │                            │
     * │                           <│>
     * │                            │
     * │                            │
     * └────────────────────────────┘
     * @private
     */
    _resizeRight(cursor_x) {
        let new_width = this.width + cursor_x - this.right;

        if (new_width < this.minWidth) {
            new_width = this.minWidth;
        }

        return new_width;
    }

    /**
     * Resizes the rect.
     * @param {module:cursor/CursorPos} cursor_pos
     * @param {module:cursor/cursorState} resize_type
     * @package
     */
    resizeToCursor(cursor_pos, resize_type) {
        let new_top, new_left, new_width, new_height;

        cursor_pos.x += this.cursorOffset.x;
        cursor_pos.y += this.cursorOffset.y;

        switch (resize_type) {

        case cursorState.right:
            new_width = this._resizeRight(cursor_pos.x);
            break;

        case cursorState.left:
            [new_width, new_left] = this._resizeLeft(cursor_pos.x);
            break;

        case cursorState.top:
            [new_height, new_top] = this._resizeTop(cursor_pos.y);
            break;

        case cursorState.bottom:
            new_height = this._resizeBottom(cursor_pos.y);
            break;

        case cursorState.bottomright:
            new_height = this._resizeBottom(cursor_pos.y);
            new_width = this._resizeRight(cursor_pos.x);
            break;

        case cursorState.bottomleft:
            new_height = this._resizeBottom(cursor_pos.y);
            [new_width, new_left] = this._resizeLeft(cursor_pos.x);
            break;

        case cursorState.topright:
            [new_height, new_top] = this._resizeTop(cursor_pos.y);
            new_width = this._resizeRight(cursor_pos.x);
            break;

        case cursorState.topleft:
            [new_height, new_top] = this._resizeTop(cursor_pos.y);
            [new_width, new_left] = this._resizeLeft(cursor_pos.x);
            break;

        //passthrough is intentional here
        case cursorState.regular:
        default:
            break;
        }

        this.update(new_left, new_top, new_width, new_height);
    }

    /**
     * Returns a {module:cursor/cursorState} value that represents
     * the cursor's state in relation to the rect.
     * For example if the cursor is on top of the rect's right edge,
     * it will return cursorState.right.
     * @param {module:cursor/CursorPos} cursor_pos
     * @package
     */
    getCursorResizeState(cursor_pos) {
        const dgOffset = 4,
              widthOffset = this.borderWidth + 2,
              totalOffset = widthOffset + dgOffset;

        if (isBetween(cursor_pos.x, this.right - widthOffset, this.right)) {
            /**
             * Cases where the cursor's position is either on the right edge of the dialog's
             * border, or on top/below it.
             *                             ██
             *                             ██
             * ┌───────────────────────────═╗
             * │ React Win32 dialog box     ║
             * ├────────────────────────────╢
             * │                            ║
             * │                            ║
             * │                            ║
             * └───────────────────────────═╝
             *                             ██
             *                             ██
             */

            if (isBetween(cursor_pos.y, this.top, this.bottom)) {

                /**
                 * Cases where the cursor's position is on the right edge of the border.
                 * ┌───────────────────────────═╗
                 * │ React Win32 dialog box     ║
                 * ├────────────────────────────╢
                 * │                            ║
                 * │                            ║
                 * │                            ║
                 * └───────────────────────────═╝
                 */
                if (cursor_pos.y >= (this.bottom - totalOffset)) {
                    return cursorState.bottomright;
                } else if (cursor_pos.y <= (this.top + totalOffset)) {
                    return cursorState.topright;
                } else {
                    return cursorState.right;
                }

            }

        } else if (isBetween(cursor_pos.x, this.left, this.left + widthOffset)) {
            /**
             * Cases where the cursor's position is either on the left edge of the dialog's.
             * border, or on top/below it.
             * ██
             * ██
             * ╔═───────────────────────────┐
             * ║ React Win32 dialog box     │
             * ╟────────────────────────────┤
             * ║                            │
             * ║                            │
             * ║                            │
             * ╚═───────────────────────────┘
             * ██
             * ██
             */

            if (isBetween(cursor_pos.y, this.top, this.bottom)) {

                /**
                 * Cases where the cursor's position is on the left edge of the border.
                 * ╔═───────────────────────────┐
                 * ║ React Win32 dialog box     │
                 * ╟────────────────────────────┤
                 * ║                            │
                 * ║                            │
                 * ║                            │
                 * ╚═───────────────────────────┘
                 */
                if (cursor_pos.y >= (this.bottom - totalOffset)) {
                    return cursorState.bottomleft;
                } else if (cursor_pos.y <= (this.top + totalOffset)) {
                    return cursorState.topleft;
                } else {
                    return cursorState.left;
                }

            }

        } else if (isBetween(cursor_pos.x, this.left, this.right)) {
            /**
             * Cases where the cursor's position is either on the top or bottom edges of the
             * dialog's border, or on the areas right above or below them.
             *   ██████████████████████████
             *   ██████████████████████████
             * ┌─══════════════════════════─┐
             * │ React Win32 dialog box     │
             * ├────────────────────────────┤
             * │                            │
             * │                            │
             * │                            │
             * └─══════════════════════════─┘
             *   ██████████████████████████
             *   ██████████████████████████
             */

            if (isBetween(cursor_pos.y, this.bottom - widthOffset, this.bottom)) {
                /**
                 * Case where the cursor's position is on the bottom edge of the dialog's border.
                 * ┌────────────────────────────┐
                 * │ React Win32 dialog box     │
                 * ├────────────────────────────┤
                 * │                            │
                 * │                            │
                 * │                            │
                 * └─══════════════════════════─┘
                 */
                return cursorState.bottom;

            } else if (isBetween(cursor_pos.y, this.top, this.top + widthOffset)) {
                /**
                 * Case where the cursor's position is on the top edge of the dialog's border.
                 * ┌─══════════════════════════─┐
                 * │ React Win32 dialog box     │
                 * ├────────────────────────────┤
                 * │                            │
                 * │                            │
                 * │                            │
                 * └────────────────────────────┘
                 */
                return cursorState.top;
            }

        }

        return cursorState.regular;
    }

    /**
     * Stretches the rect's edges so that they cover the entire
     * viewport.
     * @package
     */
    coverDocumentBody() {
        this.left = 0;
        this.top = 0;
        this.width = getViewportWidth();
        this.height = document.body.clientHeight;

        this.right = this.width + this.left;
        this.bottom = this.top + this.height;
    }

    /**
     * Moves the rect.
     * @param {module:cursor/CursorPos} cursor_pos
     * @package
     */
    moveToCursor(cursor_pos) {
        this.update(cursor_pos.x + this.cursorOffset.x,
                    cursor_pos.y + this.cursorOffset.y);
    }

    /**
     * Stores the cursor's offset in relation to the rect's edges.
     * This method should be called once, right before a move/resize operation,
     * just after the user clicks on the dialog's edges or titlebar.
     *
     * Below is an illustration of a dialog's titlebar. The asterisk (*) symbolizes
     * the point where the user clicked in order to move the window (that's
     * also the point stored in cursor_pos).
     * This is how the offsets are calculated in the move operation:
     *
     *           ╠═══ x offset ═══╣
     *     ╦     ┌───────────────────────────────────────────────────────┐
     *  y offset │                                                       │
     *     ╩     │                *                                      │
     *           ├───────────────────────────────────────────────────────┤
     *           │                                                       │
     *
     * @param {module:cursor/CursorPos} cursor_pos If it's undefined then
     * this method just resets the internal offset values.
     * @param {module:cursor/cursorState} resize_type If it's undefined then
     * this method sets up the offsets for a move operation. Otherwise it sets
     * the offsets for a resize operation. This parameter is only used if
     * cursor_pos is defined.
     * @package
     */
    setCursorOffset(cursor_pos, resize_type) {
        this.cursorOffset.x = this.cursorOffset.y = 0;

        if (cursor_pos) {
            switch (resize_type) {

            case cursorState.right:
                this.cursorOffset.x = this.right - cursor_pos.x;
                break;

            case cursorState.left:
                this.cursorOffset.x = this.left - cursor_pos.x;
                break;

            case cursorState.top:
                this.cursorOffset.y = this.top - cursor_pos.y;
                break;

            case cursorState.bottom:
                this.cursorOffset.y = this.bottom - cursor_pos.y;
                break;

            case cursorState.bottomright:
                this.cursorOffset.x = this.right - cursor_pos.x;
                this.cursorOffset.y = this.bottom - cursor_pos.y;
                break;

            case cursorState.bottomleft:
                this.cursorOffset.x = this.left - cursor_pos.x;
                this.cursorOffset.y = this.bottom - cursor_pos.y;
                break;

            case cursorState.topright:
                this.cursorOffset.x = this.right - cursor_pos.x;
                this.cursorOffset.y = this.top - cursor_pos.y;
                break;

            case cursorState.topleft:
                this.cursorOffset.x = this.left - cursor_pos.x;
                this.cursorOffset.y = this.top - cursor_pos.y;
                break;

            case cursorState.regular:
                break;

            default:
                this.cursorOffset.x = this.left - cursor_pos.x;
                this.cursorOffset.y = this.top - cursor_pos.y;
                break;
            }
        }
    }

    /**
     * Resets the rect's position to the upper left corner of the viewport.
     * @package
     */
    moveWithinViewport() {
        this.update((this.left <= 0) ? 1 : this.left,
                    (this.top <= 0) ? 1 : this.top);
    }

    /**
     * @package
     */
    toString() {
        return `DialogRect = {\n` +
               `    .left = ${this.left},\n` +
               `    .top = ${this.top},\n` +
               `    .width = ${this.width},\n` +
               `    .height = ${this.height},\n` +
               `    .minWidth = ${this.minWidth},\n` +
               `    .minHeight = ${this.minHeight}\n` +
               `}`;
    }
}


/* Copyright(c) 2014 Jan Blaha
 *
 * Extension storing 5min based statistics - amount of successfully generated, amount of failures
 */

var Statistics = function (reporter, definition) {
  this.reporter = reporter
  this.definition = definition

  this.reporter.beforeRenderListeners.add(definition.name, this, Statistics.prototype.handleBeforeRender)
  this.reporter.afterRenderListeners.add(definition.name, this, Statistics.prototype.handleAfterRender)
  this._defineEntities()
}

Statistics.prototype.handleBeforeRender = function (request, response) {
  var fiveMinuteDate = new Date((Math.floor(new Date().getTime() / 1000 / 60 / 5) * 1000 * 60 * 5))

  request.statistics = {
    fiveMinuteDate: fiveMinuteDate
  }

  return this.reporter.documentStore.collection('statistics').update({
    fiveMinuteDate: fiveMinuteDate
  }, {
    $inc: {
      amount: 1
    },
    $set: {
      fiveMinuteDate: fiveMinuteDate
    }
  }, {upsert: true})
}

Statistics.prototype.handleAfterRender = function (request, response) {
  return this.reporter.documentStore.collection('statistics').update({
    fiveMinuteDate: request.statistics.fiveMinuteDate
  }, {
    $inc: {
      success: 1
    }
  })
}

Statistics.prototype._defineEntities = function () {
  this.reporter.documentStore.registerEntityType('StatisticType', {
    _id: {type: 'Edm.String', key: true},
    fiveMinuteDate: {type: 'Edm.DateTimeOffset'},
    amount: {type: 'Edm.Int64'},
    success: {type: 'Edm.Int64'}
  })

  this.reporter.documentStore.registerEntitySet('statistics', {entityType: 'jsreport.StatisticType', shared: true})
}

module.exports = function (reporter, definition) {
  reporter[definition.name] = new Statistics(reporter, definition)
}


'use strict';

/* eslint-disable camelcase */
var prf = require('./prf');
var address = require('./address');
var bs58check = require('bs58check');
var sodium = require('libsodium-wrappers-sumo');
var zconfig = require('./config');

/*
 * Creates a Z secret key (a_sk)
 * @param {String} phrase (Password phrase)
 * @return {Sting} Z secret key (a_sk)
 */
function mkZSecretKey(phrase) {
  var a_sk = address.mkPrivKey(phrase);
  var baddr = Buffer.from(a_sk, 'hex');
  baddr[0] &= 0x0f;
  return baddr.toString('hex');
}

/*
 * Converts the secret key to a spending key
 * @param {String} a_sk (secret key)
 * @param {String} zcSpendingKeyHash (secret key hash,optional)
 * @return {Sting} sk (spending key)
 */
function zSecretKeyToSpendingKey(a_sk, zcSpendingKeyHash) {
  zcSpendingKeyHash = zcSpendingKeyHash || zconfig.mainnet.zcSpendingKeyHash;

  var buf = Buffer.from(zcSpendingKeyHash + a_sk, 'hex');
  return bs58check.encode(buf).toString('hex');
}

/*
 * Converts a Z secret key to a paying key
 * @param {String} a_sk (secret key)
 * @return {Sting} a_pk key (paying key)
 */
function zSecretKeyToPayingKey(a_sk) {
  return prf.PRF_addr_a_pk(Buffer.from(a_sk, 'hex')).toString('hex');
}

/*
 * Converts a Z secret key to a transmission key
 * @param {String} a_sk (secret key)
 * @return {Sting} pk_enc key (transmisison key)
 */
function zSecretKeyToTransmissionKey(a_sk) {
  var sk_enc = prf.PRF_addr_sk_enc(Buffer.from(a_sk, 'hex'));

  // Curve 25519 clamping
  sk_enc[0] &= 248;
  sk_enc[32] &= 127;
  sk_enc[31] |= 64;

  return Buffer.from(sodium.crypto_scalarmult_base(sk_enc)).toString('hex');
}

/*
 * Makes a Z address given:
 * @param {String} a_pk (paying key)
 * @param {String} pk_enc key (transmission key)
 * @param {String} zcPaymentAddressHash (hash for payment address, optional)
 * @return {String} Zaddress
 */
function mkZAddress(a_pk, pk_enc, zcPaymentAddressHash) {
  zcPaymentAddressHash = zcPaymentAddressHash || zconfig.mainnet.zcPaymentAddressHash;

  var buf = Buffer.from(zcPaymentAddressHash + a_pk + pk_enc, 'hex');
  return bs58check.encode(buf).toString('hex');
}

module.exports = {
  mkZSecretKey: mkZSecretKey,
  zSecretKeyToTransmissionKey: zSecretKeyToTransmissionKey,
  zSecretKeyToPayingKey: zSecretKeyToPayingKey,
  zSecretKeyToSpendingKey: zSecretKeyToSpendingKey,
  mkZAddress: mkZAddress
};


/**
 * Created by MEAN Factory on 6/17/16.
 */

/*jshint node: true*/
'use strict';

var $           = require('gulp-load-plugins')({ lazy: true }),
    args        = require('yargs').argv,
    config      = require('./gulp.config'),
    del         = require('del'),
    gulp        = require('gulp'),
    merge       = require('merge-stream'),
    path        = require('path');

var port        = process.env.PORT || config.defaultPort;

gulp.task('help', $.taskListing);
gulp.task('default', ['help']);

gulp.task('validate-js', function(){
    log('Analyzing source...');
    return gulp
        .src(config.files.js.all)
        .pipe($.if(args.verbose, $.print()))
        .pipe($.jscs())
        .pipe($.jshint())
        .pipe($.jshint.reporter('jshint-stylish', {verbose: true}))
        .pipe($.jshint.reporter('fail'));
});
gulp.task('validate-js-watch', function(){
    gulp.watch(config.files.js.all, ['validate-js']);
});

/**
 * Bump the version
 * --type=pre will bump the prerelease version *.*.*-x
 * --type=patch or no flag will bump the patch version *.*.x
 * --type=minor will bump the minor version *.x.*
 * --type=major will bump the major version x.*.*
 * --version=1.2.3 will bump to a specific version and ignore other flags
 */
gulp.task('bump', function() {
    var msg = 'Bumping versions';
    var type = args.type;
    var version = args.version;
    var options = {};
    if (version) {
        options.version = version;
        msg += ' to ' + version;
    } else {
        options.type = type;
        msg += ' for a ' + type;
    }
    log(msg);

    return gulp
        .src(config.packages)
        .pipe($.print())
        .pipe($.bump(options))
        .pipe(gulp.dest(config.folders.root));
});

// -----------------------------------

function changeEvent(event){
    var srcPattern = new RegExp('/.*(?=/' + config.source + ')/');
    log('File ' + event.path.replace(srcPattern, '') + ' ' + event.type);
}

function clean(path){
    log('Cleaning: ' + $.util.colors.blue(path));
    return del(path);
}

function log(msg) {
    if (typeof(msg) === 'object') {
        for (var item in msg) {
            if (msg.hasOwnProperty(item)) {
                $.util.log($.util.colors.blue(msg[item]));
            }
        }
    } else {
        $.util.log($.util.colors.blue(msg));
    }
}


/**
 * (C) Copyright 2016 Manuel Martins.
 *
 * This module is inspired by json_file_system.
 * (json_file_system is Copyright (c) 2014 Jalal Hejazi,
 *  Licensed under the MIT license.)
 *
 * 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.
 *
 * Created by: ManuelMartins
 * Created on: 26-01-2016
 *
 */

'use strict';

var util = require('./util');

function defaultCallback(err) {
  if (err) {
    console.log(err);
  }
}

/**
 * Provides in memory storage and regular flush to disk.
 *
 * @param {object} options
 * @param {object}  options.db
 * @param {float}  options.db._flush
 * @param {string}  options.file
 * @constructor
 */
function Memory(options) {
  options = options || {};
  var self = this;
  self.flush = options.db._db._memory.flush || false;
  self.flushInterval = options.db._db._memory.flushInterval || 10000;
  self.flushFile = options.file;
  self.memoryTable = [];
  console.log('Data will be handled using \'Memory\' driver');
  // :S yeah we need to load it synchronously otherwise it might be loaded after the first insert
  var content = util.fileSystem.readSync(self.flushFile);
  self.set(content);
  if (self.flush) {
    console.log('\'Memory\' driver will flush data every %sms', self.flushInterval);
    // set interval to flush
    setInterval(function flushToDisk() {
      util.fileSystem.lock(self.flushFile, function afterLock(err) {
        if (err) {
          throw err;
        }
        self.get(function afterGet(err, inMemoryContent) {
          if (err) {
            util.fileSystem.unlock(self.flushFile);
            throw err;
          }
          util.fileSystem.write(self.flushFile, inMemoryContent, function afterWrite(err) {
            util.fileSystem.unlock(self.flushFile);
            if (err) {
              throw err;
            }
          });
        });
      });
    }, self.flushInterval);
  }
}

/**
 * Writes a list of objects to the data driver
 *
 * @param content the content to write
 * @param callback
 */
Memory.prototype.set = function set(content, callback) {
  var self = this;
  callback = callback || defaultCallback;
  self.memoryTable = util.clone(content);
  return callback(undefined, self.memoryTable);
};

/**
 * Reads a list of objects from the data driver
 *
 * @param callback
 */
Memory.prototype.get = function get(callback) {
  var self = this;
  process.nextTick(function(){
    callback = callback || defaultCallback;
    return callback(undefined, self.memoryTable);
  });
};

/**
 * Locks the data provider
 *
 * @param callback
 */
Memory.prototype.lock = function lock(callback) {
  process.nextTick(function(){
    callback = callback || defaultCallback;
    return callback(undefined);
  });
};

/**
 * Unlocks the data provider
 *
 * @param callback
 */
Memory.prototype.unlock = function unlock(callback) {
  process.nextTick(function(){
    callback = callback || defaultCallback;
    return callback(undefined);
  });
};

module.exports = Memory;


'use strict';
/*
 * Copyright (C) 2012-2013 Nicolas Froidure
 *
 * This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 3. It is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 */
'use strict';
// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define([], function() {
// START: Module logic start

  function VarStreamWriter(callback, options) {
    this.lastContext='';
    this.callback=callback; // Output stream callback
    this.options=options;
    this.imbricatedArrayEntries=new Array();
    this.scopes=new Array();
    this.contexts=new Array();
    this.previousContext='';
  }

  // Static consts
  VarStreamWriter.MORPH_CONTEXTS=2;
  VarStreamWriter.MERGE_ARRAYS=4;
  VarStreamWriter.OPTIONS=
    VarStreamWriter.MORPH_CONTEXTS|VarStreamWriter.MERGE_ARRAYS;

  VarStreamWriter.prototype.write = function (scope, context, root) {
    context = context || '';
    if('' === context) {
      root = scope;
    }
    if(scope instanceof Object) {
      if(-1 !== this.scopes.indexOf(scope)) {
        if(root == scope) {
          this.callback(context+'&=^0'+"\n");
        } else {
          this.callback(context+'&='+this.contexts[this.scopes.indexOf(scope)]+"\n");
        }
        this.previousContext = context;
        return;
      }
      this.scopes.push(scope);
      this.contexts.push(context);
    }
    if(scope instanceof Array) {
      for(var i=0, j=scope.length; i<j; i++) {
        this.imbricatedArrayEntries.push(true);
        this.write(scope[i],(context?context+'.':'')
          +(this.options&VarStreamWriter.MERGE_ARRAYS?'?':i), root);
        this.imbricatedArrayEntries.pop();
      }
    } else if(scope instanceof Object) {
      for (var prop in scope) {
        if (scope.hasOwnProperty(prop)&&(!(scope instanceof Function))
          &&/^([a-z0-9_]+)$/i.test(prop)) {
          this.write(scope[prop],(context?context+'.':'')+prop, root);
        }
      }
    } else {
      if('' === context) {
        throw new Error('The root scope must be an Object or an Array.');
      }
      // Changing context with imbricated arrays
      for(var i=this.imbricatedArrayEntries.length-1; i>=0; i--) {
        var index=context.lastIndexOf('?');
        if(-1 === index) {
          continue;
        }
        if(this.imbricatedArrayEntries[i]) {
          context=context.substr(0,index)+'+'+context.substr(index+1);
          this.imbricatedArrayEntries[i]=false;
        } else {
          context=context.substr(0,index)+'*'+context.substr(index+1);
        }
      }
      // Trying to reduce context with ^
      var morphedContext=context;
      if(this.options&VarStreamWriter.MORPH_CONTEXTS&&this.lastContext
        &&morphedContext.indexOf(this.lastContext+'.')===0) {
        morphedContext=morphedContext.replace(this.lastContext,'^')
      }
      // Saving this context for later use
      var index=context.lastIndexOf('.');
      this.lastContext=(index!==false?context.substr(0,index):'');
      // Export the value
      if('undefined' === typeof scope) {
        scope = '';
      } else if(null === scope) {
        scope = 'null';
      } else {
        scope = (scope+'').replace(/(\r?\n)/gm,'\\'+"\n");
      }
      // Calling back
      this.callback(morphedContext+'='+scope+"\n");
      this.previousContext = context;
    }
  };

// END: Module logic end

  return VarStreamWriter;

});})(this,typeof define === 'function' && define.amd ?
  // AMD
  define :
  // NodeJS
  (typeof exports === 'object'?function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    module.exports=factory.apply(this, deps.map(function(dep){
      return require(dep);
    }));
  }:
  // Global
  function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    this.VarStreamWriter=factory.apply(this, deps.map(function(dep){
      return root[dep.substring(dep.lastIndexOf('/')+1)];
    }));
  }.bind(this)
  )
);


// make a crafter
var crafter = require('../')();

// load in recipes
crafter.recipe(require('./data/recipes.json'));

// load up items index
var itemsIndex = require('./data/items.json');

var texturePath = 'textures/';
var crafting = document.getElementById('crafting');
var items = document.getElementById('items');
var craftable = document.getElementById('craftable');
var result = document.getElementById('result');

// craft it!
// todo: maybe move this to the webui?
var giving = false;
function craftit(have) {
  have = have || [];
  [].forEach.call(craftable.querySelectorAll('li'), function(li, i) {
    var row = Math.floor(i / 3);
    if (!have[row]) have[row] = [];
    have[row].push(li.getAttribute('data-type') || 'none');
  });
  giving = crafter.craft(have.filter(function(row) {
    return row.length > 0;
  }));
  result.innerHTML = '';
  if (giving !== false) {
    var give = giving.give;
    // find item
    var item = false;
    itemsIndex.forEach(function(i) {
      if (i.name.toLowerCase() === give[0].toLowerCase()) {
        item = i;
        return false;
      }
    });
    result.setAttribute('data-type', give[0].toLowerCase());
    result.setAttribute('data-quantity', give[1]);
    if (item === false) {
      result.innerHTML = '
' + give[0] + '
' + give[1] + '';
    } else {
      result.innerHTML = '' + give[1] + '';
    }
  }
}

// removes the items from craftable if result is picked up
function deductcost() {
  var cost = [];
  if (!giving.have || giving.have.length < 1) return;

  // flatten
  var cost = [];
  for (var i = 0; i < giving.have.length; i++) {
    for (var j = 0; j < giving.have[i].length; j++) {
      cost.push(giving.have[i][j]);
    }
  }
  if (typeof cost[0] === 'string') cost = [cost];

  function deduct(from, amt) {
    var current = parseInt(from.getAttribute('data-quantity'));
    current -= amt;
    from.setAttribute('data-quantity', current);
    updateAmounts(from);
    if (current < 1) {
      from.setAttribute('data-type', 'none');
      from.innerHTML = '';
    }
  }

  [].forEach.call(craftable.querySelectorAll('li'), function(li, i) {
    var row = Math.floor(i / 3);
    var has = (li.getAttribute('data-type') || 'none').toLowerCase();
    for (var c = 0; c < cost.length; c++) {
      if (cost[c][0].toLowerCase() === has) {
        var price = cost[c][1];
        cost.splice(c, 1);
        deduct(li, price);
        return false;
      }
    }
  });
}

// UI
var webui = require('../webui')({
  container: document.getElementById('crafting')
});
function updateAmounts(slot, quantity) {
  var slotAmt = slot.querySelector('span');
  if (slotAmt) slotAmt.innerHTML = slot.getAttribute('data-quantity');
  var activeAmt = webui.active.querySelector('span');
  if (activeAmt) activeAmt.innerHTML = webui.active.getAttribute('data-quantity');
  craftit();
}
webui.on('pickup', function(slot, quantity) {
  if (slot.id === 'result') deductcost();
  updateAmounts(slot, quantity);
});
webui.on('drop', updateAmounts);

function createBlock(image) {
  var block = document.getElementById('block-template').cloneNode(true);
  [].forEach.call(block.querySelectorAll('#block > div'), function(el, i) {
    el.style.backgroundImage = 'url(' + image + ')';
  });
  return block.innerHTML;
}

// load up some items into our inventory
(function loadItems() {
  var lis = items.querySelectorAll('li');
  itemsIndex.filter(function(item) {
    return item.has > 0;
  }).forEach(function(item, i) {
    if (item.image.slice(0, 6) === 'blocks') {
      lis[i].innerHTML = createBlock(texturePath + item.image);
    } else {
      lis[i].innerHTML = '';
    }
    lis[i].innerHTML += '' + item.has + '';
    lis[i].setAttribute('data-type', item.name);
    lis[i].setAttribute('data-quantity', item.has);
  });
}());

// js editor
var jsedit = require('javascript-editor')({
  container: document.querySelector('#editor')
});
jsedit.editor.setValue(document.querySelector('#program').text);
document.getElementById('addrecipe').addEventListener('click', function(e) {
  var self = this;
  var value = jsedit.editor.getValue();
  try { eval(value); } catch (err) { console.error(err); }
  var orig = this.innerHTML;
  this.innerHTML = 'Added!';
  setTimeout(function() { self.innerHTML = orig; }, 2000);
  return false;
});


/**
 * (C) Copyright 2015 Manuel Martins.
 *
 * This module is inspired by json_file_system.
 * (json_file_system is Copyright (c) 2014 Jalal Hejazi,
 *  Licensed under the MIT license.)
 *
 * 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.
 *
 * Created by: ManuelMartins
 * Created on: 26-01-2016
 *
 */

'use strict';

var Memory = require('./memory');
var Disk = require('./disk');

/**
 * Provides the most common I/O operations.
 *
 * @param {object} options
 * @param {object} options.db
 * @param {object} options.db._
 * @param {string} options.db._._driver
 * @constructor
 */
function DataHandler(options) {
  options = options || {};
  var dataHandler = options.db._db._driver || 'disk';
  switch (dataHandler) {
    // load the driver to be used just once
    case 'memory':
      this.dataHandlerDriver = new Memory(options);
      break;
    case 'disk':
    default:
      this.dataHandlerDriver = new Disk(options);
  }
}

/**
 * Writes a list of objects to the data driver
 *
 * @param content the content to write
 * @param callback
 */
DataHandler.prototype.set = function write(content, callback) {
  return this.dataHandlerDriver.set(content, callback);
};

/**
 * Reads a list of objects from the data driver
 *
 * @param callback
 */
DataHandler.prototype.get = function read(callback) {
  return this.dataHandlerDriver.get(callback);
};

/**
 * Locks the data provider
 *
 * @param callback
 */
DataHandler.prototype.lock = function read(callback) {
  return this.dataHandlerDriver.lock(callback);
};

/**
 * Unlocks the data provider
 *
 * @param callback
 */
DataHandler.prototype.unlock = function read(callback) {
  return this.dataHandlerDriver.unlock(callback);
};

module.exports = DataHandler;


/*
 * Copyright (C) 2012-2013 Nicolas Froidure
 *
 * This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 3. It is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 */

// AMD + global + NodeJS : You can use this object by inserting a script
// or using an AMD loader (like RequireJS) or using NodeJS
(function(root,define){ define([], function() {
  'use strict';
// START: Module logic start

  // Constructor
  function VarStreamReader (scope, prop, options) {
    // Keep a ref to the root scope
    this.rootScope={root: scope, prop: prop};
    // Save the options
    this.options=options;
    // Store current scopes for backward references
    this.previousNodes=[];
    // The parse state
    this.state=PARSE_NEWLINE;
    // The current values
    this.leftValue='';
    this.rightValue='';
    this.operator='';
    this.escaped=ESC_NONE;
  }

  // Static consts
  VarStreamReader.STRICT_MODE=1;
  VarStreamReader.OPTIONS=VarStreamReader.STRICT_MODE;

  // Constants
    // Chars
  var CHR_ENDL = '\n'
    , CHR_CR = '\r'
    , CHR_SEP = '.'
    , CHR_BCK = '^'
    , CHR_EQ = '='
    , CHR_ESC = '\\'
    , CHR_PLU = '+'
    , CHR_MIN = '-'
    , CHR_MUL = '*'
    , CHR_DIV = '/'
    , CHR_MOD = '%'
    , CHR_REF = '&'
    , CHR_NEW = '!'
    , CHR_COM = '#'
    // Chars sets
    , EQ_OPS = [CHR_PLU,CHR_MIN,CHR_MUL,CHR_DIV,CHR_MOD,CHR_REF]
    , ARRAY_OPS = [CHR_PLU,CHR_MUL,CHR_NEW]
    , ARRAY_NODE_CHARS = /^[0-9]+$/
    , PROP_NODE_CHARS = /^[a-zA-Z0-9_]+$/
    , BCK_CHARS = /^\^[0-9]*$/
    // Parsing status
    , PARSE_NEWLINE = 1
    , PARSE_LVAL = 2
    , PARSE_OPERATOR = 3
    , PARSE_RVAL = 4
    , PARSE_MLSTRING = 5
    , PARSE_COMMENT = 6
    , PARSE_SILENT = 7
    // Escape status
    , ESC_NONE = 0
    , ESC_LF = 1
    , ESC_ALL = 3
  ;

  VarStreamReader.prototype.resolveScope = function (val) {
    var nodes = val.split(CHR_SEP)
      , scope = this.rootScope
      , n = 0
    ;

    // Looking for backward refs in the first node
    if(nodes[0] && nodes[0][0] == CHR_BCK) {
      // if no numbers adding every previous nodes
      if(nodes[0] == CHR_BCK) {
        n = this.previousNodes.length ? this.previousNodes.length - 1 : 0;
      // if numbers
      } else {
        // check it
        if(!BCK_CHARS.test(nodes[0])) {
          if(this.options&VarStreamReader.STRICT_MODE) {
            throw new Error('Malformed backward reference.');
          }
          return null;
        }
        n = parseInt(nodes[0].substring(1), 10);
      }
      if(n > this.previousNodes.length) {
        if(this.options&VarStreamReader.STRICT_MODE) {
          throw new SyntaxError('Backward reference index is greater than the'
            + ' previous node max index.');
        }
        return null;
      }
      this.previousNodes.length = n;
      nodes.shift();
      nodes.unshift.apply(nodes,this.previousNodes);
    }

    // Looping throught each nodes
    for(var i=0, j=nodes.length; i<j; i++) {
      // Checking if the node is not empty
      if(''===nodes[i]) {
        if(this.options&VarStreamReader.STRICT_MODE) {
          throw new Error('The leftValue can\'t have empty nodes ('+val+').');
        }
        return null;
      }
      // Array operators
      if(-1!==ARRAY_OPS.indexOf(nodes[i])||ARRAY_NODE_CHARS.test(nodes[i])) {
        // Ensure the scope is an array
        if('undefined'=== typeof scope.root[scope.prop]
          ||!(scope.root[scope.prop] instanceof Array)) {
            scope.root[scope.prop]=[];
        }
        if(nodes[i]===CHR_PLU) {
            nodes[i]=scope.root[scope.prop].length;
        }
        if(nodes[i]===CHR_MUL) {
          nodes[i]=scope.root[scope.prop].length-1;
        }
        if(nodes[i]===CHR_NEW) {
          nodes[i]=scope.root[scope.prop].length=0;
        }
      } else {
        // Checking node chars
        if(!PROP_NODE_CHARS.test(nodes[i])) {
          if(this.options&VarStreamReader.STRICT_MODE) {
            throw new SyntaxError('Illegal chars found in a the node'
              + ' "'+nodes[i]+'".');
          }
          return null;
        }
        // Ensure the scope is an object
        if('undefined'=== typeof scope.root[scope.prop]
          ||!(scope.root[scope.prop] instanceof Object)) {
            scope.root[scope.prop]={};
        }
      }
      // Resolving the node scope
      scope={
        root : scope.root[scope.prop],
        prop : nodes[i]
      };
    }

    // Keep previous nodes for backwards references
    this.previousNodes = nodes;

    return scope;
  };

  VarStreamReader.prototype.read = function (chunk) {
    // Looping throught chunk chars
    for(var i=0, j=chunk.length; i<j; i++) {
      // detect escaped chars
      if(chunk[i]===CHR_ESC && (
          this.state===PARSE_RVAL
          ||this.state===PARSE_SILENT
          ||this.state===PARSE_MLSTRING
         )
        ) {
        if(this.escaped) {
          this.escaped=ESC_NONE;
        } else {
          this.escaped=ESC_ALL;
          continue;
        }
      }
      // parsing chars according to the current state
      switch(this.state) {
        // Continue while newlines
        case PARSE_NEWLINE:
          this.escaped=ESC_NONE;
          this.operator='';
          this.leftValue='';
          this.rightValue='';
          if(chunk[i]===CHR_ENDL||chunk[i]===CHR_CR) {
            continue;
          }
        // Read left value content
        case PARSE_LVAL:
          // Detect comments
          if(chunk[i]===CHR_COM) {
            this.state=PARSE_COMMENT;
            continue;
          }
          // Detect special operators
          if(this.leftValue.lastIndexOf(CHR_SEP)!=this.leftValue.length-1
            &&-1!==EQ_OPS.indexOf(chunk[i])) {
            this.state=PARSE_OPERATOR;
            this.operator=chunk[i];
            continue;
          }
          // Detect the = operator
          if(CHR_EQ===chunk[i]) {
            this.state=PARSE_RVAL;
            this.operator=chunk[i];
            continue;
          }
          // Fail if a new line is found
          if(chunk[i]===CHR_ENDL||chunk[i]===CHR_CR) {
            if(this.options&VarStreamReader.STRICT_MODE) {
              throw SyntaxError('Unexpected new line found while parsing '
              +' a leftValue.');
            }
            this.state=PARSE_NEWLINE;
            continue;
          }
          // Store LVAL chars
          this.state=PARSE_LVAL;
          this.leftValue+=chunk[i];
          continue;
        // Read right value content
        case PARSE_RVAL:
          // Left value should not be empty
          if(''===this.leftValue) {
            if(this.options&VarStreamReader.STRICT_MODE) {
              throw SyntaxError('Found an empty leftValue.');
            }
            this.state=PARSE_SILENT;
          }
          // Stop if a new line is found
          if(chunk[i]===CHR_ENDL||chunk[i]===CHR_CR) {
            // rightValue can be empty only with the = operator
            // or if the string is multiline
            if(this.operator!=CHR_EQ&&''===this.rightValue
              &&this.escaped===ESC_NONE) {
              if(this.options&VarStreamReader.STRICT_MODE) {
                throw SyntaxError('Found an empty rightValue.');
              }
              this.state=PARSE_NEWLINE;
              continue;
            }
            // Compute rval
            // if it's a ref
            if(this.operator===CHR_REF) {
              this.rightValue=this.resolveScope(this.rightValue);
            } else if('null'===this.rightValue) {
              this.rightValue=null;
            // Booleans
            } else if('true'===this.rightValue) {
              this.rightValue=true;
            } else if('false'===this.rightValue) {
              this.rightValue=false;
            // Numbers
            } else if('NaN'===this.rightValue) {
              this.rightValue=NaN;
            } else if(/^\-?([0-9]+(\.[0-9]+)?|Infinity)$/
              .test(this.rightValue)) {
              this.rightValue=Number(this.rightValue);
            }
            // Compute lval
            this.leftValue=this.resolveScope(this.leftValue);
            // set rval in lval (with operators)
            if(null!==this.leftValue) {
              switch(this.operator) {
                case CHR_REF:
                  this.leftValue.root[this.leftValue.prop] = this.rightValue ?
                    this.rightValue.root[this.rightValue.prop] :
                    null;
                break;
                case CHR_EQ:
                  if(this.rightValue!=='' || 'string' ===
                    typeof this.leftValue.root[this.leftValue.prop]) {
                    this.leftValue.root[this.leftValue.prop]=this.rightValue;
                  } else {
                    delete this.leftValue.root[this.leftValue.prop];
                  }
                break;
                case CHR_PLU:
                  this.leftValue.root[this.leftValue.prop] +=
                    null === this.rightValue ? NaN : this.rightValue;
                break;
                case CHR_MIN:
                  this.leftValue.root[this.leftValue.prop] -=
                    null === this.rightValue ? NaN : this.rightValue;
                break;
                case CHR_MUL:
                  this.leftValue.root[this.leftValue.prop] *=
                    null === this.rightValue ? NaN : this.rightValue;
                break;
                case CHR_DIV:
                  this.leftValue.root[this.leftValue.prop] /=
                    null === this.rightValue ? NaN : this.rightValue;
                break;
                case CHR_MOD:
                  this.leftValue.root[this.leftValue.prop]  %=
                    null === this.rightValue ? NaN : this.rightValue;
                break;
              }
            }
            // if the newline was escaped, continue to read the string
            if(this.escaped) {
              if(chunk[i]===CHR_CR) {
                this.escaped=ESC_LF;
              } else {
                this.escaped=ESC_NONE;
              }
              if(null!==this.leftValue) {
                this.state=PARSE_MLSTRING;
                this.leftValue.root[this.leftValue.prop]+=chunk[i];
              } else {
                this.state=PARSE_SILENT;
              }
            } else {
              this.state=PARSE_NEWLINE;
            }
            continue;
          }
          // Store RVAL chars
          if(this.escaped) {
            if(this.escaped==ESC_ALL) {
              if(this.options&VarStreamReader.STRICT_MODE) {
                throw Error('Found an escape char but there was nothing to escape.');
                }
              this.rightValue+='\\';
            }
            this.escaped=ESC_NONE;
          }
          this.rightValue+=chunk[i];
          continue;
        // Parse the content of a multiline value
        case PARSE_MLSTRING:
          if(this.escaped) {
            if(this.escaped===ESC_ALL&&chunk[i]===CHR_CR) {
              this.escaped=ESC_LF;
            } else if(chunk[i]===CHR_ENDL) {
              this.escaped=ESC_NONE;
            } else {
              if(this.escaped===ESC_LF) {
                if(this.options&VarStreamReader.STRICT_MODE) {
                  throw new SyntaxError('Assuming a LF after an escaped CR, '
                    +chunk[i]+' found instead.');
                }
              } else {
                if(this.options&VarStreamReader.STRICT_MODE) {
                  throw new SyntaxError('Found an escape char but there was'
                    + ' nothing to escape.');
                }
                this.leftValue.root[this.leftValue.prop]+='\\';
              }
              this.escaped=ESC_NONE;
            }
          } else if(chunk[i]===CHR_ENDL||chunk[i]===CHR_CR) {
            this.state=PARSE_NEWLINE;
            continue;
          }
          // Store RVAL chars
          this.leftValue.root[this.leftValue.prop]+=chunk[i];
          continue;
        // Finding the = char after an operator
        case PARSE_OPERATOR:
          if(chunk[i]===CHR_EQ) {
            this.state=PARSE_RVAL;
            continue;
          }
          if(this.options&VarStreamReader.STRICT_MODE) {
            throw new SyntaxError('Unexpected char after the'
              + ' "'+this.operator+'" operator. Expected "="'
              + ' found "'+chunk[i]+'".');
          }
          if(chunk[i]===CHR_ENDL || chunk[i]===CHR_CR) {
            this.state=PARSE_NEWLINE;
          } else {
            this.state=PARSE_SILENT;
          }
          continue;
        // Parsing a comment content
        case PARSE_COMMENT:
          if((chunk[i]===CHR_ENDL&&!(this.escaped&ESC_LF))
            ||(chunk[i]===CHR_CR&&!(this.escaped&ESC_ALL))) {
            this.state=PARSE_NEWLINE;
            continue;
          }
          if(chunk[i]===CHR_CR&&(this.escaped&ESC_ALL)) {
            this.escaped=ESC_LF;
          }
          if(chunk[i]===CHR_ENDL&&(this.escaped&ESC_LF)) {
            this.escaped=ESC_NONE;
          }
          continue;
        // Something was wrong, waiting for a newline to continue parsing
        case PARSE_SILENT:
          if((chunk[i]===CHR_ENDL&&!(this.escaped&ESC_LF))
            ||(chunk[i]===CHR_CR&&!(this.escaped&ESC_ALL))) {
            this.state=PARSE_NEWLINE;
            continue;
          }
          if(chunk[i]===CHR_CR&&(this.escaped&ESC_ALL)) {
            this.escaped=ESC_LF;
          }
          if(chunk[i]===CHR_ENDL&&(this.escaped&ESC_LF)) {
            this.escaped=ESC_NONE;
          }
          continue;
      }
    }
  };

// END: Module logic end

  return VarStreamReader;

});})(this,typeof define === 'function' && define.amd ?
  // AMD
  define :
  // NodeJS
  (typeof exports === 'object'?function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    module.exports=factory.apply(this, deps.map(function(dep){
      return require(dep);
    }));
  }:
  // Global
  function (name, deps, factory) {
    var root=this;
    if(typeof name === 'object') {
      factory=deps; deps=name;
    }
    this.VarStreamReader=factory.apply(this, deps.map(function(dep){
      return root[dep.substring(dep.lastIndexOf('/')+1)];
    }));
  }.bind(this)
  )
);


'use strict';
/*
 * Copyright (C) 2012 Nicolas Froidure
 *
 * This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 3. It is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 */
var DuplexStream = require('readable-stream').Duplex
  , util = require('util')
  , VarStreamReader = require('./VarStreamReader')
  , VarStreamWriter = require('./VarStreamWriter')
;

// Inherit of duplex stream
util.inherits(VarStream, DuplexStream);

// Constructor
function VarStream(rootObject, rootProperty, options) {
  var self = this;

  // Ensure new were used
  if(!(this instanceof VarStream)) {
    return new VarStream(rootObject, rootProperty, options);
  }

  // Ensure we had root object and property
  if(!(rootObject instanceof Object)) {
    throw new Error('No root object provided.');
  }
  if('string' !== typeof rootProperty || rootProperty == '') {
    throw new Error('No root property name given.');
  }

  // Parent constructor
  DuplexStream.call(this);

  this._varstreamReader=new VarStreamReader(rootObject, rootProperty,
    options ? options&VarStreamReader.OPTIONS : 0);

  this._varstreamWriter = new VarStreamWriter(function(str) {
      self.push(new Buffer(str, 'utf8'));
  }, options ? options&VarStreamWriter.OPTIONS : 0);

  // Parse input
  this._write = function _write(chunk, encoding, done) {
    this._varstreamReader.read(chunk.toString(
      encoding !== 'buffer' ? encoding : 'utf8'
    ));
    done();
  };

  // Output data
  this._read = function _read() {
    this._varstreamWriter.write(rootObject[rootProperty]);
    this.push(null);
  };

}

// Parse helper
VarStream.parse = function(content) {
  var root = {};
  var stream = new VarStream(root, 'prop');
  stream.write(Buffer(content));
  stream.end();
  return root.prop || {};
};

// Export helper
VarStream.stringify = function(obj) {
  var root = {prop: obj}, stream, content;
  if('object' !== typeof obj) {
    throw new Error('The stringified object must be an instance of Object.');
  }
  stream = new VarStream(root, 'prop');
  content = stream.read();
  return String(content);
};

// Exporting
VarStream.VarStreamReader = VarStream.Reader = VarStreamReader;
VarStream.VarStreamWriter = VarStream.Writer = VarStreamWriter;
module.exports = VarStream;


'use strict';

// parse the data of documents
// only used by DAO

var parser = module.exports = {};

var glob        = require('glob');
var marked      = require('marked');
var highlight   = require('highlight.js');
var node_path   = require('path');
var sep         = node_path.sep;
var node_fs     = require('fs');
var lang        = require('./lang');
var async       = require('async');

// parse the tree structure of the document
// basic infomations
// @param {path} dir absolute dir of the document root

// tree:
// {
//     index: 'README.md',
//     // relative depth
//     rel_depth: 0,
//     pages: {
//         '1_a.md': {
//             name: '1_a.md',
//             parent: <recursive>,
//             rel_depth: 1,
//             // empty array indicates there're no sub pages
//             pages: {}
//         }
//     }
// }
parser.raw_tree = function (dir, index_file, callback) {
    glob('**/*.md', {
        cwd: dir,
        strict: true

    }, function (err, files) {
        if ( err ) {
            return callback(err);
        }

        var tree = {
            // rel_depth: 0,
            pages: {},
            type: 'dir'
        };

        files.forEach(function (file) {
            parser._save_item(tree, file, dir, index_file);
        });

        callback(null, tree);
    });
};

parser._save_item = function (tree, path, root, index_file) {
    var parent = tree;
    var abs = root;

    path.split(sep).some(function (slice, i) {
        abs = node_path.join(abs, slice);

        if ( slice === index_file && node_fs.statSync(abs).isFile() ) {
            parent.path = abs;
            return true;
        }

        var current = parent.pages[slice];

        if ( !current ) {
            current = parent.pages[slice] = {
                // duplicate data for more easily parsing
                name: slice,
                // recursive data
                parent: parent
            };

            if ( node_fs.statSync(abs).isDirectory() ) {
                if ( !current.pages ) {
                    current.pages = {};
                }

                current.type = 'folder';
                parent = current;

            } else {
                current.type = 'file';
                current.path = abs;
                // if file occurs, skip parsing
                return true;
            }
        }

        parent = current;
    });
};

// TODO
var REGEX_NAME_SPLITTER = /[_\s]+/g;
var REGEX_INT = /^\d+$/;

parser.cook_name = function (name) {
    var split = name.split(REGEX_NAME_SPLITTER).filter(Boolean);

    var first = split[0];

    // number string
    if ( REGEX_INT.test(first) ) {
        split.shift();
    }

    return {
        // '01_abc_def' -> 'Abc Def'
        display_name: split.map(function (part) {
            return lang.capitalize(part);
        }).join(' '),

        // '01_abc_def' -> 'abc-def'
        uri_name    : split.map(function (part) {
            return part.toLowerCase();
        }).join('-')
    }
};

// @param {Object=} languages
// {
//     "zh-CN": {
//     }
// }
parser.language_tree = function (raw_tree, languages) {
    if ( lang.isEmptyObject(languages) ) {
        languages = null;
    }

    var ret = {};

    if ( languages ) {
        var root = raw_tree.pages || {};

        lang.each(languages, function (name, language) {
            var ltree = root[language] || {};
            delete ltree.parent;

            // It's hard to detect the closest match if user had a wrong configuration
            // convert to lowercase to make it easier
            ret[language.toLowerCase()] = {
                name: name,
                tree: ltree
            };

            ltree.language = language;
            ltree.language_text = name;
        });

        return ret;

    } else {
        return {
            default: {
                tree: raw_tree
            }
        };
    }
};

var SUPPORTED_LANG = Object.keys(highlight.LANGUAGES);

var marked_opt = {
    breaks: false,
    gfm: true,
    langPrefix: 'language-',
    pedantic: false,
    sanitize: false,
    silent: false,
    smartLists: false,
    smartypants: false,
    tables: true,
    highlight: function (code, lang) {
        if ( ~ SUPPORTED_LANG.indexOf(lang) ) {
            return highlight.highlight(lang, code).value;
        } else {
            return highlight.highlightAuto(code).value;
        }
    }
};

// options.
parser.markdown = function (code, callback) {
    marked(code, marked_opt, callback);
};

var REGEX_MATCH_H1 = /<h1>(.*?)<\/h1>/i;

parser.get_title = function (html) {
    html = html || '';

    var match = html.match(REGEX_MATCH_H1);

    return match ? match[1] : undefined;
};

// @param {Object} options
// - define
parser.sort_pages = function (pages) {
    pages.sort(function (a, b) {
        return a.name < b.name ? - 1 : 1;
    });
};

// create a cache
parser.create_data = function (language_tree, sys, callback) {
    var counter = Object.keys(language_tree).length;
    var error;
    var nodes = [];

    function iterator (data, done) {
        var node = data.node;
        var descend = data.descend;

        nodes.push(node);

        parser._parse_content(node, function (err, html) {
            if ( err ) {
                return done(err);
            }

            var title;

            if ( html ) {
                node.html = html;
                // There might be no title inside the html
                title = parser.get_title(html);
            }

            parser._generate_url(node, title || node.name);

            var pages = node.pages;

            if ( pages ) {
                // `pages` from object to array
                node.pages = Object.keys(pages).map(function (name) {
                    return pages[name];
                });

                parser.sort_pages(node.pages);
            }

            descend();
            done(null);
        });
    };

    lang.each(language_tree, function (l, language) {
        var tree = l.tree;

        if ( !tree.path ) {
            tree.path = sys.readme;
        }

        parser.walk(l.tree, iterator, function (err) {
            if ( err ) {
                error = err;
                return callback(err);

            } else if (error) {
                return;

            } else if ( -- counter === 0 ) {
                nodes.forEach(function (node) {
                    if ( node ) {
                        // remove recursive property
                        // delete node.parent;
                        // remove private data
                        delete node.path;
                    }
                });
                // free
                nodes.length = 0;

                return callback(null, language_tree);
            }
        });
    });
};

parser._parse_content = function (node, callback) {
    var file = node.path;

    if ( file ) {
        node_fs.readFile(file, function (err, content) {
            if ( err ) {
                return done(err);
            }

            parser.markdown(content.toString(), callback);
        });

    } else {
        callback(null);
    }
};

// TODO: #7
parser._generate_url = function (node, name) {
    if ( !node.parent ) {
        node.url = '';

    } else {
        if ( !name ) {
            console.log(node);
        }

        var cooked = parser.cook_name(name);
        node.display_name = cooked.display_name;
        node.uri_name = cooked.uri_name;

        node.url = node.parent.url + '/' + node.uri_name;
    }
};

// walk and traverse a tree
parser.walk = function (tree, iterator, callback) {
    var q = async.queue(iterator);
    var errors = [];

    q.drain = function () {
        callback( errors.length ? errors : null );
    };

    function walk (node, depth) {
        node.depth = depth;

        q.push({
            node: node,

            // descend a specific node or descend all
            descend: function (sub_node) {
                if ( sub_node ) {
                    walk(sub_node, depth + 1);

                } else if ( node.pages ) {
                    node.pages.forEach(function (sub_node, name) {
                        walk(sub_node, depth + 1);
                    });
                }
            }

        }, function (err){
            if ( err ) {
                errors.push(err);
            }
        });
    }

    walk(tree, 0);
};


/* @flow */
'use strict'

const SemVer = require('semver')

/* ::
export type NodejsVersion = {
  version: string;
  date: string;
  files: string[];
  modules: string;
  lts: boolean | string;
};
*/

function find (
  list /* : NodejsVersion[] */,
  version /* : string */
) /* : NodejsVersion */ {
  for (let v = 0, vLength = list.length; v < vLength; v += 1) {
    const candidate = list[v]
    if (candidate.version === version) {
      return candidate
    }
  }
  throw new Error(`version ${version} not listed`)
}

function latestStableVersion (
  list /* : NodejsVersion[] */
) /* : string */ {
  // assume list is newest-to-oldest
  for (let v = 0, vLength = list.length; v < vLength; v += 1) {
    const version = list[v].version
    if (SemVer.major(version) % 2 === 0) {
      return version
    }
  }
  throw new Error('no stable versions identified')
}

function latestVersion (
  list /* : NodejsVersion[] */
) /* : string */ {
  // assume list is newest-to-oldest
  return list[0].version
}

module.exports = {
  find,
  latestStableVersion,
  latestVersion
}


'use strict';

const isStream = require('isstream');
const Stream = require('readable-stream');
const util = require('util');

// Inherit of Duplex stream
util.inherits(Duplexer, Stream.Duplex);

// Constructor
function Duplexer(options, writableStream, readableStream) {
  const _this = this;

  // Ensure new were used
  if (!(this instanceof Duplexer)) {
    return new (Duplexer.bind.apply(
      Duplexer, // eslint-disable-line
      [Duplexer].concat([].slice.call(arguments, 0))
    ))();
  }

  // Mapping args
  if (isStream(options)) {
    readableStream = writableStream;
    writableStream = options;
    options = {};
  } else {
    options = options || {};
  }
  this._reemitErrors =
    'boolean' === typeof options.reemitErrors ? options.reemitErrors : true;
  delete options.reemitErrors;

  // Checking arguments
  if (!isStream(writableStream, 'Writable', 'Duplex')) {
    throw new Error(
      'The writable stream must be an instanceof Writable or Duplex.'
    );
  }
  if (!isStream(readableStream, 'Readable')) {
    throw new Error('The readable stream must be an instanceof Readable.');
  }

  // Parent constructor
  Stream.Duplex.call(this, options);

  // Save streams refs
  this._writable = writableStream;
  this._readable = readableStream;

  // Internal state
  this._waitDatas = false;
  this._hasDatas = false;

  if ('undefined' == typeof this._readable._readableState) {
    this._readable = new Stream.Readable({
      objectMode: options.objectMode || false,
    }).wrap(this._readable);
  }

  if (this._reemitErrors) {
    this._writable.on('error', err => {
      _this.emit('error', err);
    });
    this._readable.on('error', err => {
      _this.emit('error', err);
    });
  }

  this._writable.on('drain', () => {
    _this.emit('drain');
  });

  this.once('finish', () => {
    _this._writable.end();
  });

  this._writable.once('finish', () => {
    _this.end();
  });

  this._readable.on('readable', () => {
    _this._hasDatas = true;
    if (_this._waitDatas) {
      _this._pushAll();
    }
  });

  this._readable.once('end', () => {
    _this.push(null);
  });
}

Duplexer.prototype._read = function _plexerRead() {
  this._waitDatas = true;
  if (this._hasDatas) {
    this._pushAll();
  }
};

Duplexer.prototype._pushAll = function _plexerPushAll() {
  const _this = this;
  let chunk;

  do {
    chunk = _this._readable.read();
    if (null !== chunk) {
      this._waitDatas = _this.push(chunk);
    }
    this._hasDatas = null !== chunk;
  } while (this._waitDatas && this._hasDatas);
};

Duplexer.prototype._write = function _plexerWrite(chunk, encoding, callback) {
  return this._writable.write(chunk, encoding, callback);
};

Duplexer.obj = function _plexerObj(options) {
  const firstArgumentIsAStream = isStream(options);
  const streams = [].slice.call(arguments, firstArgumentIsAStream ? 0 : 1);

  options = firstArgumentIsAStream ? {} : options;
  options.objectMode = true;
  return Duplexer.apply({}.undef, [options].concat(streams));
};

module.exports = Duplexer;


var _ = require('lodash'),
	async = require('async');

/**
 * Asynchronous Dependency Injection Library
 */

// This regex detects the arguments portion of a function definition
// Thanks to Angular for the regex
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;

/**
 * Calling the module directly returns a new Wrapper instance
 *
 * @param  {Function} fn
 * @return {Wrapper}
 */
exports = module.exports = function(fn, context, opts) {
	return new Wrapper(fn, context, opts);
};

/**
 * Wrapper Class
 *
 * @param {Function} fn
 * @param {Object} context
 */
var Wrapper = exports.Wrapper = function Wrapper(fn, context, opts) {

	// Ensure a new instance has been created.
	// Calling Wrapper as a function will return a new instance instead.
	if (!(this instanceof Wrapper)) {
		return new Wrapper(fn, context);
	}

	// Ensure a function has actually been provided.
	if (!_.isFunction(fn)) {
		throw new Error('AsyncDI Wrapper must be initialised with a function');
	}

	this.options = _.defaults({}, opts, {
		callback: ['callback']
	});

	if (_.isString(this.options.callback)) {
		this.options.callback = [this.options.callback];
	}

	// Save the function
	this.fn = fn;

	// Detect the dependencies using the regex
	this.deps = Function.toString.call(fn)
		.match(FN_ARGS)[1]
		.split(',')
		.map(function(i) { return i.trim(); })
		.filter(function(i) { return i; });

	// Create a map of dependency names for easy presence detection
	this.requires = {};
	this.deps.forEach(function(i) {
		this.requires[i] = true;
	}, this);

	// If the last argument is named 'callback', the function is async.
	// The callback is removed from the dependencies so it doesn't get considered
	// when this.provides() is called
	if (this.options.callback.indexOf(_.last(this.deps)) > -1) {
		this.isAsync = true;
		this.deps.pop();
	}

	// Save the context (may be changed later)
	this._context = context;

	// The internal provides object maps dependencies that can be provided if
	// requested by the function
	this._provides = null;

	// The internal arguments array contains the provided deps mapped to the
	// arguments requested, and is ready to be applied to the function
	this._arguments = [];

};

_.extend(Wrapper.prototype, {

	/**
	 * Registers dependencies that can be provided to the function
	 * @param  {Object|Array} provides map of key: value pairs or an array of values
	 * @return {Wrapper} this instance
	 */
	provides: function(provides) {
		if (_.isArray(provides)) {
			this._arguments = this._provides = (!this._provides) ? provides : this._provides.concat(provides);
		} else {
			this._provides = _.extend({}, this._provides, provides);
			this._arguments = _.map(this.deps, function(key) {
				return this._provides[key];
			}, this);
		}
		return this;
	},

	/**
	 * Changes the context of the function to the object provided
	 * @param  {Object} context
	 * @return {Wrapper} this instance
	 */
	bind: function(context) {
		this._context = context;
		return this;
	},

	/**
	 * Calls the function
	 *
	 * Will return the result if not async and no callback is provided
	 *
	 * @param  {Object} context (optional)
	 * @param  {Function} callback
	 */
	call: function(context, callback) {
		if (arguments.length === 1) {
			callback = context;
			context = this._context;
		}
		if (this.isAsync) {
			// clone the arguments so this.call can be reused with different callbacks
			var asyncArgs = this._arguments.slice();
			// push the callback onto the new arguments array
			asyncArgs.push(callback);
			// call the function
			this.fn.apply(context, asyncArgs);
		} else {
			// if the function isn't async, allow it to be called with or without a callback
			if (callback) {
				// If a callback is provided, it must use the error-first arguments pattern.
				// The return value of the function will be the second argument.
				try {
					callback(null, this.fn.apply(context, this._arguments));
				} catch(e) {
					callback(e);
				}
			} else {
				// If no callback is provided simply return the result of the function
				return this.fn.apply(context, this._arguments);
			}
		}
	},

	/**
	 * Applies the function iterator to each item in arr, in parallel.
	 *
	 * The context of the function will be the current item in the array.
	 *
	 * @param  {Array} arr
	 * @param  {Function} callback
	 */
	each: function each(arr, callback) {
		var wrapper = this;
		if (this.isAsync) {
			return async.each(arr, function(item, cb) {
				wrapper.call(item, cb);
			}, callback);
		} else {
			arr.each(function(item) {
				wrapper.call(item);
			});
			if (callback) { callback(); }
		}
	},

	/**
	 * Returns the results of interating the function on each item in an array.
	 *
	 * The context of the function will be the current item in the array.
	 *
	 * @param  {Array} arr
	 * @param  {Function} callback
	 * @return {Array}
	 */
	map: function map(arr, callback) {
		var wrapper = this;
		if (this.isAsync) {
			async.map(arr, function(item, cb) {
				wrapper.call(item, cb);
			}, callback);
		} else {
			callback(null, arr.map(function(item) {
				return wrapper.call(item);
			}));
		}
	}

});


/*!
 * ${copyright}
 */

// Provides control it.designfuture.flatpickr.FlatDatePicker
sap.ui.define([
		'jquery.sap.global',
		'sap/m/InputBase',
		'./3rdparty/flatpickr',
		'./library'
	], function(jQuery, InputBase, flatpickr, library) {
	"use strict";

	/**
	 * Constructor for a new FlatDatePicker.
	 *
	 * @param {string} [sId] id for the new control, generated automatically if no id is given
	 * @param {object} [mSettings] initial settings for the new control
	 *
	 * @class
	 * FlatDatePicker TODO ADD HERE A DESCRIPTION
	 * @extends sap.m.InputBase
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @since 1.40
	 * @name it.designfuture.flatpickr.FlatDatePicker
	 */

	var FlatDatePicker = InputBase.extend("it.designfuture.flatpickr.FlatDatePicker", /** @lends it.designfuture.flatpickr.FlatDatePicker prototype */ {

		__flatPickr: undefined,

		metadata : {
			library: 'it.designfuture.flatpickr',
			properties : {

				/**
				 * Exactly the same as date format, but for the altInput field
				 */
				altFormat : {type : "string", group : "Appearance", defaultValue : "F j, Y"},

				/**
				 * Show the user a readable date (as per altFormat), but return something totally different to the server.
				 */
				altInput : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * This class will be added to the input element created by the altInput option.
				 * Note that altInput already inherits classes from the original input.
				 */
				altInputClass : {type : "string", group : "Appearance", defaultValue : ""},

				/**
				 * Allows the user to enter a date directly input the input field. By default, direct entry is disabled.
				 */
				allowInput : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Allows the user to enter a date directly input the input field. By default, direct entry is disabled.
				 */
				//appendTo : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Whether clicking on the input should open the picker.
				 * You could disable this if you wish to open the calendar manually with.open()
				 */
				clickOpens : {type : "boolean", group : "Appearance", defaultValue : true},

				/**
				 * A string of characters which are used to define how the date will be displayed in the input box.
				 * The supported characters are defined in the table below.
				 */
				dateFormat : {type : "string", group : "Appearance", defaultValue : "Y-m-d"},

				/**
				 * Sets the initial selected date(s).
				 * If you're using mode: "multiple" or a range calendar supply an Array of Date objects or an Array of date strings which follow your dateFormat.
				 * Otherwise, you can supply a single Date object or a date string.
				 */
				dateValue : {type : "object", group : "Appearance", defaultValue : null, bindable: "bindable"},

				/**
				 * Initial value of the hour element.
				 */
				hourValue : {type : "int", group : "Appearance", defaultValue : 12, bindable: "bindable"},

				/**
				 * Initial value of the minute element.
				 */
				minuteValue : {type : "int", group : "Appearance", defaultValue : 0, bindable: "bindable"},

				/**
				 * Initial value of the seconds element.
				 */
				secondsValue : {type : "int", group : "Appearance", defaultValue : 0, bindable: "bindable"},

				/**
				 * Arrays of dates object to be disabled
				 */
				disabledDates : {type : "object[]", group : "Appearance", defaultValue : [], bindable: "bindable"},

				/**
				 * Set disableMobile to true to always use the non-native picker.
				 * By default, Flatpickr utilizes native datetime widgets unless certain options (e.g. disable) are used.
				 */
				disableMobile : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Arrays of dates object to be enabled
				 */
				enabledDates : {type : "object[]", group : "Appearance", defaultValue : [], bindable: "bindable"},

				/**
				 * Enables time picker
				 */
				enableTime : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Enables seconds in the time picker.
				 */
				enableSeconds : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Adjusts the step for the hour input (incl. scrolling)
				 */
				hourIncrement : {type : "int", group : "Appearance", defaultValue : 1},

				/**
				 * Displays the calendar inline
				 */
				inline : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * The maximum date that a user can pick to (inclusive).
				 */
				maxDate : {type : "object", group : "Appearance", defaultValue : null},

				/**
				 * The minimum date that a user can start picking from (inclusive).
				 */
				minDate : {type : "object", group : "Appearance", defaultValue : null},

				/**
				 * Adjusts the step for the minute input (incl. scrolling)
				 */
				minuteIncrement : {type : "int", group : "Appearance", defaultValue : 5},

				/**
				 * "single", "multiple", or "range"
				 */
				mode : {type : "string", group : "Appearance", defaultValue : "single"},

				/**
				 * Hides the day selection in calendar.
				 * Use it along with enableTime to create a time picker.
				 */
				noCalendar : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * 	Show the month using the shorthand version (ie, Sep instead of September).
				 */
				shorthandCurrentMonth : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Position the calendar inside the wrapper and next to the input element.
				 * (Leave false unless you know what you're doing.)
				 */
				static : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Displays time picker in 24 hour mode without AM/PM selection when enabled.
				 */
				time_24hr : {type : "boolean", group : "Appearance", defaultValue : false},

				/**
				 * Enables display of week numbers in calendar.
				 */
				weekNumbers : {type : "boolean", group : "Appearance", defaultValue : false},
			},
			aggregations: {},
			events: {

				/**
				 * onChange gets triggered when the user selects a date, or changes the time on a selected date
				 */
				onChange: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * onOpen gets triggered when the calendar is opened
				 */
				onOpen: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * onClose gets triggered when the calendar is closed
				 */
				onClose: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * onMonthChange gets triggered when the month is changed, either by the user or programmatically
				 */
				onMonthChange: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * onMonthChange gets triggered when the year is changed, either by the user or programmatically
				 */
				onYearChange: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * onReady gets triggered once the calendar is in a ready state.
				 */
				onReady: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * onValueUpdate gets triggered when the input value is updated with a new date string
				 */
				onValueUpdate: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				},

				/**
				 * Take full control of every date cell with the onDayCreate() hook
				 */
				onDayCreate: {
					parameters: {
						/**
						 * An array of Date objects selected by the user. When there are no dates selected, the array is empty.
						 */
						selectedDates: { type: "object[]" },

						/**
						 * String representation of the latest selected Date object by the user. The string is formatted as per the dateFormat option
						 */
						dateStr: { type: "string" },

						/**
						 * The Flatpickr object, containing various methods and properties
						 */
						instance: { type: "object" }
					}
				}

			}
		},

		init: function() {
			//	Init all the things!
		},

		onAfterRendering: function() {
			var that = this;
			this.__flatPickr = $("#"+this.getId()+".flatpickrCustomControl").flatpickr({
			    altFormat: this.getAltFormat(),
			    altInput: this.getAltInput(),
			    altInputClass: this.getAltInputClass(),
			    allowInput: this.getAllowInput(),
			    clickOpens: this.getClickOpens(),
			    dateFormat: this.getDateFormat(),
			    defaultDate: this.getDateValue(),
			    defaultHour: this.getHourValue(),
			    defaultMinute: this.getMinuteValue(),
			    defaultSeconds: this.getSecondsValue(),
			    disable: this.getDisabledDates(),
				disableMobile: this.getDisableMobile(),
			    enable: this.getEnabledDates(),
			    enableTime: this.getEnableTime(),
			    enableSeconds: this.getEnableSeconds(),
			    hourIncrement: this.getHourIncrement(),
			    inline: this.getInline(),
			    maxDate: this.getMaxDate(),
			    minDate: this.getMinDate(),
			    minuteIncrement: this.getMinuteIncrement(),
			    mode: this.getMode(),
			    noCalendar: this.getNoCalendar(),
			    shorthandCurrentMonth: this.getShorthandCurrentMonth(),
			    static: this.getStatic(),
			    time_24hr: this.getTime_24hr(),
			    weekNumbers: this.getWeekNumbers(),
			    wrap: true, // force wrap to true

			    // defining events callback
			    onChange: function(selectedDates, dateStr, instance) {
					that.setProperty("dateValue", selectedDates, true);
					that.fireOnChange({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onOpen: function(selectedDates, dateStr, instance) {
					that.fireOnOpen({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onClose: function(selectedDates, dateStr, instance) {
					that.fireOnClose({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onMonthChange: function(selectedDates, dateStr, instance) {
					that.fireOnMonthChange({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onYearChange: function(selectedDates, dateStr, instance) {
					that.fireOnYearChange({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onReady: function(selectedDates, dateStr, instance) {
					that.fireOnReady({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onValueUpdate: function(selectedDates, dateStr, instance) {
					that.fireOnValueUpdate({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				},
			    onDayCreate: function(selectedDates, dateStr, instance) {
					that.fireOnDayCreate({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
				}
			});
		},

		////////////////////////////////////////////////////
		//	FASTPICKR METHODS
		////////////////////////////////////////////////////

		/*
		* Resets the selected dates (if any) and clears the input.
		* @public
		*/
		clearDatePicker: function() {
			if( this.__flatPickr ) {
				this.__flatPickr.clear();
			}
		},

		/*
		* Closes the calendar.
		* @public
		*/
		closeDatePicker: function() {
			if( this.__flatPickr ) {
				this.__flatPickr.close();
			}
		},

		/*
		* Destroys the Flatpickr instance, cleans up - removes event listeners, restores inputs, etc.
		* @public
		*/
		destroyDatePicker: function() {
			if( this.__flatPickr ) {
				this.__flatPickr.destroy();
			}
		},

		/*
		* Return a formatted date
		* @public
		* @param {string} formatStr Formatting tokens string
		* @param {string} dateObj Date to be formatted
		* @returns {string} A string representation of dateObj,  formatted as per formatStr
		*/
		formatDate: function(formatStr, dateObj) {
			if( this.__flatPickr ) {
				return this.__flatPickr.formatDate(formatStr, dateObj);
			}
			return null;
		},

		/*
		* Sets the calendar view to the year and month ofdate, which can be a date string, a Date, or nothing
		* If date is undefined, the view is set to the latest selected date, the minDate, or today’s date
		* @public
		* @param {Date} date Date to jump to
		*/
		jumpToDate: function(date) {
			if( this.__flatPickr ) {
				this.__flatPickr.jumpToDate(date);
			}
		},

		/*
		* Shows/opens the calendar.
		* @public
		*/
		openDatePicker: function() {
			if( this.__flatPickr ) {
				this.__flatPickr.open();
			}
		},

		/*
		* Parses a date string or a timestamp, and returns a Date.
		* @public
		* @param {object} date String or timestamp to be parsed
		* @returns {Date} Parsed date
		*/
		parseDate: function(date) {
			if( this.__flatPickr ) {
				this.__flatPickr.parseDate();
			}
		},

		/*
		* Redraws the calendar. Shouldn’t be necessary in most cases
		* @public
		*/
		redrawDatePicker: function() {
			if( this.__flatPickr ) {
				this.__flatPickr.redraw();
			}
		},

		/*
		* Sets the current selected date(s) todate, which can be a date string, a Date, or an Array of the Dates.
		* Optionally, pass true as the second argument to force any onChange events to fire
		* @public
		* @param {object} string, Date or an Array of the Dates
		* @param {boolean} triggerChange If true will force any onChange events to fire
		*/
		setDate: function(date, triggerChange) {
			this.__flatPickr.setDate(date, triggerChange);
		},

		/*
		* Shows/opens the calendar if its closed, hides/closes it otherwise
		* @public
		*/
		toggleDatePicker: function() {
			this.__flatPickr.toggle();
		},

		////////////////////////////////////////////////////
		//	EVENTS HANDLING
		////////////////////////////////////////////////////

		onChange: function(selectedDates, dateStr, instance) {
			this.fireOnChange({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onOpen: function(selectedDates, dateStr, instance) {
			this.fireOnOpen({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onClose: function(selectedDates, dateStr, instance) {
			this.fireOnClose({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onMonthChange: function(selectedDates, dateStr, instance) {
			this.fireOnMonthChange({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onYearChange: function(selectedDates, dateStr, instance) {
			this.fireOnYearChange({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onReady: function(selectedDates, dateStr, instance) {
			this.fireOnReady({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onValueUpdate: function(selectedDates, dateStr, instance) {
			this.fireOnValueUpdate({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		onDayCreate: function(selectedDates, dateStr, instance) {
			this.fireOnDayCreate({selectedDates: selectedDates, dateStr: dateStr, instance: instance});
		},

		////////////////////////////////////////////////////
		//	GETTER & SETTER FOR FLAT PICKER PROPERTIES
		////////////////////////////////////////////////////

		getSelectedDates: function() {
			if( this.__flatPickr ) {
				return this.__flatPickr.selectedDates;
			}
			return null;
		},

		setAltFormat: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("altFormat", value);
			}
			this.setProperty("altFormat", value, false);
			return this;
		},

		setAltInput: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("altInput", value);
			}
			this.setProperty("altInput", value, false);
			return this;
		},

		setAltInputClass: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("altInputClass", value);
			}
			this.setProperty("altInputClass", value, false);
			return this;
		},

		setAllowInput: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("allowInput", value);
			}
			this.setProperty("allowInput", value, false);
			return this;
		},

		setClickOpens: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("clickOpens", value);
			}
			this.setProperty("clickOpens", value, false);
			return this;
		},

		setDateFormat: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("dateFormat", value);
			}
			this.setProperty("dateFormat", value, false);
			return this;
		},

		setDateValue: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("defaultDate", value);
			}
			this.setProperty("dateValue", value, false);
			return this;
		},

		setHourValue: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("defaultHour", value);
			}
			this.setProperty("hourValue", value, false);
			return this;
		},

		setMinuteValue: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("defaultMinute", value);
			}
			this.setProperty("minuteValue", value, false);
			return this;
		},

		setSecondsValue: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("defaultSeconds", value);
			}
			this.setProperty("secondsValue", value, false);
			return this;
		},

		setDisabledDates: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("disable", value);
			}
			this.setProperty("disabledDates", value, false);
			return this;
		},

		setDisableMobile: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("disableMobile", value);
			}
			this.setProperty("disableMobile", value, false);
			return this;
		},

		setEnabledDates: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("enable", value);
			}
			this.setProperty("enabledDates", value, false);
			return this;
		},

		setEnableTime: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("enableTime", value);
			}
			this.setProperty("enableTime", value, false);
			return this;
		},

		setEnableSeconds: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("enableSeconds", value);
			}
			this.setProperty("enableSeconds", value, false);
			return this;
		},

		setHourIncrement: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("hourIncrement", value);
			}
			this.setProperty("hourIncrement", value, false);
			return this;
		},

		setInline: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("inline", value);
			}
			this.setProperty("inline", value, false);
			return this;
		},

		setMaxDate: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("maxDate", value);
			}
			this.setProperty("maxDate", value, false);
			return this;
		},

		setMinDate: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("minDate", value);
			}
			this.setProperty("minDate", value, false);
			return this;
		},

		setMinuteIncrement: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("minuteIncrement", value);
			}
			this.setProperty("minuteIncrement", value, false);
			return this;
		},

		setMode: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("mode", value);
			}
			this.setProperty("mode", value, false);
			return this;
		},

		setNoCalendar: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("noCalendar", value);
			}
			this.setProperty("noCalendar", value, false);
			return this;
		},

		setShorthandCurrentMonth: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("shorthandCurrentMonth", value);
			}
			this.setProperty("shorthandCurrentMonth", value, false);
			return this;
		},

		setStatic: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("static", value);
			}
			this.setProperty("static", value, false);
			return this;
		},

		setTime_24hr: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("time_24hr", value);
			}
			this.setProperty("time_24hr", value, false);
			return this;
		},

		setWeekNumbers: function(value) {
			if( this.__flatPickr ) {
				this.__flatPickr.set("weekNumbers", value);
			}
			this.setProperty("weekNumbers", value, false);
			return this;
		}

	});

	/*
	* Override the exit method to free local resources and destroy
	* Optionally, pass true as the second argument to force any onChange events to fire
	* @public
	*/
	FlatDatePicker.prototype.exit = function() {
		InputBase.prototype.exit.apply(this, arguments);
		if (this.__flatPickr) {
			if (this.__flatPickr.isOpen) {
				this.closeDatePicker();
			}
			this.destroyDatePicker();
		}
		this.__flatPickr = undefined;
	};

	return FlatDatePicker;

}, /* bExport= */ true);


/**
 * XOR128 pseudo-random number generator. This is based off of algorithm found on the XORSHIFT wiki here:
 * http://en.wikipedia.org/wiki/Xorshift. Also the code ensures all numbers are handled as unsigned 32bit integers and
 * is consistent with the C++ example provided xor128.cpp
 *
 * @param {Number} x
 * @param {Number} y
 * @param {Number} z
 * @param {Number} w
 * @returns {SeededRandomNumber}
 */
function XOR128(x, y, z, w) {
    if ((x && x < 1) || (y && y < 1) || (z && z < 1) || (w && w < 1)) {
        throw new Error('Invalid Seed');
    }

	this.x = x ? x : Math.random() * 4294967296;
	this.y = y ? y : Math.random() * 4294967296;
	this.z = z ? z : Math.random() * 4294967296;
	this.w = w ? w : Math.random() * 4294967296;
}

module.exports = XOR128;

/**
 * Returns the next unsigned 32bit integer in the sequence
 *
 * @returns {Number}
 */
XOR128.prototype.next = function () {
	var t = this.x ^ ((this.x << 11) >>> 0);

	this.x = this.y;
	this.y = this.z;
	this.z = this.w;
	this.w = (this.w ^ (this.w >>> 19) ^ (t ^ (t >>> 8))) >>> 0;

	return this.w;
};

/**
 * Returns the next number in the sequence as a float value between 0 & 1
 *
 * @returns {Number}
 */
XOR128.prototype.random = function () {
	return this.next() / 4294967296;
};

/**
 * Draws noise graph onto a given canvas object
 *
 * @param {type} canvas
 * @param {type} width
 * @param {type} height
 * @returns {undefined}
 */
XOR128.prototype.drawCanvas = function (canvas, width, height) {
	var context = canvas.getContext('2d');

	function drawPixel(x, y, r, g, b) {
		context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + 1 + ')';
		context.fillRect(x, y, 1, 1);
	}

	for (var y = 0; y < height; y += 1) {
		for (var x = 0; x < width; x += 1) {
			var color = this.next();
			drawPixel(x, y, color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF);
		}
	}
};